Skip to content

Instantly share code, notes, and snippets.

@zkessin
Created May 17, 2019 09:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zkessin/ba33136dc2d6de790072748df75e8615 to your computer and use it in GitHub Desktop.
Save zkessin/ba33136dc2d6de790072748df75e8615 to your computer and use it in GitHub Desktop.
defmodule Counter do
use GenServer
defstruct val: 0,
watches: []
def increment() do
GenServer.cast({:global, __MODULE__}, :increment)
end
def decrement() do
GenServer.cast({:global, __MODULE__}, :decrement)
end
def value() do
GenServer.call({:global, __MODULE__}, :value)
end
def watch(value) do
GenServer.cast({:global, __MODULE__}, {:watch, self(), value})
end
# --------------------------------------------------------------------------------
def start_link(initial_value) do
GenServer.start_link(__MODULE__, initial_value, name: {:global, __MODULE__})
end
# --------------------------------------------------------------------------------
def init(initial_value) do
Process.flag(:trap_exit, true)
{:ok, %Counter{val: initial_value}}
end
def handle_cast(:increment, state = %Counter{val: val, watches: watches}) do
new_value = val + 1
check_watches(new_value, watches)
{:noreply, %{state | val: new_value}}
end
def handle_cast(:decrement, state = %Counter{val: val, watches: watches}) do
new_value = val - 1
check_watches(new_value, watches)
{:noreply, %{state | val: new_value}}
end
def handle_cast({:watch, client_pid, value}, state = %Counter{watches: watches}) do
new_state = %{
state
| watches: [%{client_pid: client_pid, watch: value} | watches]
}
Process.monitor(client_pid)
{:noreply, new_state}
end
def handle_call(:value, _from, state = %Counter{val: val}) do
{:reply, val, state}
end
def handle_info({:DOWN, _ref, :process, pid, _reason}, state = %Counter{watches: watches}) do
new_watches = :lists.delete(pid, watches)
{:noreply, %{state | watches: new_watches}}
end
def handle_info(_msg, state) do
{:noreply, state}
end
def terminate(_reason, %Counter{watches: watches}) do
Enum.each(watches, fn %{client_pid: client_pid} ->
send(client_pid, {:warning, "Counter going down"})
end)
end
# --------------------------------------------------------------------------------
defp check_watches(value_sought, watches) do
watches
|> Enum.filter(fn %{watch: value} -> value == value_sought end)
|> Enum.each(fn %{client_pid: client_pid, watch: value} ->
send(client_pid, {:alarm, value})
end)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment