Documentation ¶
Index ¶
- Variables
- type Event
- type EventData
- type EventHandler
- type EventType
- type State
- type StateMachine
- func (sm *StateMachine) Emit(event *Event) error
- func (sm *StateMachine) GetState() (st State, err error)
- func (sm *StateMachine) IsHandlerAssigned(t EventType, s State) (defined bool, err error)
- func (sm *StateMachine) Off(t EventType, s State) error
- func (sm *StateMachine) On(t EventType, ss []State, h EventHandler) error
- func (sm *StateMachine) OnChain(t EventType, ss []State, hs []EventHandler) error
- func (sm *StateMachine) SetState(state State) error
- func (sm *StateMachine) Terminate() error
- func (sm *StateMachine) TerminatedChannel() (isTerminatedCh chan struct{})
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // Returned from Emit if there is no mapping for the current state and the // event that is being emitted. ErrIllegalEvent = errors.New("Illegal event received") // Returned from a method if the state machine is already terminated. ErrTerminated = errors.New("State machine terminated") )
Functions ¶
This section is empty.
Types ¶
type EventHandler ¶
Various EventHandlers can be registered to process events in particular states. By registering event handlers we build up a mapping of state x event -> handler and the handler is invoked exactly in the defined state when the defined event is emitted.
Once a handler is invoked, its role is to take the StateMachine into the next state, doing some useful work on the way.
If an event is emitted in a state where no handler is defined, ErrIllegalEvent is returned.
type StateMachine ¶
type StateMachine struct {
// contains filtered or unexported fields
}
StateMachine is the only struct this package exports. Once an event is emitted on a StateMachine, the relevant handler is fetched and invoked. StateMachine takes care of all the synchronization, it is thread-safe. It does not use any locking, just channels. While that may be a bit more overhead, it is more robust and clear.
It uses an unbuffered channel for passing events to the internal goroutine, so all the methods block until their requests read from that channel.
Example ¶
// Allocate space for 3 states, 3 commands and 10 requests in the channel. sm := New(stateStopped, 3, 3) // Allocate a new Context which is going to keep our data between // the handler calls. ctx := new(Context) fmt.Printf("Context struct: %#v\n", ctx) // RUN sm.On(cmdRun, []State{ stateStopped, }, ctx.handleRun) // STOP sm.On(cmdStop, []State{ stateRunning, }, ctx.handleStop) // CLOSE sm.On(cmdClose, []State{ stateStopped, stateRunning, }, ctx.handleClose) var ( run = &Event{cmdRun, nil} stop = &Event{cmdStop, nil} cls = &Event{cmdClose, nil} ) sm.Emit(run) sm.Emit(stop) sm.Emit(run) sm.Emit(stop) // Show how to write an event producer. exit := make(chan struct{}) go func() { eventCh := make(chan *Event) go func() { eventCh <- run eventCh <- stop eventCh <- run eventCh <- cls close(exit) }() for { select { case event := <-eventCh: sm.Emit(event) case <-sm.TerminatedChannel(): return } } }() // Wait for the inner goroutine to close the exit channel. <-exit // Close our state machine. sm.Terminate() // Wait for the state machine to terminate. <-sm.TerminatedChannel() fmt.Println("Goroutine exited")
Output: Context struct: &statemachine.Context{seq:0} Event number 1 received STOPPED -> RUNNING by RUN Event number 2 received RUNNING -> STOPPED by STOP Event number 3 received STOPPED -> RUNNING by RUN Event number 4 received RUNNING -> STOPPED by STOP Event number 5 received STOPPED -> RUNNING by RUN Event number 6 received RUNNING -> STOPPED by STOP Event number 7 received STOPPED -> RUNNING by RUN Event number 8 received RUNNING -> CLOSED by CLOSE Goroutine exited
func New ¶
func New(initState State, stateCount, eventCount uint) *StateMachine
Create new StateMachine. Allocate internal memory for particular number of states and events.
func (*StateMachine) GetState ¶
func (sm *StateMachine) GetState() (st State, err error)
GetState returns the internal state machine state.
func (*StateMachine) IsHandlerAssigned ¶
func (sm *StateMachine) IsHandlerAssigned(t EventType, s State) (defined bool, err error)
Check if a handler is defined for this state and event.
func (*StateMachine) Off ¶
func (sm *StateMachine) Off(t EventType, s State) error
Drop the handler assigned to the requested state and event.
func (*StateMachine) On ¶
func (sm *StateMachine) On(t EventType, ss []State, h EventHandler) error
Register an event handler.
func (*StateMachine) OnChain ¶
func (sm *StateMachine) OnChain(t EventType, ss []State, hs []EventHandler) error
Register the chain of event handlers.
func (*StateMachine) SetState ¶
func (sm *StateMachine) SetState(state State) error
SetState changes the internal state machine state.
func (*StateMachine) Terminate ¶
func (sm *StateMachine) Terminate() error
Terminate the internal event loop and close all internal channels. Particularly the termination channel is closed to signal all producers that they can no longer emit any events and shall exit.
func (*StateMachine) TerminatedChannel ¶
func (sm *StateMachine) TerminatedChannel() (isTerminatedCh chan struct{})
TerminateChannel can be used to obtain a channel that is closed once the state machine is terminated and is no longer willing to accept any events. This is useful if you want to start multiple goroutines to asynchronously post events. You can just start them, pass them this termination channel and leave them be. The only requirement is that those producer goroutines should exit or simply stop posting any events as soon as the channel is closed.