@@ -25,11 +25,27 @@ func ScheduleTimer(ctx Context, delay time.Duration) Future[struct{}] {
25
25
26
26
scheduleEventID := wfState .GetNextScheduleEventID ()
27
27
at := Now (ctx ).Add (delay )
28
+
28
29
timerCmd := command .NewScheduleTimerCommand (scheduleEventID , at )
29
30
wfState .AddCommand (timerCmd )
30
-
31
31
wfState .TrackFuture (scheduleEventID , workflowstate .AsDecodingSettable (converter .GetConverter (ctx ), f ))
32
32
33
+ cancelReceiver := & sync.Receiver [struct {}]{
34
+ Receive : func (v struct {}, ok bool ) {
35
+ timerCmd .Cancel ()
36
+
37
+ // Remove the timer future from the workflow state and mark it as canceled if it hasn't already fired. This is different
38
+ // from subworkflow behavior, where we want to wait for the subworkflow to complete before proceeding. Here we can
39
+ // continue right away.
40
+ if fi , ok := f .(sync.FutureInternal [struct {}]); ok {
41
+ if ! fi .Ready () {
42
+ wfState .RemoveFuture (scheduleEventID )
43
+ f .Set (v , sync .Canceled )
44
+ }
45
+ }
46
+ },
47
+ }
48
+
33
49
ctx , span := workflowtracer .Tracer (ctx ).Start (ctx , "ScheduleTimer" ,
34
50
trace .WithAttributes (
35
51
attribute .Int64 ("duration_ms" , int64 (delay / time .Millisecond )),
@@ -42,26 +58,10 @@ func ScheduleTimer(ctx Context, delay time.Duration) Future[struct{}] {
42
58
if c , cancelable := ctx .Done ().(sync.CancelChannel ); cancelable {
43
59
// Register a callback for when it's canceled. The only operation on the `Done` channel
44
60
// is that it's closed when the context is canceled.
45
- canceled := false
46
-
47
- c .AddReceiveCallback (func (v struct {}, ok bool ) {
48
- // Ignore any future cancelation events for this timer
49
- if canceled {
50
- return
51
- }
52
- canceled = true
61
+ c .AddReceiveCallback (cancelReceiver )
53
62
54
- timerCmd .Cancel ()
55
-
56
- // Remove the timer future from the workflow state and mark it as canceled if it hasn't already fired. This is different
57
- // from subworkflow behavior, where we want to wait for the subworkflow to complete before proceeding. Here we can
58
- // continue right away.
59
- if fi , ok := f .(sync.FutureInternal [struct {}]); ok {
60
- if ! fi .Ready () {
61
- wfState .RemoveFuture (scheduleEventID )
62
- f .Set (v , sync .Canceled )
63
- }
64
- }
63
+ timerCmd .WhenDone (func () {
64
+ c .RemoveReceiveCallback (cancelReceiver )
65
65
})
66
66
}
67
67
0 commit comments