lfsm

package module
v0.0.0-...-0f20e1f Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 10, 2019 License: MIT Imports: 4 Imported by: 0

README

Lock-Free State Machine GoDoc Build Status

LFSM is a light-weight State Machine implementation that doesn't use any locks.

Basic Example

package main

import (
	"log"
	"os"

	"github.com/Eyal-Shalev/lfsm"
)

func main() {
	l := log.New(os.Stdout, "", log.Lshortfile)
	const (
		opened uint32 = iota
		closed
	)
	s := lfsm.NewState(
		lfsm.Constraints{
			opened: {closed},
			closed: {opened},
		},
		lfsm.InitialState(closed),
		lfsm.StateName(opened, "opened"),
		lfsm.StateName(closed, "closed"),
	)

	l.Println(s) // digraph g{s[label="",shape=none,height=.0,width=.0];s->n1;n0[label="opened"];n1[label="closed"];n0->n1;n1->n0;}

	l.Printf("Current state: %s", s.CurrentName()) // Current state: closed

	if err := s.Transition(opened); err != nil {
		l.Fatal(err)
	}

	if err := s.Transition(opened); err != nil {
		l.Printf("Expected error: %s", err) // Expected error: invalid transition (opened -> opened)
	}

	if err := s.TransitionFrom(closed, opened); err != nil {
		l.Printf("Expected error: %s", err) // Expected error: transition failed (closed -> opened)
	}

	l.Printf("Current state: %s", s.CurrentName()) // Current state: opened

	if err := s.Transition(closed); err != nil {
		l.Fatal(err)
	}

	l.Printf("Current state: %s", s.CurrentName()) // Current state: closed
}

Documentation

Overview

Package lfsm provides a light-weight lock-free state machine.

This state machine uses atomic operations to transition between states.

Basic Example:

package main

import (
	"log"
	"github.com/Eyal-Shalev/lfsm"
)

func main() {
	s := lfsm.NewState(lfsm.Constraints{0: {1}, 1: {0}})

	log.Printf("Current state: %s", s.Current()) // Current state: 1
	if err := s.Transition(0); err != nil {
		panic(err)
	}
	log.Printf("Current state: %s", s.Current()) // Current state: 0
}

You may want to label your states, so the lfsm.StateNames struct and lfsm.StateName function can be used to supply options for the State Machine. For extra convenience you can use constants for your states. Named Example:

package main

import (
	"log"
	"github.com/Eyal-Shalev/lfsm"
)
const (
	opened uint64 = iota
	closed
)

func main() {
	s := lfsm.NewState(
		lfsm.Constraints{
			opened: {closed},
			closed: {opened},
		},
		lfsm.StateNames{opened: "opened", closed: "closed"},
	)

	log.Printf("Current state: %s", s.Current()) // Current state: closed
	if err := s.Transition(0); err != nil {
		panic(err)
	}
	log.Printf("Current state: %s", s.Current()) // Current state: opened
}

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func InitialState

func InitialState(v uint32) option

InitialState sets the initial state of the state machine

Example
package main

import (
	"fmt"

	"github.com/Eyal-Shalev/lfsm"
)

func main() {
	s := lfsm.NewState(lfsm.Constraints{0: {1}, 1: {0}}, lfsm.InitialState(1))
	fmt.Printf("Current state: %d.\n", s.Current())
}
Output:

Current state: 1.

func StateName

func StateName(v uint32, name string) option

StateName sets an alias to a state integer.

Types

type Constraints

type Constraints map[uint32][]uint32

Constraints defines the possible transition for this state machine.

The map keys describe the source states, and their values are the valid target destinations.

type State

type State struct {
	// contains filtered or unexported fields
}

State is the structs that holds the current state, the available transitions and other options.

func NewState

func NewState(m Constraints, opts ...option) *State

NewState creates a new State Machine.

func (*State) Current

func (s *State) Current() uint32

Current returns the current state.

func (*State) CurrentName

func (s *State) CurrentName() string

