MCPcopy Index your code
hub / github.com/astavonin/gfsm

github.com/astavonin/gfsm @v0.2.0

Chat with this repo
repository ↗ · DeepWiki ↗ · release v0.2.0 ↗ · + Follow
89 symbols 160 edges 7 files 34 documented · 38% updated 16mo agov0.2.0 · 2025-02-05★ 59
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

GFSM - simple and fast Finite State Machine for Go

PkgGoDev build-and-test CI

The GFSM library provides a simple and fast implementation of a Finite State Machine (FSM) for Go. The library adopts C++-style approaches with a primary focus on speed and simplicity, which is the main difference from alternative Go FSM implementations like looplab/fsm. You can also create a visual representation of an FSM using the gfsm_uml generator.

How To use GFSM

Two Phase Commit protocol (TPC protocol) is an excellent example of Finite State Machine use case.

NOTE: Full example is in examples/two-phase-commit/main.go folder.

Having the following State Machine:

graph TD;
    Init-->Wait;
    Wait-->Abort;
    Wait-->Commit;

we should: 1. Enumerate all possible states 2. Describe each states 3. Describe a state machine 4. Run the state machine

Enumerate all possible states

The GFSM library can consume any comparable interface as a states enumerator, but the recommended approach is using int-based types. For example, we can define type State with all TPC protocol-related states.

type State int
const (
    Init State = iota
    Wait
    Abort
    Commit
)

Describe each states

Each State is represented by its unique stateID, action handler, and list of possible transitions. Each state can have a unique handler that supports StateAction interface:

type StateAction[StateIdentifier comparable] interface {
    OnEnter(smCtx StateMachineContext)
    OnExit(smCtx StateMachineContext)
    Execute(smCtx StateMachineContext, eventCtx EventContext) StateIdentifier
}

Where OnEnter and OnExit will be called once on the state entering or exiting respectfully, and Execute is the call that the state machine routes to the current state from StateMachineHandler.ProcessEvent(...).

Describe a state machine

Any State Machine can have its unique context. For the TPC protocol, it's important to have information about the voting process, like the expected voter count and committed ID.

type coordinatorContext struct {
    commitID string
    partCnt  int
}

StateMachineContext is stored inside the constructed StateMachineHandler object and will be passed to each state on any calls (OnEnter, OnExit, and Execute).

To create a new state machine, StateMachineBuilder should be used. To build a TPC protocol state machine, you can use the following approach:

sm := gfsm.NewBuilder[State]().
    SetDefaultState(Init).
    SetSmContext(&coordinatorContext{partCnt: 3}).
    RegisterState(Init, &initState{}, []State{Wait}).
    RegisterState(Wait, &waitState{}, []State{Abort, Commit}).
    RegisterState(Abort, &responseState{
        keepResp: Abort,
    }, []State{Init}).
    RegisterState(Commit, &responseState{
        keepResp: Commit,
    }, []State{Init}).
    Build()

Run the state machine

After the construction, the state machine shall be executed and terminated on exit:

sm.Start()
defer sm.Stop()

To process an event, the state machine object implements a ProcessEvent(eventCtx EventContext) error call, where eventCtx can be any data that the state will process internally as primary input data.

err := sm.ProcessEvent(commitRequest{"commit_1"})

During the event processing, the state machine will call Execute with the passed EventContext and StateMachineContext. Based on these data, the state can decide either to keep the current state (Init in the example below) or make a switch (Wait) by returning the new expected state.

func (s *initState) Execute(smCtx gfsm.StateMachineContext, eventCtx gfsm.EventContext) State {
    cCtx := smCtx.(*coordinatorContext)
    req, ok := eventCtx.(commitRequest)
    if !ok {
        // ...
        return Init
    }
    // ...
    return Wait
}

Extension points exported contracts — how you extend this code

StateAction (Interface)
StateAction is the interface which each state must implement. [6 implementers]
state_action.go
StateMachineBuilder (Interface)
StateMachineBuilder interface provides access to a builder that simplifies state machine creation. Builder usage is opti
sm_builder.go
StateMachineHandler (Interface)
StateMachineHandler is the main state machine interface. All manipulation with the state machine object shall be perform
gfsm.go
EventContext (Interface)
EventContext is an abstraction that represent any data that user passes to the current state for execution in StateMachi
state_action.go
StateMachineContext (Interface)
StateMachineContext is an abstraction that represent any data that user passes to the state machine. The data will be fo
state_action.go

Core symbols most depended-on inside this repo

State
called by 12
gfsm.go
RegisterState
called by 9
sm_builder.go
ProcessEvent
called by 6
gfsm.go
OnEnter
called by 3
state_action.go
OnExit
called by 3
state_action.go
Start
called by 3
gfsm.go
Stop
called by 3
gfsm.go
SetDefaultState
called by 2
sm_builder.go

Shape

Method 42
Function 21
Struct 17
Interface 5
TypeAlias 4

Languages

Go100%

Modules by API surface

gfsm_test.go21 symbols
examples/two-phase-commit/main.go17 symbols
gfsm.go16 symbols
sm_builder.go14 symbols
cmd/gfsm_uml/main.go11 symbols
state_action.go6 symbols
perf_test.go4 symbols

For agents

$ claude mcp add gfsm \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact