Skip to content

Commit e668a85

Browse files
emindeneminabex
andauthored
race fixes (#94)
* 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]>
1 parent 525a1dd commit e668a85

File tree

4 files changed

+9
-17
lines changed

4 files changed

+9
-17
lines changed

.github/workflows/main.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
go-version: 1.16
1919

2020
- name: Test
21-
run: go test -coverprofile=coverage.out ./...
21+
run: go test -race -coverprofile=coverage.out ./...
2222

2323
- name: Convert coverage
2424
uses: jandelgado/[email protected]

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ default: services test
22

33
.PHONY: test
44
test:
5-
go test ./...
5+
go test -race ./...
66

77
.PHONY: lint
88
lint:

fsm.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@ func (f *FSM) Event(ctx context.Context, event string, args ...interface{}) erro
349349

350350
f.stateMu.Lock()
351351
f.current = dst
352+
f.transition = nil // treat the state transition as done
352353
f.stateMu.Unlock()
353354

354355
// at this point, we unlock the event mutex in order to allow
@@ -359,7 +360,6 @@ func (f *FSM) Event(ctx context.Context, event string, args ...interface{}) erro
359360
f.eventMu.Unlock()
360361
unlocked = true
361362
}
362-
f.transition = nil // treat the state transition as done
363363
f.enterStateCallbacks(ctx, e)
364364
f.afterEventCallbacks(ctx, e)
365365
}
@@ -420,7 +420,6 @@ func (t transitionerStruct) transition(f *FSM) error {
420420
return NotInTransitionError{}
421421
}
422422
f.transition()
423-
f.transition = nil
424423
return nil
425424
}
426425

fsm_test.go

+6-13
Original file line numberDiff line numberDiff line change
@@ -544,20 +544,17 @@ func TestCancelAsyncTransition(t *testing.T) {
544544
if !ok {
545545
t.Errorf("expected error to be 'AsyncError', got %v", err)
546546
}
547-
var asyncStateTransitionWasCanceled bool
547+
var asyncStateTransitionWasCanceled = make(chan struct{})
548548
go func() {
549549
<-asyncError.Ctx.Done()
550-
asyncStateTransitionWasCanceled = true
550+
close(asyncStateTransitionWasCanceled)
551551
}()
552552
asyncError.CancelTransition()
553-
time.Sleep(20 * time.Millisecond)
553+
<-asyncStateTransitionWasCanceled
554554

555555
if err = fsm.Transition(); err != nil {
556556
t.Errorf("expected no error, got %v", err)
557557
}
558-
if !asyncStateTransitionWasCanceled {
559-
t.Error("expected async state transition cancelation to have propagated")
560-
}
561558
if fsm.Current() != "start" {
562559
t.Error("expected state to be 'start'")
563560
}
@@ -775,7 +772,7 @@ func TestTransitionInCallbacks(t *testing.T) {
775772

776773
func TestContextInCallbacks(t *testing.T) {
777774
var fsm *FSM
778-
var enterEndAsyncWorkDone bool
775+
var enterEndAsyncWorkDone = make(chan struct{})
779776
fsm = NewFSM(
780777
"start",
781778
Events{
@@ -787,7 +784,7 @@ func TestContextInCallbacks(t *testing.T) {
787784
"enter_end": func(ctx context.Context, e *Event) {
788785
go func() {
789786
<-ctx.Done()
790-
enterEndAsyncWorkDone = true
787+
close(enterEndAsyncWorkDone)
791788
}()
792789

793790
<-ctx.Done()
@@ -806,11 +803,7 @@ func TestContextInCallbacks(t *testing.T) {
806803
if !errors.Is(err, context.Canceled) {
807804
t.Errorf("expected 'context canceled' error, got %v", err)
808805
}
809-
time.Sleep(20 * time.Millisecond)
810-
811-
if !enterEndAsyncWorkDone {
812-
t.Error("expected asynchronous work in callback to be done but it wasn't")
813-
}
806+
<-enterEndAsyncWorkDone
814807

815808
currentState := fsm.Current()
816809
if currentState != "end" {

0 commit comments

Comments
 (0)