From b2f0ab5cc1b0529718bd94b4b3770787847fe409 Mon Sep 17 00:00:00 2001 From: Igor Kuzmin <146714691+kuzmig@users.noreply.github.com> Date: Tue, 13 Feb 2024 19:20:45 +0300 Subject: [PATCH] Deadlock calling Event in after-event callback when current state == dst (#104) * Deadlock in afterEventCallbacks in case when src == dst fixed * Test added --------- Co-authored-by: Igor Kuzmin --- fsm.go | 4 ++++ fsm_test.go | 25 +++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/fsm.go b/fsm.go index bd269d7..4465867 100644 --- a/fsm.go +++ b/fsm.go @@ -333,6 +333,10 @@ func (f *FSM) Event(ctx context.Context, event string, args ...interface{}) erro } if f.current == dst { + f.stateMu.RUnlock() + defer f.stateMu.RLock() + f.eventMu.Unlock() + unlocked = true f.afterEventCallbacks(ctx, e) return NoTransitionError{e.Err} } diff --git a/fsm_test.go b/fsm_test.go index 8e0c4ed..4d4479a 100644 --- a/fsm_test.go +++ b/fsm_test.go @@ -825,6 +825,31 @@ func TestNoTransition(t *testing.T) { } } +func TestNoTransitionAfterEventCallbackTransition(t *testing.T) { + var fsm *FSM + fsm = NewFSM( + "start", + Events{ + {Name: "run", Src: []string{"start"}, Dst: "start"}, + {Name: "finish", Src: []string{"start"}, Dst: "finished"}, + }, + Callbacks{ + "after_event": func(_ context.Context, e *Event) { + fsm.Event(context.Background(), "finish") + }, + }, + ) + err := fsm.Event(context.Background(), "run") + if _, ok := err.(NoTransitionError); !ok { + t.Error("expected 'NoTransitionError'") + } + + currentState := fsm.Current() + if currentState != "finished" { + t.Errorf("expected state to be 'finished', was '%s'", currentState) + } +} + func ExampleNewFSM() { fsm := NewFSM( "green",