Skip to content

Instantly share code, notes, and snippets.

@kalouo
Last active December 15, 2020 10:16
Show Gist options
  • Save kalouo/3a1fbe4b1521d1df1b0ef1eeee54f408 to your computer and use it in GitHub Desktop.
Save kalouo/3a1fbe4b1521d1df1b0ef1eeee54f408 to your computer and use it in GitHub Desktop.
GenServer, DynamicSupervisor and Registry
defmodule GamePlatform.Game do
use GenServer
require Logger
@registry :game_registry
@initial_state %{players: []}
## GenServer API
def start_link(name) do
GenServer.start_link(__MODULE__, name, name: via_tuple(name))
end
def log_state(process_name) do
process_name |> via_tuple() |> GenServer.call(:log_state)
end
def add_player(process_name, player_name) do
process_name |> via_tuple() |> GenServer.call({:add_player, player_name})
end
@doc """
This function will be called by the supervisor to retrieve the specification
of the child process.The child process is configured to restart only if it
terminates abnormally.
"""
def child_spec(process_name) do
%{
id: __MODULE__,
start: {__MODULE__, :start_link, [process_name]},
restart: :transient
}
end
def stop(process_name, stop_reason) do
# Given the :transient option in the child spec, the GenServer will restart
# if any reason other than `:normal` is given.
process_name |> via_tuple() |> GenServer.stop(stop_reason)
end
## GenServer Callbacks
@impl true
def init(name) do
Logger.info("Starting process #{name}")
{:ok, @initial_state}
end
@impl true
def handle_call(:log_state, _from, state) do
{:reply, "State: #{inspect(state)}", state}
end
@impl true
def handle_call({:add_player, new_player}, _from, state) do
new_state =
Map.update!(state, :players, fn existing_players ->
[new_player | existing_players]
end)
{:reply, :player_added, new_state}
end
## Private Functions
defp via_tuple(name),
do: {:via, Registry, {@registry, name}}
end
defmodule GamePlatform.Server do
@moduledoc """
This supervisor is responsible for:
- A supervisor monitoring game processes.
- A Registry providing a key-value store for game processes.
"""
use Application
@registry :game_registry
def start() do
children = [
{GamePlatform.GameSupervisor, []},
{Registry, [keys: :unique, name: @registry]}
]
# :one_for_one strategy: if a child process crashes, only that process is restarted.
opts = [strategy: :one_for_one, name: __MODULE__]
Supervisor.start_link(children, opts)
end
end
defmodule GamePlatform.GameSupervisor do
@moduledoc """
This supervisor is responsible game child processes.
"""
use DynamicSupervisor
alias GamePlatform.Game
def start_link(_arg) do
DynamicSupervisor.start_link(__MODULE__, [], name: __MODULE__)
end
def start_child(game_name) do
# Shorthand to retrieve the child specification from the `child_spec/1` method of the given module.
child_specification = {Game, game_name}
DynamicSupervisor.start_child(__MODULE__, child_specification)
end
@impl true
def init(_arg) do
# :one_for_one strategy: if a child process crashes, only that process is restarted.
DynamicSupervisor.init(strategy: :one_for_one)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment