Lockstep.GenStatem (Lockstep v0.1.0)

Copy Markdown View Source

Minimal :gen_statem-shaped wrapper that runs under Lockstep's controller. Supports the handle_event_function callback mode -- one handle_event/4 clause per (event_type, event_content, state, data) combination -- which is the pattern most modern Elixir code uses.

Replies for :call events go through {:reply, From, Reply} actions, same as real :gen_statem.

Limitations (v0.5)

  • callback_mode :state_functions is NOT supported -- use :handle_event_function only.
  • No :state_timeout, :generic_timeout, or :event_timeout actions; use Lockstep.send_after/3 from inside handle_event/4 and pattern-match on the resulting :info event instead.
  • No :postpone action; the event you got is the event you handle.
  • No terminate/3 callback.
  • handle_event/4 may return: {:next_state, new_state, new_data} / {:next_state, new_state, new_data, actions} / {:keep_state, new_data} / {:keep_state, new_data, actions} / :keep_state_and_data / {:keep_state_and_data, actions} / {:stop, reason, _data}. Each actions element is {:reply, From, Reply}.

Example

defmodule Door do
  def init(_), do: {:ok, :closed, nil}

  def handle_event(:call, :open, :closed, data),
    do: {:next_state, :open, data, [{:reply, from(), :ok}]}

  def handle_event(:call, :open, :open, data),
    do: {:keep_state, data, [{:reply, from(), :already_open}]}

  def handle_event(:call, :state, state, data),
    do: {:keep_state, data, [{:reply, from(), state}]}
end

In practice you receive from as the third argument of handle_event (when event_type == :call); your implementation typically captures it locally and uses it in [{:reply, from, ...}].

Real :gen_statem passes from as part of the event content for call events; this wrapper does the same -- event_content for a call is {from, request}. Reply via {:reply, from, value} action.

Summary

Functions

Synchronous request -- blocks (in the controller) for the matching {:reply, from, _} action emitted by handle_event/4.

Fire-and-forget request.

Spawn a managed gen_statem process. Returns {:ok, pid} to match OTP's :gen_statem.start_link/3 shape.

Types

from()

@type from() :: {pid(), reference()}

server()

@type server() :: pid()

Functions

call(server, request)

@spec call(server(), any()) :: any()

Synchronous request -- blocks (in the controller) for the matching {:reply, from, _} action emitted by handle_event/4.

cast(server, request)

@spec cast(server(), any()) :: :ok

Fire-and-forget request.

start_link(module, init_arg)

@spec start_link(module(), any()) :: {:ok, pid()}

Spawn a managed gen_statem process. Returns {:ok, pid} to match OTP's :gen_statem.start_link/3 shape.