Skip to content

Commit

Permalink
Merge d0046a7 into 13de56e
Browse files Browse the repository at this point in the history
  • Loading branch information
annismckenzie committed Jul 22, 2022
2 parents 13de56e + d0046a7 commit 551f76d
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 120 deletions.
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ From examples/simple.go:
package main

import (
"context"
"fmt"

"github.com/looplab/fsm"
)

Expand All @@ -39,14 +41,14 @@ func main() {

fmt.Println(fsm.Current())

err := fsm.Event("open")
err := fsm.Event(context.Background(), "open")
if err != nil {
fmt.Println(err)
}

fmt.Println(fsm.Current())

err = fsm.Event("close")
err = fsm.Event(context.Background(), "close")
if err != nil {
fmt.Println(err)
}
Expand All @@ -63,7 +65,9 @@ From examples/struct.go:
package main

import (
"context"
"fmt"

"github.com/looplab/fsm"
)

Expand All @@ -84,7 +88,7 @@ func NewDoor(to string) *Door {
{Name: "close", Src: []string{"open"}, Dst: "closed"},
},
fsm.Callbacks{
"enter_state": func(e *fsm.Event) { d.enterState(e) },
"enter_state": func(_ context.Context, e *fsm.Event) { d.enterState(e) },
},
)

