Instana tracer implementation
This commit is contained in:
parent
c2c6aee18a
commit
aef24dd74b
43 changed files with 4502 additions and 2 deletions
447
vendor/github.com/looplab/fsm/fsm.go
generated
vendored
Normal file
447
vendor/github.com/looplab/fsm/fsm.go
generated
vendored
Normal file
|
@ -0,0 +1,447 @@
|
|||
// Copyright (c) 2013 - Max Persson <max@looplab.se>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package fsm implements a finite state machine.
|
||||
//
|
||||
// It is heavily based on two FSM implementations:
|
||||
//
|
||||
// Javascript Finite State Machine
|
||||
// https://github.com/jakesgordon/javascript-state-machine
|
||||
//
|
||||
// Fysom for Python
|
||||
// https://github.com/oxplot/fysom (forked at https://github.com/mriehl/fysom)
|
||||
//
|
||||
package fsm
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// transitioner is an interface for the FSM's transition function.
|
||||
type transitioner interface {
|
||||
transition(*FSM) error
|
||||
}
|
||||
|
||||
// FSM is the state machine that holds the current state.
|
||||
//
|
||||
// It has to be created with NewFSM to function properly.
|
||||
type FSM struct {
|
||||
// current is the state that the FSM is currently in.
|
||||
current string
|
||||
|
||||
// transitions maps events and source states to destination states.
|
||||
transitions map[eKey]string
|
||||
|
||||
// callbacks maps events and targers to callback functions.
|
||||
callbacks map[cKey]Callback
|
||||
|
||||
// transition is the internal transition functions used either directly
|
||||
// or when Transition is called in an asynchronous state transition.
|
||||
transition func()
|
||||
// transitionerObj calls the FSM's transition() function.
|
||||
transitionerObj transitioner
|
||||
|
||||
// stateMu guards access to the current state.
|
||||
stateMu sync.RWMutex
|
||||
// eventMu guards access to Event() and Transition().
|
||||
eventMu sync.Mutex
|
||||
}
|
||||
|
||||
// EventDesc represents an event when initializing the FSM.
|
||||
//
|
||||
// The event can have one or more source states that is valid for performing
|
||||
// the transition. If the FSM is in one of the source states it will end up in
|
||||
// the specified destination state, calling all defined callbacks as it goes.
|
||||
type EventDesc struct {
|
||||
// Name is the event name used when calling for a transition.
|
||||
Name string
|
||||
|
||||
// Src is a slice of source states that the FSM must be in to perform a
|
||||
// state transition.
|
||||
Src []string
|
||||
|
||||
// Dst is the destination state that the FSM will be in if the transition
|
||||
// succeds.
|
||||
Dst string
|
||||
}
|
||||
|
||||
// Callback is a function type that callbacks should use. Event is the current
|
||||
// event info as the callback happens.
|
||||
type Callback func(*Event)
|
||||
|
||||
// Events is a shorthand for defining the transition map in NewFSM.
|
||||
type Events []EventDesc
|
||||
|
||||
// Callbacks is a shorthand for defining the callbacks in NewFSM.a
|
||||
type Callbacks map[string]Callback
|
||||
|
||||
// NewFSM constructs a FSM from events and callbacks.
|
||||
//
|
||||
// The events and transitions are specified as a slice of Event structs
|
||||
// specified as Events. Each Event is mapped to one or more internal
|
||||
// transitions from Event.Src to Event.Dst.
|
||||
//
|
||||
// Callbacks are added as a map specified as Callbacks where the key is parsed
|
||||
// as the callback event as follows, and called in the same order:
|
||||
//
|
||||
// 1. before_<EVENT> - called before event named <EVENT>
|
||||
//
|
||||
// 2. before_event - called before all events
|
||||
//
|
||||
// 3. leave_<OLD_STATE> - called before leaving <OLD_STATE>
|
||||
//
|
||||
// 4. leave_state - called before leaving all states
|
||||
//
|
||||
// 5. enter_<NEW_STATE> - called after entering <NEW_STATE>
|
||||
//
|
||||
// 6. enter_state - called after entering all states
|
||||
//
|
||||
// 7. after_<EVENT> - called after event named <EVENT>
|
||||
//
|
||||
// 8. after_event - called after all events
|
||||
//
|
||||
// There are also two short form versions for the most commonly used callbacks.
|
||||
// They are simply the name of the event or state:
|
||||
//
|
||||
// 1. <NEW_STATE> - called after entering <NEW_STATE>
|
||||
//
|
||||
// 2. <EVENT> - called after event named <EVENT>
|
||||
//
|
||||
// If both a shorthand version and a full version is specified it is undefined
|
||||
// which version of the callback will end up in the internal map. This is due
|
||||
// to the psuedo random nature of Go maps. No checking for multiple keys is
|
||||
// currently performed.
|
||||
func NewFSM(initial string, events []EventDesc, callbacks map[string]Callback) *FSM {
|
||||
f := &FSM{
|
||||
transitionerObj: &transitionerStruct{},
|
||||
current: initial,
|
||||
transitions: make(map[eKey]string),
|
||||
callbacks: make(map[cKey]Callback),
|
||||
}
|
||||
|
||||
// Build transition map and store sets of all events and states.
|
||||
allEvents := make(map[string]bool)
|
||||
allStates := make(map[string]bool)
|
||||
for _, e := range events {
|
||||
for _, src := range e.Src {
|
||||
f.transitions[eKey{e.Name, src}] = e.Dst
|
||||
allStates[src] = true
|
||||
allStates[e.Dst] = true
|
||||
}
|
||||
allEvents[e.Name] = true
|
||||
}
|
||||
|
||||
// Map all callbacks to events/states.
|
||||
for name, fn := range callbacks {
|
||||
var target string
|
||||
var callbackType int
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(name, "before_"):
|
||||
target = strings.TrimPrefix(name, "before_")
|
||||
if target == "event" {
|
||||
target = ""
|
||||
callbackType = callbackBeforeEvent
|
||||
} else if _, ok := allEvents[target]; ok {
|
||||
callbackType = callbackBeforeEvent
|
||||
}
|
||||
case strings.HasPrefix(name, "leave_"):
|
||||
target = strings.TrimPrefix(name, "leave_")
|
||||
if target == "state" {
|
||||
target = ""
|
||||
callbackType = callbackLeaveState
|
||||
} else if _, ok := allStates[target]; ok {
|
||||
callbackType = callbackLeaveState
|
||||
}
|
||||
case strings.HasPrefix(name, "enter_"):
|
||||
target = strings.TrimPrefix(name, "enter_")
|
||||
if target == "state" {
|
||||
target = ""
|
||||
callbackType = callbackEnterState
|
||||
} else if _, ok := allStates[target]; ok {
|
||||
callbackType = callbackEnterState
|
||||
}
|
||||
case strings.HasPrefix(name, "after_"):
|
||||
target = strings.TrimPrefix(name, "after_")
|
||||
if target == "event" {
|
||||
target = ""
|
||||
callbackType = callbackAfterEvent
|
||||
} else if _, ok := allEvents[target]; ok {
|
||||
callbackType = callbackAfterEvent
|
||||
}
|
||||
default:
|
||||
target = name
|
||||
if _, ok := allStates[target]; ok {
|
||||
callbackType = callbackEnterState
|
||||
} else if _, ok := allEvents[target]; ok {
|
||||
callbackType = callbackAfterEvent
|
||||
}
|
||||
}
|
||||
|
||||
if callbackType != callbackNone {
|
||||
f.callbacks[cKey{target, callbackType}] = fn
|
||||
}
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
// Current returns the current state of the FSM.
|
||||
func (f *FSM) Current() string {
|
||||
f.stateMu.RLock()
|
||||
defer f.stateMu.RUnlock()
|
||||
return f.current
|
||||
}
|
||||
|
||||
// Is returns true if state is the current state.
|
||||
func (f *FSM) Is(state string) bool {
|
||||
f.stateMu.RLock()
|
||||
defer f.stateMu.RUnlock()
|
||||
return state == f.current
|
||||
}
|
||||
|
||||
// SetState allows the user to move to the given state from current state.
|
||||
// The call does not trigger any callbacks, if defined.
|
||||
func (f *FSM) SetState(state string) {
|
||||
f.stateMu.Lock()
|
||||
defer f.stateMu.Unlock()
|
||||
f.current = state
|
||||
return
|
||||
}
|
||||
|
||||
// Can returns true if event can occur in the current state.
|
||||
func (f *FSM) Can(event string) bool {
|
||||
f.stateMu.RLock()
|
||||
defer f.stateMu.RUnlock()
|
||||
_, ok := f.transitions[eKey{event, f.current}]
|
||||
return ok && (f.transition == nil)
|
||||
}
|
||||
|
||||
// AvailableTransitions returns a list of transitions avilable in the
|
||||
// current state.
|
||||
func (f *FSM) AvailableTransitions() []string {
|
||||
f.stateMu.RLock()
|
||||
defer f.stateMu.RUnlock()
|
||||
var transitions []string
|
||||
for key := range f.transitions {
|
||||
if key.src == f.current {
|
||||
transitions = append(transitions, key.event)
|
||||
}
|
||||
}
|
||||
return transitions
|
||||
}
|
||||
|
||||
// Cannot returns true if event can not occure in the current state.
|
||||
// It is a convenience method to help code read nicely.
|
||||
func (f *FSM) Cannot(event string) bool {
|
||||
return !f.Can(event)
|
||||
}
|
||||
|
||||
// Event initiates a state transition with the named event.
|
||||
//
|
||||
// The call takes a variable number of arguments that will be passed to the
|
||||
// callback, if defined.
|
||||
//
|
||||
// It will return nil if the state change is ok or one of these errors:
|
||||
//
|
||||
// - event X inappropriate because previous transition did not complete
|
||||
//
|
||||
// - event X inappropriate in current state Y
|
||||
//
|
||||
// - event X does not exist
|
||||
//
|
||||
// - internal error on state transition
|
||||
//
|
||||
// The last error should never occur in this situation and is a sign of an
|
||||
// internal bug.
|
||||
func (f *FSM) Event(event string, args ...interface{}) error {
|
||||
f.eventMu.Lock()
|
||||
defer f.eventMu.Unlock()
|
||||
|
||||
f.stateMu.RLock()
|
||||
defer f.stateMu.RUnlock()
|
||||
|
||||
if f.transition != nil {
|
||||
return InTransitionError{event}
|
||||
}
|
||||
|
||||
dst, ok := f.transitions[eKey{event, f.current}]
|
||||
if !ok {
|
||||
for ekey := range f.transitions {
|
||||
if ekey.event == event {
|
||||
return InvalidEventError{event, f.current}
|
||||
}
|
||||
}
|
||||
return UnknownEventError{event}
|
||||
}
|
||||
|
||||
e := &Event{f, event, f.current, dst, nil, args, false, false}
|
||||
|
||||
err := f.beforeEventCallbacks(e)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if f.current == dst {
|
||||
f.afterEventCallbacks(e)
|
||||
return NoTransitionError{e.Err}
|
||||
}
|
||||
|
||||
// Setup the transition, call it later.
|
||||
f.transition = func() {
|
||||
f.stateMu.Lock()
|
||||
f.current = dst
|
||||
f.stateMu.Unlock()
|
||||
|
||||
f.enterStateCallbacks(e)
|
||||
f.afterEventCallbacks(e)
|
||||
}
|
||||
|
||||
if err = f.leaveStateCallbacks(e); err != nil {
|
||||
if _, ok := err.(CanceledError); ok {
|
||||
f.transition = nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Perform the rest of the transition, if not asynchronous.
|
||||
f.stateMu.RUnlock()
|
||||
err = f.doTransition()
|
||||
f.stateMu.RLock()
|
||||
if err != nil {
|
||||
return InternalError{}
|
||||
}
|
||||
|
||||
return e.Err
|
||||
}
|
||||
|
||||
// Transition wraps transitioner.transition.
|
||||
func (f *FSM) Transition() error {
|
||||
f.eventMu.Lock()
|
||||
defer f.eventMu.Unlock()
|
||||
return f.doTransition()
|
||||
}
|
||||
|
||||
// doTransition wraps transitioner.transition.
|
||||
func (f *FSM) doTransition() error {
|
||||
return f.transitionerObj.transition(f)
|
||||
}
|
||||
|
||||
// transitionerStruct is the default implementation of the transitioner
|
||||
// interface. Other implementations can be swapped in for testing.
|
||||
type transitionerStruct struct{}
|
||||
|
||||
// Transition completes an asynchrounous state change.
|
||||
//
|
||||
// The callback for leave_<STATE> must prviously have called Async on its
|
||||
// event to have initiated an asynchronous state transition.
|
||||
func (t transitionerStruct) transition(f *FSM) error {
|
||||
if f.transition == nil {
|
||||
return NotInTransitionError{}
|
||||
}
|
||||
f.transition()
|
||||
f.transition = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// beforeEventCallbacks calls the before_ callbacks, first the named then the
|
||||
// general version.
|
||||
func (f *FSM) beforeEventCallbacks(e *Event) error {
|
||||
if fn, ok := f.callbacks[cKey{e.Event, callbackBeforeEvent}]; ok {
|
||||
fn(e)
|
||||
if e.canceled {
|
||||
return CanceledError{e.Err}
|
||||
}
|
||||
}
|
||||
if fn, ok := f.callbacks[cKey{"", callbackBeforeEvent}]; ok {
|
||||
fn(e)
|
||||
if e.canceled {
|
||||
return CanceledError{e.Err}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// leaveStateCallbacks calls the leave_ callbacks, first the named then the
|
||||
// general version.
|
||||
func (f *FSM) leaveStateCallbacks(e *Event) error {
|
||||
if fn, ok := f.callbacks[cKey{f.current, callbackLeaveState}]; ok {
|
||||
fn(e)
|
||||
if e.canceled {
|
||||
return CanceledError{e.Err}
|
||||
} else if e.async {
|
||||
return AsyncError{e.Err}
|
||||
}
|
||||
}
|
||||
if fn, ok := f.callbacks[cKey{"", callbackLeaveState}]; ok {
|
||||
fn(e)
|
||||
if e.canceled {
|
||||
return CanceledError{e.Err}
|
||||
} else if e.async {
|
||||
return AsyncError{e.Err}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// enterStateCallbacks calls the enter_ callbacks, first the named then the
|
||||
// general version.
|
||||
func (f *FSM) enterStateCallbacks(e *Event) {
|
||||
if fn, ok := f.callbacks[cKey{f.current, callbackEnterState}]; ok {
|
||||
fn(e)
|
||||
}
|
||||
if fn, ok := f.callbacks[cKey{"", callbackEnterState}]; ok {
|
||||
fn(e)
|
||||
}
|
||||
}
|
||||
|
||||
// afterEventCallbacks calls the after_ callbacks, first the named then the
|
||||
// general version.
|
||||
func (f *FSM) afterEventCallbacks(e *Event) {
|
||||
if fn, ok := f.callbacks[cKey{e.Event, callbackAfterEvent}]; ok {
|
||||
fn(e)
|
||||
}
|
||||
if fn, ok := f.callbacks[cKey{"", callbackAfterEvent}]; ok {
|
||||
fn(e)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
callbackNone int = iota
|
||||
callbackBeforeEvent
|
||||
callbackLeaveState
|
||||
callbackEnterState
|
||||
callbackAfterEvent
|
||||
)
|
||||
|
||||
// cKey is a struct key used for keeping the callbacks mapped to a target.
|
||||
type cKey struct {
|
||||
// target is either the name of a state or an event depending on which
|
||||
// callback type the key refers to. It can also be "" for a non-targeted
|
||||
// callback like before_event.
|
||||
target string
|
||||
|
||||
// callbackType is the situation when the callback will be run.
|
||||
callbackType int
|
||||
}
|
||||
|
||||
// eKey is a struct key used for storing the transition map.
|
||||
type eKey struct {
|
||||
// event is the name of the event that the keys refers to.
|
||||
event string
|
||||
|
||||
// src is the source from where the event can transition.
|
||||
src string
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue