Skip to content

Instantly share code, notes, and snippets.

@mmmries
Last active August 18, 2019 12:26
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mmmries/cd84b0d6008f58b83d95 to your computer and use it in GitHub Desktop.
Save mmmries/cd84b0d6008f58b83d95 to your computer and use it in GitHub Desktop.
3 Trvial Concurrency Examples in Elixir

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!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment