Standard library patterns · 3 MIN READ
Timers and cancellation: versioned semantics still need a single owner
Explain Stop/Reset behavior, timer ownership and why select is not a priority statement.
Timers have both an API contract and version-dependent implementation behavior. Modern Go timer channels avoid the old buffered stale-value behavior under the documented module/runtime conditions, but this does not let several goroutines reset, receive and decide business state without a protocol. Treat a timer as one owner’s scheduling tool and separately define which events may commit work.
stdlib · A MAP OF THE IDEA
- Choose documented semantics
- Keep one timer owner
- Select ready events
- Check state before effect
Useful when
- Explain Stop/Reset behavior, timer ownership and why select is not a priority statement.
- Makes the owner and the failure boundary explicit.
Watch out
- Repeated time.After in a hot loop allocates timers and is not a substitute for a clear lifecycle.
- Run native service and failure tests in your own development environment.
Implementation & operation cost: Additional state and tests must be justified by the demonstrated failure; this lesson is not a requirement for every Go program.
The contract and the failure
Timers have both an API contract and version-dependent implementation behavior. Modern Go timer channels avoid the old buffered stale-value behavior under the documented module/runtime conditions, but this does not let several goroutines reset, receive and decide business state without a protocol. Treat a timer as one owner’s scheduling tool and separately define which events may commit work.
Work through the example
For Go 1.23-and-later timer-channel semantics, inspect the module’s go directive and any asynctimerchan compatibility setting when diagnosing old patterns. Do not use len(timer.C) to synchronize. Stop/Reset documentation distinguishes modern guarantees from older drain patterns. A select with several ready cases chooses among them; listing ctx.Done first does not grant cancellation priority.
package main
import (
"fmt"
"time"
)
func main() {
timer := time.NewTimer(time.Hour)
defer timer.Stop()
timer.Reset(0)
<-timer.C
fmt.Println("one owner received the reset timer")
}One goroutine creates, resets and receives the timer. No second receiver can consume its event unexpectedly. The example does not infer fairness or a global ordering against cancellation from a timer firing. It is compatible with the modern timer contract used by the course; review the time package documentation when maintaining older modules or runtime compatibility settings.
Production decisions
Repeated time.After in a hot loop allocates timers and is not a substitute for a clear lifecycle. Stopping a timer does not close its channel or prove a worker exited. If cancellation must prevent a side effect, recheck the operation state under the same synchronization that commits the effect. Test ready timer plus cancellation, reset ownership and shutdown without relying on lucky sleeps.
Explain it in an interview
If ctx.Done is the first select case and another case is ready, does cancellation always win? No. Ready cases have no source-order priority; define ownership and commit checks explicitly rather than depending on select order. Describe the state before the operation, its owner, the failure between steps and the evidence you would test. Distinguish what this example demonstrates from the infrastructure a production service still needs.
TAKE THIS WITH YOU
Explain Stop/Reset behavior, timer ownership and why select is not a priority statement. No. Ready cases have no source-order priority; define ownership and commit checks explicitly rather than depending on select order.
Check your understanding
If ctx.Done is the first select case and another case is ready, does cancellation always win?
Go to the source
Progress stays in this browser. No account needed.