Skip to content

Commit

Permalink
race fixes (#94)
Browse files Browse the repository at this point in the history
* race fixes

* race flag

* ci race flag

* update transition within state mutex, this prevents races at Can calls

---------

Co-authored-by: Emin Tasgetiren <[email protected]>
  • Loading branch information
eminden and eminabex authored Feb 2, 2023
1 parent 525a1dd commit e668a85
Show file tree
Hide file tree
Showing 4 changed files with 9 additions and 17 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
go-version: 1.16

- name: Test
run: go test -coverprofile=coverage.out ./...
run: go test -race -coverprofile=coverage.out ./...

- name: Convert coverage
uses: jandelgado/[email protected]
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ default: services test

.PHONY: test
test:
go test ./...
go test -race ./...

.PHONY: lint
lint:
Expand Down
3 changes: 1 addition & 2 deletions fsm.go
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ func (f *FSM) Event(ctx context.Context, event string, args ...interface{}) erro

f.stateMu.Lock()
f.current = dst
f.transition = nil // treat the state transition as done
f.stateMu.Unlock()

// at this point, we unlock the event mutex in order to allow
Expand All @@ -359,7 +360,6 @@ func (f *FSM) Event(ctx context.Context, event string, args ...interface{}) erro
f.eventMu.Unlock()
unlocked = true
}
f.transition = nil // treat the state transition as done
f.enterStateCallbacks(ctx, e)
f.afterEventCallbacks(ctx, e)
}
Expand Down Expand Up @@ -420,7 +420,6 @@ func (t transitionerStruct) transition(f *FSM) error {
return NotInTransitionError{}
}
f.transition()
f.transition = nil
return nil
}

Expand Down
19 changes: 6 additions & 13 deletions fsm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -544,20 +544,17 @@ func TestCancelAsyncTransition(t *testing.T) {
if !ok {
t.Errorf("expected error to be 'AsyncError', got %v", err)
}
var asyncStateTransitionWasCanceled bool
var asyncStateTransitionWasCanceled = make(chan struct{})
go func() {
<-asyncError.Ctx.Done()
asyncStateTransitionWasCanceled = true
close(asyncStateTransitionWasCanceled)
}()
asyncError.CancelTransition()
time.Sleep(20 * time.Millisecond)
<-asyncStateTransitionWasCanceled

if err = fsm.Transition(); err != nil {
t.Errorf("expected no error, got %v", err)
}
if !asyncStateTransitionWasCanceled {
t.Error("expected async state transition cancelation to have propagated")
}
if fsm.Current() != "start" {
t.Error("expected state to be 'start'")
}
Expand Down Expand Up @@ -775,7 +772,7 @@ func TestTransitionInCallbacks(t *testing.T) {

func TestContextInCallbacks(t *testing.T) {
var fsm *FSM
var enterEndAsyncWorkDone bool
var enterEndAsyncWorkDone = make(chan struct{})
fsm = NewFSM(
"start",
Events{
Expand All @@ -787,7 +784,7 @@ func TestContextInCallbacks(t *testing.T) {
"enter_end": func(ctx context.Context, e *Event) {
go func() {
<-ctx.Done()
enterEndAsyncWorkDone = true
close(enterEndAsyncWorkDone)
}()

<-ctx.Done()
Expand All @@ -806,11 +803,7 @@ func TestContextInCallbacks(t *testing.T) {
if !errors.Is(err, context.Canceled) {
t.Errorf("expected 'context canceled' error, got %v", err)
}
time.Sleep(20 * time.Millisecond)

if !enterEndAsyncWorkDone {
t.Error("expected asynchronous work in callback to be done but it wasn't")
}
<-enterEndAsyncWorkDone

currentState := fsm.Current()
if currentState != "end" {
Expand Down

0 comments on commit e668a85

Please sign in to comment.