Skip to content

Instantly share code, notes, and snippets.

@andrewhao
Last active August 7, 2023 21:37
Show Gist options
  • Save andrewhao/c90f3d12cc92c0356f7d2d7173289071 to your computer and use it in GitHub Desktop.
Save andrewhao/c90f3d12cc92c0356f7d2d7173289071 to your computer and use it in GitHub Desktop.
Dynamic Supervisors in Elixir
defmodule Game do
use GenServer
def init(game_id) do
{:ok, %{game_id: game_id}}
end
def start_link(game_id) do
GenServer.start_link(__MODULE__, game_id, name: {:global, "game:#{game_id}"})
end
def add_player(pid, player_name) do
GenServer.call(pid, {:add_player, player_name})
end
def handle_call({:add_player, player_name}, _from, %{game_id: game_id} = state) do
# Uh oh, we started this process but it's not under supervision!
start_status = Player.start_link({player_name, game_id})
{:reply, start_status, state}
end
end
defmodule SupervisedGame do
# ...
def handle_call({:add_player, player_name}, _from, %{game_id: game_id} = state) do
# Now we replace this with supervised management
start_status = PlayerSupervisor.add_player(player_name, game_id)
{:reply, start_status, state}
end
end
defmodule Player do
use GenServer
def init({player_name, game_id}) do
{:ok, %{name: player_name, game_id: game_id}}
end
def start_link({player_name, game_id}) do
GenServer.start_link(
__MODULE__,
{player_name, game_id},
name: {:global, "player:#{player_name}"}
)
end
def get(pid) do
GenServer.call(pid, :get)
end
def handle_call(:get, _from, state) do
{:reply, {:ok, state}, state}
end
end
defmodule PlayerDynamicSupervisor do
use DynamicSupervisor
def start_link(_arg) do
DynamicSupervisor.start_link(__MODULE__, :ok, name: __MODULE__)
end
def init(:ok) do
DynamicSupervisor.init(strategy: :one_for_one)
end
# Start a Player process and add it to supervision
def add_player(player_name, game_id) do
# Note that start_child now directly takes in a child_spec.
child_spec = {Player, {player_name, game_id}}
# Equivalent to:
# child_spec = Player.child_spec({player_name, game_id})
DynamicSupervisor.start_child(__MODULE__, child_spec)
end
# Terminate a Player process and remove it from supervision
def remove_player(player_pid) do
DynamicSupervisor.terminate_child(__MODULE__, player_pid)
end
# Nice utility method to check which processes are under supervision
def children do
DynamicSupervisor.which_children(__MODULE__)
end
# Nice utility method to check which processes are under supervision
def count_children do
DynamicSupervisor.count_children(__MODULE__)
end
end
defmodule Player do
# ...
# Insert this start_link/2 method, which intercepts the extra `[]`
# argument from the Supervisor and molds it back to correct form.
def start_link([], {player_name, game_id}) do
start_link({player_name, game_id})
end
def start_link({player_name, game_id}) do
# Original implementation...
end
# ...
end
defmodule PlayerSupervisor do
use Supervisor
def start_link([]) do
Supervisor.start_link(__MODULE__, :ok, name: __MODULE__)
end
def init(:ok) do
Supervisor.init([Player], strategy: :simple_one_for_one)
end
# Start a Player process and add it to supervision
def add_player(player_name, game_id) do
# Note that the second arg to start_child/2 must be an Enumerable
Supervisor.start_child(__MODULE__, [{player_name, game_id}])
end
# Terminate a Player process and remove it from supervision
def remove_player(player_pid) do
Supervisor.terminate_child(__MODULE__, player_pid)
end
# Nice utility method to check which processes are under supervision
def children do
Supervisor.which_children(__MODULE__)
end
# Nice utility method to check which processes are under supervision
def count_children do
Supervisor.count_children(__MODULE__)
end
end
@ceoro9
Copy link

ceoro9 commented Mar 10, 2019

I am not sure, but probably the correct start_link signature in Player module should look like this def start_link([player_name, game_id]). Anyway that was the issue in my case, may be will help somebody.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment