Skip to content

Instantly share code, notes, and snippets.

@d-led
Last active May 2, 2019 16:25
Show Gist options
  • Save d-led/e1b18377bbe2cc5dd92cdb54e2183b1b to your computer and use it in GitHub Desktop.
Save d-led/e1b18377bbe2cc5dd92cdb54e2183b1b to your computer and use it in GitHub Desktop.
# https://ideone.com/w49bT4
# https://gist.github.com/d-led/e1b18377bbe2cc5dd92cdb54e2183b1b
# inpired by https://andrealeopardi.com/posts/connection-managers-with-gen_statem/
defmodule Counter do
@moduledoc """
This is an example gen_statem based Counter.
The counter enters an invalid state, when decremented at state 0.
To recover from the invalid state, reset it via Counter.reset
"""
@behaviour :gen_statem
# API
@doc """
Starts the counter process.
Returns `{:ok, pid}` on success.
"""
def start_link(_) do
:gen_statem.start_link(__MODULE__, nil, [])
end
@doc """
Increments the counter asychronously
"""
def increment(pid), do: :gen_statem.cast(pid, {:increment})
@doc """
Decrements the counter asychronously
"""
def decrement(pid), do: :gen_statem.cast(pid, {:decrement})
@doc """
Resets the counter asychronously
"""
def reset(pid), do: :gen_statem.cast(pid, {:reset})
@doc """
Synchrounously returns the current counter state
"""
def state(pid), do: :gen_statem.call(pid, {:state})
# state structure
defstruct [count: 0]
# gen_statem boilerplate
@impl true
def callback_mode(), do: :state_functions
@impl true
def init(_) do
data = %__MODULE__{}
actions = [{:next_event, :internal, :initialize}]
{:ok, :invalid, data, actions}
end
# states
def invalid(:internal, :initialize, data) do
IO.puts("initializing (asynchronously)")
:timer.sleep(1000)
IO.puts("initialized")
{:next_state, :active, data}
end
def invalid(:cast, {:reset}, _data) do
IO.puts("resetting (asynchronously)")
{:next_state, :active, %__MODULE__{}}
end
def invalid({:call, from}, {:state}, _data) do
actions = [{:reply, from, :invalid_state}]
{:keep_state_and_data, actions}
end
def active(:cast, {:increment}, data) do
{:keep_state, %__MODULE__{data | count: data.count + 1}, []}
end
def active(:cast, {:decrement}, %__MODULE__{count: 0} = data) do
{:next_state, :invalid, data, []}
end
def active(:cast, {:decrement}, data) do
{:keep_state, %__MODULE__{data | count: data.count - 1}, []}
end
def active({:call, from}, {:state}, data) do
actions = [{:reply, from, data.count}]
{:keep_state_and_data, actions}
end
end
{:ok, counter} = Counter.start_link([])
IO.puts "started the counter"
counter |> Counter.increment()
counter |> Counter.increment()
counter |> Counter.state() |> IO.inspect()
counter |> Counter.decrement()
counter |> Counter.state() |> IO.inspect()
counter |> Counter.decrement()
counter |> Counter.decrement()
counter |> Counter.state() |> IO.inspect()
counter |> Counter.reset()
counter |> Counter.state() |> IO.inspect()
@d-led
Copy link
Author

d-led commented May 1, 2019

initializing (asynchronously)
started the counter
initialized
2
1
:invalid_state
resetting (asynchronously)
0

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