Expand All @@ -98,12 +102,12 @@ func (d *Door) enterState(e *fsm.Event) {
func main() {
door := NewDoor("heaven")

err := door.FSM.Event("open")
err := door.FSM.Event(context.Background(), "open")
if err != nil {
fmt.Println(err)
}

err = door.FSM.Event("close")
err = door.FSM.Event(context.Background(), "close")
if err != nil {
fmt.Println(err)
}
Expand Down
4 changes: 4 additions & 0 deletions event.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,17 @@ type Event struct {

// async is an internal flag set if the transition should be asynchronous
async bool

// cancelFunc is called in case the event is canceled.
cancelFunc func()
}

// Cancel can be called in before_<EVENT> or leave_<STATE> to cancel the
// current transition before it happens. It takes an optional error, which will
// overwrite e.Err if set before.
func (e *Event) Cancel(err ...error) {
e.canceled = true
e.cancelFunc()

if len(err) > 0 {
e.Err = err[0]
Expand Down
19 changes: 11 additions & 8 deletions examples/alternate.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
//go:build ignore
// +build ignore

package main

import (
"context"
"fmt"

"github.com/looplab/fsm"
)

Expand All @@ -18,45 +21,45 @@ func main() {
{Name: "finish", Src: []string{"scanning"}, Dst: "idle"},
},
fsm.Callbacks{
"scan": func(e *fsm.Event) {
"scan": func(_ context.Context, e *fsm.Event) {
fmt.Println("after_scan: " + e.FSM.Current())
},
"working": func(e *fsm.Event) {
"working": func(_ context.Context, e *fsm.Event) {
fmt.Println("working: " + e.FSM.Current())
},
"situation": func(e *fsm.Event) {
"situation": func(_ context.Context, e *fsm.Event) {
fmt.Println("situation: " + e.FSM.Current())
},
"finish": func(e *fsm.Event) {
"finish": func(_ context.Context, e *fsm.Event) {
fmt.Println("finish: " + e.FSM.Current())
},
},
)

fmt.Println(fsm.Current())

err := fsm.Event("scan")
err := fsm.Event(context.Background(), "scan")
if err != nil {
fmt.Println(err)
}

fmt.Println("1:" + fsm.Current())

err = fsm.Event("working")
err = fsm.Event(context.Background(), "working")
if err != nil {
fmt.Println(err)
}

fmt.Println("2:" + fsm.Current())

err = fsm.Event("situation")
err = fsm.Event(context.Background(), "situation")
if err != nil {
fmt.Println(err)
}

fmt.Println("3:" + fsm.Current())

err = fsm.Event("finish")
err = fsm.Event(context.Background(), "finish")
if err != nil {
fmt.Println(err)
}
Expand Down
10 changes: 6 additions & 4 deletions examples/data.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
//go:build ignore
// +build ignore

package main

import (
"context"
"fmt"

"github.com/looplab/fsm"
Expand All @@ -16,11 +18,11 @@ func main() {
{Name: "consume", Src: []string{"idle"}, Dst: "idle"},
},
fsm.Callbacks{
"produce": func(e *fsm.Event) {
"produce": func(_ context.Context, e *fsm.Event) {
e.FSM.SetMetadata("message", "hii")
fmt.Println("produced data")
},
"consume": func(e *fsm.Event) {
"consume": func(_ context.Context, e *fsm.Event) {
message, ok := e.FSM.Metadata("message")
if ok {
fmt.Println("message = " + message.(string))
Expand All @@ -32,14 +34,14 @@ func main() {

fmt.Println(fsm.Current())

err := fsm.Event("produce")
err := fsm.Event(context.Background(), "produce")
if err != nil {
fmt.Println(err)
}

fmt.Println(fsm.Current())

err = fsm.Event("consume")
err = fsm.Event(context.Background(), "consume")
if err != nil {
fmt.Println(err)
}
Expand Down
7 changes: 5 additions & 2 deletions examples/simple.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
//go:build ignore
// +build ignore

package main

import (
"context"
"fmt"

"github.com/looplab/fsm"
)

Expand All @@ -19,14 +22,14 @@ func main() {

fmt.Println(fsm.Current())

err := fsm.Event("open")
err := fsm.Event(context.Background(), "open")
if err != nil {
fmt.Println(err)
}

fmt.Println(fsm.Current())

err = fsm.Event("close")
err = fsm.Event(context.Background(), "close")
if err != nil {
fmt.Println(err)
}
Expand Down
9 changes: 6 additions & 3 deletions examples/struct.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
//go:build ignore
// +build ignore

package main

import (
"context"
"fmt"

"github.com/looplab/fsm"
)

Expand All @@ -24,7 +27,7 @@ func NewDoor(to string) *Door {
{Name: "close", Src: []string{"open"}, Dst: "closed"},
},
fsm.Callbacks{
"enter_state": func(e *fsm.Event) { d.enterState(e) },
"enter_state": func(_ context.Context, e *fsm.Event) { d.enterState(e) },
},
)

Expand All @@ -38,12 +41,12 @@ func (d *Door) enterState(e *fsm.Event) {
func main() {
door := NewDoor("heaven")

err := door.FSM.Event("open")
err := door.FSM.Event(context.Background(), "open")
if err != nil {
fmt.Println(err)
}

err = door.FSM.Event("close")
err = door.FSM.Event(context.Background(), "close")
if err != nil {
fmt.Println(err)
}
Expand Down
43 changes: 23 additions & 20 deletions fsm.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
package fsm

import (
"context"
"strings"
"sync"
)
Expand Down Expand Up @@ -84,7 +85,7 @@ type EventDesc struct {

// Callback is a function type that callbacks should use. Event is the current
// event info as the callback happens.
type Callback func(*Event)
type Callback func(context.Context, *Event)

// Events is a shorthand for defining the transition map in NewFSM.
type Events []EventDesc
Expand Down Expand Up @@ -286,7 +287,7 @@ func (f *FSM) SetMetadata(key string, dataValue interface{}) {
//
// 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 {
func (f *FSM) Event(ctx context.Context, event string, args ...interface{}) error {
f.eventMu.Lock()
defer f.eventMu.Unlock()

Expand All @@ -307,15 +308,17 @@ func (f *FSM) Event(event string, args ...interface{}) error {
return UnknownEventError{event}
}

e := &Event{f, event, f.current, dst, nil, args, false, false}
ctx, cancel := context.WithCancel(ctx)
defer cancel()
e := &Event{f, event, f.current, dst, nil, args, false, false, cancel}

err := f.beforeEventCallbacks(e)
err := f.beforeEventCallbacks(ctx, e)
if err != nil {
return err
}

if f.current == dst {
f.afterEventCallbacks(e)
f.afterEventCallbacks(ctx, e)
return NoTransitionError{e.Err}
}

Expand All @@ -325,11 +328,11 @@ func (f *FSM) Event(event string, args ...interface{}) error {
f.current = dst
f.stateMu.Unlock()

f.enterStateCallbacks(e)
f.afterEventCallbacks(e)
f.enterStateCallbacks(ctx, e)
f.afterEventCallbacks(ctx, e)
}

if err = f.leaveStateCallbacks(e); err != nil {
if err = f.leaveStateCallbacks(ctx, e); err != nil {
if _, ok := err.(CanceledError); ok {
f.transition = nil
}
Expand Down Expand Up @@ -378,15 +381,15 @@ func (t transitionerStruct) transition(f *FSM) error {

// beforeEventCallbacks calls the before_ callbacks, first the named then the
// general version.
func (f *FSM) beforeEventCallbacks(e *Event) error {
func (f *FSM) beforeEventCallbacks(ctx context.Context, e *Event) error {
if fn, ok := f.callbacks[cKey{e.Event, callbackBeforeEvent}]; ok {
fn(e)
fn(ctx, e)
if e.canceled {
return CanceledError{e.Err}
}
}
if fn, ok := f.callbacks[cKey{"", callbackBeforeEvent}]; ok {
fn(e)
fn(ctx, e)
if e.canceled {
return CanceledError{e.Err}
}
Expand All @@ -396,17 +399,17 @@ func (f *FSM) beforeEventCallbacks(e *Event) error {

// leaveStateCallbacks calls the leave_ callbacks, first the named then the
// general version.
func (f *FSM) leaveStateCallbacks(e *Event) error {
func (f *FSM) leaveStateCallbacks(ctx context.Context, e *Event) error {
if fn, ok := f.callbacks[cKey{f.current, callbackLeaveState}]; ok {
fn(e)
fn(ctx, 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)
fn(ctx, e)
if e.canceled {
return CanceledError{e.Err}
} else if e.async {
Expand All @@ -418,23 +421,23 @@ func (f *FSM) leaveStateCallbacks(e *Event) error {

// enterStateCallbacks calls the enter_ callbacks, first the named then the
// general version.
func (f *FSM) enterStateCallbacks(e *Event) {
func (f *FSM) enterStateCallbacks(ctx context.Context, e *Event) {
if fn, ok := f.callbacks[cKey{f.current, callbackEnterState}]; ok {
fn(e)
fn(ctx, e)
}
if fn, ok := f.callbacks[cKey{"", callbackEnterState}]; ok {
fn(e)
fn(ctx, e)
}
}

// afterEventCallbacks calls the after_ callbacks, first the named then the
// general version.
func (f *FSM) afterEventCallbacks(e *Event) {
func (f *FSM) afterEventCallbacks(ctx context.Context, e *Event) {
if fn, ok := f.callbacks[cKey{e.Event, callbackAfterEvent}]; ok {
fn(e)
fn(ctx, e)
}
if fn, ok := f.callbacks[cKey{"", callbackAfterEvent}]; ok {
fn(e)
fn(ctx, e)
}
}

Expand Down
Loading

0 comments on commit 551f76d

Please sign in to comment.