77 lines
1.6 KiB
Elixir
77 lines
1.6 KiB
Elixir
# how to implement a generic server
|
|
# motivation for GenServer
|
|
defmodule GenericServer do
|
|
### Server Implementation ###
|
|
|
|
# pass in a module 'm', since module names are just atoms
|
|
# more generic, take in starting args and pass to modules
|
|
# init function to do whatever is required beforehand
|
|
def start(m, arg) do
|
|
state = m.init(arg)
|
|
spawn(fn -> loop(m, state) end)
|
|
end
|
|
|
|
defp loop(m, state) do
|
|
receive do
|
|
{:call, msg, from} ->
|
|
{reply, new_state} = m.handle_call(msg, from, state)
|
|
send(from, {self(), reply})
|
|
loop(m, new_state)
|
|
|
|
{:cast, msg} ->
|
|
new_state = m.handle_cast(msg, state)
|
|
loop(m, new_state)
|
|
end
|
|
end
|
|
|
|
### Client API ###
|
|
def call(pid, msg, timeout \\ 5000) do
|
|
send(pid, {:call, msg, self()})
|
|
|
|
receive do
|
|
# pin operator, reply must have same pid as call argument pid
|
|
{^pid, x} -> x
|
|
after
|
|
timeout ->
|
|
:timeout
|
|
end
|
|
end
|
|
|
|
def cast(pid, msg) do
|
|
send(pid, {:cast, msg})
|
|
end
|
|
end
|
|
|
|
defmodule Counter do
|
|
### Server Implementation ###
|
|
def start(file) do
|
|
GenericServer.start(__MODULE__, file)
|
|
end
|
|
|
|
def init(file) do
|
|
with {:ok, content} <- File.read(file),
|
|
{n, _} <- Integer.parse(content) do
|
|
n
|
|
end
|
|
if is_integer(val), do: val, else: 0
|
|
end
|
|
|
|
def handle_call(:value, _from, state) do
|
|
# -> {reply, new_state} in GenericServer
|
|
{state, state}
|
|
end
|
|
|
|
def handle_cast({:inc, amt}, state) do
|
|
state + amt
|
|
end
|
|
|
|
### Client API ###
|
|
def value(pid) do
|
|
GenericServer.call(pid, :value)
|
|
end
|
|
|
|
def inc(pid, amt \\ 1) do
|
|
GenericServer.cast(pid, {:inc, amt})
|
|
end
|
|
end
|