* runtime: fix ticker not stopping when Stop races with its callback
On the threads and cores schedulers, timer callbacks run concurrently
with user goroutines.
If Stop (or Reset) was called in the window after the node was popped
but before its callback re-added it, removeTimer would not find the
timer in the queue, so it would be re-added to timer anyway.
Track timers whose callback is currently running in a firing list, and
have removeTimer mark a firing timer as stopped so its callback does not
re-add it.
Also make the timers test drain robust: allow a possible in-flight tick
delivered concurrently with Stop to settle before draining the channel.
Signed-off-by: deadprogram <ron@hybridgroup.com>
* runtime: fix Stop/Reset semantics when racing a firing timer callback
Address review feedback on the ticker Stop-race fix. On the threads and
cores schedulers a timer callback runs concurrently with user goroutines,
which left several problems:
- removeTimer reported a firing timer as successfully removed, so
Stop/Reset could return true even though the callback had already
started (wrong semantics, notably for AfterFunc). firingTimerStop now
returns a bool and removeTimer no longer hands the still-firing node
back to resetTimer.
- The periodic advance (when += period) ran in timerCallback outside the
scheduler timer lock. Move it into each scheduler's reAddTimer, under
the lock and after the stopped check, so a concurrent Reset can't have
its freshly-queued deadline corrupted.
- resetTimer now sets when/period after removeTimer for the same reason.
Add testdata/timer_stop_reset_race.go and TestTimerStopResetRace, which
reproduce the stop-while-firing and reset-while-firing races via the
runtime timer linkname hooks.
Signed-off-by: deadprogram <ron@hybridgroup.com>
* runtime: fix timer Stop/Reset race with firing periodic timers
Signed-off-by: deadprogram <ron@hybridgroup.com>
---------
Signed-off-by: deadprogram <ron@hybridgroup.com>
Ticker.Stop does not drain already-buffered ticks from the channel,
so on slow CI runners a tick may already be queued before Stop takes
effect, causing a spurious "fail: ticker should have stopped!" failure.
Drain the channel immediately after Stop before checking that no new
ticks arrive.
Signed-off-by: deadprogram <ron@hybridgroup.com>
This commit adds support for time.NewTimer and time.NewTicker. It also
adds support for the Stop() method on time.Timer, but doesn't (yet) add
support for the Reset() method.
The implementation has been carefully written so that programs that
don't use these timers will normally not see an increase in RAM or
binary size. None of the examples in the drivers repo change as a result
of this commit. This comes at the cost of slightly more complex code and
possibly slower execution of the timers when they are used.