These are my solutions to the three concurrency exercises from Katrina Owen's Go Post.
I'm posting here with example output in the hopes that someone call tell me how to do it better
These are my solutions to the three concurrency exercises from Katrina Owen's Go Post.
I'm posting here with example output in the hopes that someone call tell me how to do it better
# Note: I've scaled all the times down by a factor of 10 for convencience when waiting for the program to run | |
defmodule Walker do | |
use GenServer | |
def init(name) do | |
:rand.seed(:exs1024) | |
{:ok, name} | |
end | |
def handle_call(:get_ready, _from, name) do | |
IO.puts "#{name} is getting ready..." | |
time_to_get_ready = 6.0 + (:rand.uniform * 3.0) | |
:timer.sleep( trunc(time_to_get_ready * 1000.0) ) | |
IO.puts "#{name} spent #{time_to_get_ready}sec getting ready" | |
{:reply, :ok, name} | |
end | |
def handle_call(:tie_shoes, _from, name) do | |
IO.puts "#{name} started putting on shoes" | |
time_to_put_on_shoes = 3.5 + (:rand.uniform * 1.0) | |
:timer.sleep( trunc(time_to_put_on_shoes * 1000.0) ) | |
IO.puts "#{name} spent #{time_to_put_on_shoes}sec putting on shoes" | |
{:reply, :ok, name} | |
end | |
def get_ready(pid), do: GenServer.call(pid, :get_ready, 9_000) | |
def tie_shoes(pid), do: GenServer.call(pid, :tie_shoes, 4_500) | |
end | |
{:ok, bob_pid} = GenServer.start_link(Walker, "bob") | |
{:ok, alice_pid} = GenServer.start_link(Walker, "alice") | |
IO.puts "Let's go for a walk!" | |
bob_get_ready = Task.async fn -> Walker.get_ready(bob_pid) end | |
alice_get_ready = Task.async fn -> Walker.get_ready(alice_pid) end | |
:ok = Task.await(bob_get_ready, 9_000) | |
:ok = Task.await(alice_get_ready, 9_000) | |
IO.puts "everyone is ready" | |
alarm_set = Task.async fn -> | |
:timer.sleep( 6_000 ) | |
IO.puts "alarm armed!" | |
end | |
bob_tie_shoes = Task.async fn -> Walker.tie_shoes(bob_pid) end | |
alice_tie_shoes = Task.async fn -> Walker.tie_shoes(alice_pid) end | |
:ok = Task.await(bob_tie_shoes, 4_500) | |
:ok = Task.await(alice_tie_shoes, 4_500) | |
:ok = Task.await(alarm_set, 6_000) |
L422-MRies:trivial_concurrency mmmries$ elixir 01.daily_walk.exs | |
Let's go for a walk! | |
bob is getting ready... | |
alice is getting ready... | |
bob spent 6.2278073495116155sec getting ready | |
alice spent 6.347995207812311sec getting ready | |
everyone is ready | |
bob started putting on shoes | |
alice started putting on shoes | |
alice spent 3.5384977318306476sec putting on shoes | |
bob spent 3.678138007024623sec putting on shoes | |
alarm armed! |
defmodule Tapas do | |
use GenServer | |
def start_link, do: GenServer.start_link(__MODULE__, self(), name: __MODULE__) | |
def init(report_to_pid) do | |
:rand.seed(:exs1024) | |
tapas = "chorizo" |> List.duplicate(4 + :rand.uniform(6)) | |
tapas = tapas ++ ("pimientos de padrón" |> List.duplicate(4 + :rand.uniform(6))) | |
tapas = tapas ++ ("croquetas" |> List.duplicate(4 + :rand.uniform(6))) | |
tapas = tapas ++ ("patatas bravas" |> List.duplicate(4 + :rand.uniform(6))) | |
tapas = tapas ++ ("chopitos" |> List.duplicate(4 + :rand.uniform(6))) | |
{:ok, {report_to_pid, Enum.shuffle(tapas)}} | |
end | |
def handle_call(:take_a_morsel, _from, {report_to_pid, []}) do | |
send report_to_pid, :meal_complete | |
{:reply, :no_food_for_you, {report_to_pid, []}} | |
end | |
def handle_call(:take_a_morsel, _from, {report_to_pid, [tapa|rest]}) do | |
{:reply, tapa, {report_to_pid, rest}} | |
end | |
def take_a_morsel, do: GenServer.call(__MODULE__, :take_a_morsel) | |
end | |
defmodule Eater do | |
use GenServer | |
def start_link(name), do: GenServer.start_link(__MODULE__, name) | |
def init(name) do | |
:rand.seed(:exs1024) | |
{:ok, name, 500 + :rand.uniform(2_500)} | |
end | |
def handle_info(:timeout, name) do | |
case Tapas.take_a_morsel do | |
morsel when is_binary(morsel) -> | |
IO.puts "#{name} is enjoying some #{morsel}" | |
{:noreply, name, 500 + :rand.uniform(2_500)} | |
_ -> | |
IO.puts "#{name} is finished" | |
{:stop, :normal, name} | |
end | |
end | |
end | |
Tapas.start_link | |
IO.puts "Bon appétit!" | |
{:ok, _alice_pid} = Eater.start_link("Alice") | |
{:ok, _bob_pid} = Eater.start_link("Bob") | |
{:ok, _charlie_pid} = Eater.start_link("Charlie") | |
{:ok, _dave_pid} = Eater.start_link("Dave") | |
receive do | |
:meal_complete -> | |
IO.puts "That was delicious!" | |
end |
L422-MRies:trivial_concurrency mmmries$ elixir 02.eating_tapas.exs | |
Bon appétit! | |
Bob is enjoying some patatas bravas | |
Alice is enjoying some chorizo | |
Charlie is enjoying some pimientos de padrón | |
Dave is enjoying some chopitos | |
Alice is enjoying some chopitos | |
Bob is enjoying some chopitos | |
Alice is enjoying some chorizo | |
Dave is enjoying some pimientos de padrón | |
Charlie is enjoying some pimientos de padrón | |
Charlie is enjoying some chorizo | |
Dave is enjoying some pimientos de padrón | |
Alice is enjoying some patatas bravas | |
Bob is enjoying some croquetas | |
Dave is enjoying some pimientos de padrón | |
Dave is enjoying some chorizo | |
Alice is enjoying some pimientos de padrón | |
Charlie is enjoying some croquetas | |
Bob is enjoying some croquetas | |
Charlie is enjoying some chorizo | |
Alice is enjoying some croquetas | |
Bob is enjoying some patatas bravas | |
Dave is enjoying some patatas bravas | |
Charlie is enjoying some chopitos | |
Bob is enjoying some chorizo | |
Charlie is enjoying some patatas bravas | |
Alice is enjoying some pimientos de padrón | |
Bob is enjoying some pimientos de padrón | |
Charlie is enjoying some chopitos | |
Dave is enjoying some chopitos | |
Charlie is enjoying some pimientos de padrón | |
Alice is enjoying some pimientos de padrón | |
Charlie is enjoying some patatas bravas | |
Dave is enjoying some chopitos | |
Bob is enjoying some croquetas | |
That was delicious! | |
Charlie is finished |
defmodule InternetCafe do | |
use GenServer | |
def start_link, do: GenServer.start_link(__MODULE__, nil, name: __MODULE__) | |
def request_a_computer do | |
GenServer.call(__MODULE__, {:request_a_computer, self}) | |
end | |
def logoff_computer(computer_id) do | |
GenServer.call(__MODULE__, {:logoff_computer, computer_id}) | |
end | |
def init(nil) do | |
{:ok, %{computers: Enum.to_list(1..8), waiting: []}} | |
end | |
def handle_call({:request_a_computer, pid}, _from, %{computers: [], waiting: waiting}) do | |
GenServer.cast(pid, :please_wait) | |
new_waiting_list = waiting ++ [pid] | |
{:reply, :ok, %{computers: [], waiting: new_waiting_list}} | |
end | |
def handle_call({:request_a_computer, pid}, _from, %{computers: [computer|rest], waiting: []}) do | |
GenServer.cast(pid, {:your_turn, computer}) | |
{:reply, :ok, %{computers: rest, waiting: []}} | |
end | |
def handle_call({:logoff_computer, computer}, _from, %{waiting: [], computers: computers}) do | |
new_computers = [computer|computers] | |
case length(new_computers) do | |
8 -> send(:main, :all_customers_served) | |
_ -> nil | |
end | |
{:reply, :ok, %{waiting: [], computers: [computer|computers]}} | |
end | |
def handle_call({:logoff_computer, computer}, _from, %{waiting: [next_in_line|waiting], computers: []}) do | |
GenServer.cast(next_in_line, {:your_turn, computer}) | |
{:reply, :ok, %{waiting: waiting, computers: []}} | |
end | |
end | |
defmodule Tourist do | |
use GenServer | |
def start_link(name), do: GenServer.start_link(__MODULE__, name) | |
def init(name) do | |
InternetCafe.request_a_computer | |
{:ok, %{name: name}} | |
end | |
def handle_cast(:please_wait, state) do | |
IO.puts "#{state.name} is waiting in line" | |
{:noreply, state} | |
end | |
def handle_cast({:your_turn, computer_id}, state) do | |
IO.puts "#{state.name} is online (computer #{computer_id})" | |
{:noreply, Dict.put(state, :computer_id, computer_id), browsing_time} | |
end | |
def handle_info(:timeout, state) do | |
IO.puts "#{state.name} is done" | |
InternetCafe.logoff_computer(state.computer_id) | |
{:stop, :normal, state} | |
end | |
defp browsing_time, do: 250 + :rand.uniform(5_000) | |
end | |
Process.register(self, :main) | |
InternetCafe.start_link | |
(1..25) | |
|> Enum.map(&( "Tourist #{&1}" )) | |
|> Enum.shuffle | |
|> Enum.map(&( Tourist.start_link(&1) )) | |
receive do | |
:all_customers_served -> | |
IO.puts "The place is empty, let's close up and got to the beach!" | |
end |
L422-MRies:trivial_concurrency mmmries$ elixir 03.internet_cafe.exs | |
Tourist 3 is online (computer 1) | |
Tourist 7 is online (computer 2) | |
Tourist 2 is online (computer 3) | |
Tourist 13 is online (computer 4) | |
Tourist 8 is online (computer 5) | |
Tourist 20 is waiting in line | |
Tourist 15 is waiting in line | |
Tourist 4 is waiting in line | |
Tourist 9 is waiting in line | |
Tourist 21 is waiting in line | |
Tourist 17 is waiting in line | |
Tourist 1 is waiting in line | |
Tourist 18 is waiting in line | |
Tourist 22 is waiting in line | |
Tourist 5 is waiting in line | |
Tourist 23 is waiting in line | |
Tourist 16 is waiting in line | |
Tourist 12 is waiting in line | |
Tourist 14 is waiting in line | |
Tourist 11 is waiting in line | |
Tourist 24 is waiting in line | |
Tourist 19 is waiting in line | |
Tourist 6 is online (computer 6) | |
Tourist 10 is online (computer 7) | |
Tourist 25 is online (computer 8) | |
Tourist 2 is done | |
Tourist 20 is online (computer 3) | |
Tourist 20 is done | |
Tourist 15 is online (computer 3) | |
Tourist 8 is done | |
Tourist 4 is online (computer 5) | |
Tourist 3 is done | |
Tourist 9 is online (computer 1) | |
Tourist 4 is done | |
Tourist 21 is online (computer 5) | |
Tourist 6 is done | |
Tourist 17 is online (computer 6) | |
Tourist 10 is done | |
Tourist 1 is online (computer 7) | |
Tourist 25 is done | |
Tourist 18 is online (computer 8) | |
Tourist 7 is done | |
Tourist 22 is online (computer 2) | |
Tourist 13 is done | |
Tourist 5 is online (computer 4) | |
Tourist 15 is done | |
Tourist 23 is online (computer 3) | |
Tourist 9 is done | |
Tourist 16 is online (computer 1) | |
Tourist 22 is done | |
Tourist 12 is online (computer 2) | |
Tourist 17 is done | |
Tourist 14 is online (computer 6) | |
Tourist 1 is done | |
Tourist 11 is online (computer 7) | |
Tourist 11 is done | |
Tourist 24 is online (computer 7) | |
Tourist 12 is done | |
Tourist 19 is online (computer 2) | |
Tourist 21 is done | |
Tourist 18 is done | |
Tourist 5 is done | |
Tourist 23 is done | |
Tourist 24 is done | |
Tourist 14 is done | |
Tourist 16 is done | |
Tourist 19 is done | |
The place is empty, let's close up and got to the beach! |