View Source Protean.Interpreter (Protean v0.1.0)
Execution logic for a Protean machine.
overview-of-interpretation-loop
Overview of interpretation loop
At a high level, an SCXML-confirming statechart (which Protean is based on) defines an interpreter loop that maintains certain properties laid out in the SCXML specification. In addition to executing purely-functional state transformations, the interpreter is responsible for executing actions, side-effects, and tracking process state.
The interpreter loop consists of the following steps:
- Check if interpreter is running; if not, exit.
- Execute any automatic transitions that are active in the current state, recursively until there are no more automatic transitions to be run.
- Process internal event if one is present. a. Microstep the interpreter by running any transitions and executing any resulting actions. b. Loop back to 1.
- Await an external event.
- Process external event in the same manner as 3.
Note that the loop above contains a conceptually "blocking" operation in awaiting an external
event. Protean.Interpreter.start/1
executes the first 3 steps of the interpreter, taking any
automatic transitions that are immediately active based on the machine configuration, etc. This
guarantees that the machine ends in a stable state where no more automatic transitions or
internal events are left, at which point we can wait for an external event.
Macrosteps and microsteps
The execution of this loop until it is waiting for an external event (or interpretation has stopped) is called a macrostep. The execution of a single set of transitions resulting from an automatic transition, internal event, or external event is called a microstep. Macrosteps can consist of one or more microsteps.
After this initialization phase, the trigger for the loop to continue is that we've received an external event. In the context of Protean, it's easier to think of this as initialization and event handling.
Initialization:
- Check if the interpreter is running; if not, exit.
- Execute any automatic transitions that are active, looping back to 1. When there are none left, continue.
- Process internal event if one is present, looping back to 1.
This guarantees that the machine is in a stable state with no more automatic transitions or internal events left to run.
Handle external event:
- Run any transitions and execute actions associated with the external event.
- Check if the interpreter is running; if not, exit. (A transition may have caused the machine to enter a final state.)
- Execute any automatic transitions that have become active as a result of the event, looping back to 2.
- Process internal event if one is present, looping back to 2.
As with initialization, this guarantees that the machine is in a stable state, ready to handle the next external event.
The SCXML specification references "run-to-completion", which refers to the property that, after interpretation has started, there should be no (visible) point where the interpreter is in a non-stable state. When the interpreter starts, it transitions until it is stable. When it receives an event, it transitions until it is stable.
Link to this section Summary
Functions
Handle an event, executing any transitions, actions, and side-effects associated with the current machine context.
Create a new Interpreter
. The returned interpreter will still need to be started, which could
result in additional side-effects. See start/1
.
Whether the interpreter has been started and can accept events.
Entrypoint for the interpreter that must be called before the interpreter will be in a state where it can handle external events. This is necessary in order to handle any initializing actions, spawns, or automatic transitions.
Stop an interpreter, preventing further event processing.
Link to this section Types
@type t() :: %Protean.Interpreter{ config: Protean.MachineConfig.t(), context: Protean.Context.t(), hooks: map(), id: Protean.id() | nil, internal_queue: :queue.queue(), parent: pid(), running: boolean() }
Link to this section Functions
@spec handle_event(t(), Protean.event()) :: {t(), [term()]}
Handle an event, executing any transitions, actions, and side-effects associated with the current machine context.
Returns a tuple of the interpreter and any replies resulting from actions that were run.
Create a new Interpreter
. The returned interpreter will still need to be started, which could
result in additional side-effects. See start/1
.
Whether the interpreter has been started and can accept events.
Entrypoint for the interpreter that must be called before the interpreter will be in a state where it can handle external events. This is necessary in order to handle any initializing actions, spawns, or automatic transitions.
Calling start/1
on an already-running interpreter is a no-op.
Stop an interpreter, preventing further event processing.