Lockstep.Supervisor (Lockstep v0.1.0)

Copy Markdown View Source

A :one_for_one supervisor that runs under Lockstep's controller. Builds on Lockstep.spawn_link/1 + Lockstep.flag(:trap_exit, true) so child crashes are observed via {:EXIT, child, reason} and restarts can be issued in-band with the rest of the test schedule.

Supported

Child specs

%{id: :counter, start: {Counter, :start_link, [42]}, restart: :permanent}

Or the shorthand {Counter, 42}, which maps to %{id: Counter, start: {Counter, :start_link, [42]}, restart: :permanent}.

Or the bare module Counter, which calls Counter.child_spec(:no_arg) if exported, otherwise {Counter, :start_link, []}.

Caveats

Summary

Functions

Count of currently-tracked child slots (including :undefined).

Build the supervisor-spec tuple returned from a use Supervisor module's init/1 callback. Mirrors OTP Supervisor.init/2.

Restart a child whose pid is :undefined. Errors if the child is already alive or doesn't exist.

Add a new child dynamically. Returns {:ok, pid} or {:error, reason}.

Start a supervisor. Two shapes

Terminate a running child by id. The slot is preserved with pid :undefined so restart_child/2 can revive it.

List children: [{id, pid_or_:undefined, :worker, [module]}, ...].

Types

child_id()

@type child_id() :: any()

child_info()

@type child_info() :: {child_id(), child_pid(), :worker | :supervisor, [module()]}

child_pid()

@type child_pid() :: pid() | :undefined

child_spec()

@type child_spec() :: %{
  :id => any(),
  :start => {module(), atom(), [any()]},
  optional(:restart) => :permanent | :transient | :temporary
}

Functions

count_children(sup)

@spec count_children(pid()) :: non_neg_integer()

Count of currently-tracked child slots (including :undefined).

init(children, opts)

@spec init(
  [child_spec()],
  keyword()
) :: {:ok, {map(), [child_spec()]}}

Build the supervisor-spec tuple returned from a use Supervisor module's init/1 callback. Mirrors OTP Supervisor.init/2.

def init(_arg) do
  children = [...]
  Lockstep.Supervisor.init(children, strategy: :one_for_one)
end

Returns {:ok, {sup_flags, children}} where sup_flags carries :strategy, :intensity (from :max_restarts), and :period (from :max_seconds).

restart_child(sup, child_id)

@spec restart_child(pid(), child_id()) :: {:ok, pid()} | {:error, term()}

Restart a child whose pid is :undefined. Errors if the child is already alive or doesn't exist.

start_child(sup, spec)

@spec start_child(pid(), child_spec()) :: {:ok, pid()} | {:error, term()}

Add a new child dynamically. Returns {:ok, pid} or {:error, reason}.

start_link(children_or_module, opts_or_arg \\ [])

@spec start_link([child_spec()] | module(), keyword() | any()) ::
  {:ok, pid()} | :ignore | {:error, term()}

Start a supervisor. Two shapes:

  • start_link(children_list, opts) -- children are a literal list of child specs.
  • start_link(module, init_arg, opts) -- children are returned from module.init(init_arg), which must return {:ok, {sup_flags, child_specs}} (typically via Lockstep.Supervisor.init/2).

Options:

  • :strategy -- :one_for_one (default; only one supported).
  • :max_restarts -- default 3.
  • :max_seconds -- default 5.

start_link(module, init_arg, opts)

@spec start_link(module(), any(), keyword()) ::
  {:ok, pid()} | :ignore | {:error, term()}

terminate_child(sup, child_id)

@spec terminate_child(pid(), child_id()) :: :ok | {:error, :not_found}

Terminate a running child by id. The slot is preserved with pid :undefined so restart_child/2 can revive it.

which_children(sup)

@spec which_children(pid()) :: [child_info()]

List children: [{id, pid_or_:undefined, :worker, [module]}, ...].