11 releases (5 breaking)

0.7.0 Oct 15, 2024
0.5.3 Aug 23, 2024
0.5.1 Jul 29, 2024
0.4.0 Mar 28, 2024

#232 in Algorithms

Download history 470/week @ 2024-07-29 729/week @ 2024-08-05 508/week @ 2024-08-12 754/week @ 2024-08-19 421/week @ 2024-08-26 689/week @ 2024-09-02 585/week @ 2024-09-09 396/week @ 2024-09-16 316/week @ 2024-09-23 385/week @ 2024-09-30 287/week @ 2024-10-07 378/week @ 2024-10-14 226/week @ 2024-10-21 124/week @ 2024-10-28 232/week @ 2024-11-04 46/week @ 2024-11-11

633 downloads per month

MIT license

115KB
2.5K SLoC

rex-sm 👑

Design Features

  • Make state machines modular units that can be reused
  • State machines that use other state machines are aware of who they can use, but state machines that are being used are not aware of state machines that use them
  • No tasking/threading
  • Handle multiple state machines concurrently
  • Trigger other state machines, and wait for their completion, then resume
  • Create/delete timers
  • Send notifications outside the state machine group
  • Send events to themselves
  • Accept events from outside the state machine group
  • Encourage state machine implementations to be represented in a (<State>, <Event>) format

Inspiration

Overview

StateMachines are first registered with the StateMachineManager, which I will refer to as simply the Manager. Every call to Manager::cycle() processes a single event. A single event corresponds to running on a single state machine. The Manager accesses the contents of the Controller and manipulates it. A single Controller is shared amongst all state machines registered with the Manager.

There are two types of events UserEvents and SystemEvents. UserEvents are passed to StateMachine::cycle() while SystemEvents are not. StateMachine::cycle() accepts a &mut Controller and a UserEvent. The StateMachine uses the functions in the Controller to add/remove events from the event queue; all functions do this except for timer related functions. SystemEvents are consumed by the manager and used to modify the Controller internals or send data or notifications to outside the state machine group.

Node based StateMachine Manager (in development)

Goals

  • decoupling state machine input processing from a given state’s current enumerations
  • state signaling that all feeds into the same sink (the manager’s signal_queue) ; this allows lifts and transits to be processed homogeneously thus avoiding type opacity through Box<dyn Signal>

In practice the design should give at most three message streams connected to a particular state machine down:

I/O

  • One Input (handles both external and internal events)
Signal {
    id: StateId<K>,
    input: I,
}

Two outputs:

  • Signal output (events meant to be processed as inputs for other state machines)
  • Notification output (events meant to be processed by anything that is not a state machine fed by a given signal_queue)

The StateMachineManager owns:

  • The state storage layer (using StateStore)
  • the input event stream
  • The state machine processors

The StateMachineManager is responsible for:

  • routing Signals to the appropriate state machines
  • Injecting ProcessorContexts into the state machines: this action is what allows state machines to cycle concurrently

StateStore is responsible for:

  • inserting & updating various state hierarchies
  • operations are done concurrently by holding all node trees in Arc<Mutex<_>> containers.

This allows StateStore storage to:

  • Create multiple indices (Through fresh DashMap key insertions) pointing to the same tree by incrementing the Arc count and inserting a new entry per child node
  • Allows independent interior mutability per state tree, decoupling unrelated states from resource contention

Node based TimeoutManager (in development)

Considerations:

  • only one timer is active per StateId<K>, State machines should not have to keep track of Operation::Set(Instant::now()) emitted to notifications. Thus, all timers should be indexable by StateId<K>.
  • A newer Operation::Set for the same StateId<K> should override an old timer.
  • A timeout should emit a Signal that is pertinent to the related state machine.

Approach:

  • TimeoutManager implements a per tick polling approach to resolving timeouts
  • TimeoutManager accepts two types of inputs, set and cancel (timeout)
  • Timeouts are stored within the TimeoutLedger
  • TimeoutLedger contains a BTreeMap that indexes IDs by Instant and a HashMap that indexes Instants by ID This double indexing allows timeout cancellations to go through without providing the Instant that they were meant to remove

Dependencies

~5–12MB
~122K SLoC