Last active
December 15, 2020 10:16
-
-
Save kalouo/3a1fbe4b1521d1df1b0ef1eeee54f408 to your computer and use it in GitHub Desktop.
GenServer, DynamicSupervisor and Registry
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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