CurrentName returns the alias for the current state. If no alias is defined, the state integer will be returned in its string version.

Example
package main

import (
	"fmt"

	"github.com/Eyal-Shalev/lfsm"
)

func main() {
	s := lfsm.NewState(lfsm.Constraints{0: {1}, 1: {0}}, lfsm.StateName(1, "foo"))
	fmt.Printf("Current state: %s(%d).\n", s.CurrentName(), s.Current())
	_ = s.Transition(1)
	fmt.Printf("Current state: %s(%d).\n", s.CurrentName(), s.Current())

}
Output:

Current state: 0(0).
Current state: foo(1).

func (*State) String

func (s *State) String() string

String returns the Graphviz representation of this state machine.

See: https://www.graphviz.org/ & https://dreampuf.github.io/GraphvizOnline

Example
package main

import (
	"fmt"

	"github.com/Eyal-Shalev/lfsm"
)

func main() {
	fmt.Println(lfsm.NewState(lfsm.Constraints{0: {0}}, lfsm.StateNames{0: "foo"}))
}
Output:

digraph g{s[label="",shape=none,height=.0,width=.0];s->n0;n0[label="foo",style=filled];n0->n0;}

func (*State) Transition

func (s *State) Transition(dst uint32) error

Transition tries to change the state. It uses the current state as the source state, if you want to specify the source state use TransitionFrom instead. Returns an error if the transition failed.

Example
package main

import (
	"fmt"

	"github.com/Eyal-Shalev/lfsm"
)

func main() {
	s := lfsm.NewState(lfsm.Constraints{0: {0, 1}, 1: {0}}, lfsm.InitialState(1))
	err := s.Transition(1)
	fmt.Printf("Expected error: %s.\n", err)

	err = s.Transition(0)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Current state: %d.\n", s.Current())

	err = s.Transition(0)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Current state: %d.\n", s.Current())

	err = s.Transition(1)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Current state: %d.\n", s.Current())

}
Output:

Expected error: invalid transition (1 -> 1).
Current state: 0.
Current state: 0.
Current state: 1.

func (*State) TransitionFrom

func (s *State) TransitionFrom(src, dst uint32) error

TransitionFrom tries to change the state. Returns an error if the transition failed.

Example
package main

import (
	"fmt"

	"github.com/Eyal-Shalev/lfsm"
)

func main() {
	s := lfsm.NewState(lfsm.Constraints{0: {1}, 1: {0}}, lfsm.InitialState(1))
	err := s.TransitionFrom(0, 1)
	fmt.Printf("Expected error: %s.\n", err)

	err = s.TransitionFrom(1, 0)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Current state: %d.\n", s.Current())

}
Output:

Expected error: transition failed (0 -> 1) current state is not 0.
Current state: 0.

type StateNames

type StateNames map[uint32]string

StateNames holds a mapping between the state (in its integer form) to its alias.

Example
package main

import (
	"fmt"

	"github.com/Eyal-Shalev/lfsm"
)

func main() {
	s := lfsm.NewState(lfsm.Constraints{0: {}}, lfsm.StateNames{0: "foo"})
	fmt.Printf("Current state: %s(%d).\n", s.CurrentName(), s.Current())
}
Output:

Current state: foo(0).

type TransitionError

type TransitionError struct {
	Src, Dst uint32
	// contains filtered or unexported fields
}

TransitionError is an error struct for all failed transition attempts.

func NewFailedTransitionError

func NewFailedTransitionError(src, dst uint32, stateNames StateNames) *TransitionError

NewFailedTransitionError reports that the current state differs from the transition source state.

func NewInvalidTransitionError

func NewInvalidTransitionError(src, dst uint32, stateNames StateNames) *TransitionError

NewFailedTransitionError reports about a transition attempt that was not defined in the Constraints map.

func (*TransitionError) DstName

func (f *TransitionError) DstName() string

func (*TransitionError) Error

func (f *TransitionError) Error() string

func (*TransitionError) SrcName

func (f *TransitionError) SrcName() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL