Documentation
¶
Overview ¶
Package timewheel provides a generic, high-performance timer wheel implementation.
A time wheel is a data structure used to efficiently manage a large number of timers. It works by dividing time into fixed-size slots arranged in a circular buffer. Each tick advances the pointer by one slot and executes any tasks scheduled for that slot.
Basic usage ¶
tw, err := timewheel.New[string](
100*time.Millisecond, // tick interval (resolution)
60, // number of slots
func(data string) { // default job
fmt.Println("fired:", data)
},
)
if err != nil {
log.Fatal(err)
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
tw.Start(ctx)
key := tw.AddTimer(500*time.Millisecond, "hello")
if t, ok := tw.NextFireTime(key); ok {
fmt.Println("fires at", t)
}
Generics ¶
TimeWheel is parameterised over the task-data type T. This eliminates the need for type assertions and makes the API type-safe at compile time.
Concurrency ¶
All public methods are safe for concurrent use. Internally each slot owns its own mutex so that concurrent add / remove operations on different slots do not block one another. The event loop itself runs in a single goroutine; actual job execution is dispatched to separate goroutines (optionally bounded by a worker-pool semaphore).
Index ¶
- type Job
- type Logger
- type Option
- type Stats
- type TimeWheel
- func (tw *TimeWheel[T]) AddRepeating(delay time.Duration, data T) uint64
- func (tw *TimeWheel[T]) AddRepeatingWithJob(delay time.Duration, data T, job Job[T]) uint64
- func (tw *TimeWheel[T]) AddTimer(delay time.Duration, data T) uint64
- func (tw *TimeWheel[T]) AddTimerFunc(delay time.Duration, fn func()) uint64
- func (tw *TimeWheel[T]) AddTimerWithJob(delay time.Duration, data T, job Job[T]) uint64
- func (tw *TimeWheel[T]) NextFireTime(key uint64) (time.Time, bool)
- func (tw *TimeWheel[T]) PendingTimers() []TimerInfo
- func (tw *TimeWheel[T]) RemoveTimer(key uint64)
- func (tw *TimeWheel[T]) Start(ctx context.Context)
- func (tw *TimeWheel[T]) Stats() Stats
- func (tw *TimeWheel[T]) Wait()
- type TimerInfo
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Job ¶
type Job[T any] func(data T)
Job is the callback signature invoked when a timer fires. T is the type of the payload that was registered with the timer.
type Logger ¶
Logger is the minimal logging interface used by TimeWheel.
The package does not bind to any concrete logging implementation. Callers may pass *slog.Logger directly or adapt zap, zerolog, or another logger. If no logger is configured, TimeWheel does not emit internal logs.
type Option ¶
type Option[T any] func(*config[T])
Option is a functional option for New.
func WithErrorHandler ¶
WithErrorHandler registers a function that is called with the recovered value whenever a job panics. If not set, panics propagate and crash the program.
func WithLogger ¶
WithLogger configures the logger used for internal diagnostic messages.
func WithWorkerPool ¶
WithWorkerPool limits the number of concurrently running job goroutines to n. When n <= 0 the option is ignored and goroutines are spawned without bound.
type Stats ¶
type Stats struct {
// Pending is the number of tasks currently queued in the wheel.
Pending int64
// Executed is the total number of tasks that have been dispatched.
Executed int64
// Removed is the total number of tasks explicitly cancelled via [TimeWheel.RemoveTimer].
Removed int64
}
Stats is a snapshot of runtime counters. All fields are read atomically.
type TimeWheel ¶
type TimeWheel[T any] struct { // contains filtered or unexported fields }
TimeWheel is a generic timer wheel.
T is the type of the data payload stored with each timer. Use New to create a TimeWheel; the zero value is not usable.
func New ¶
func New[T any](interval time.Duration, slotNum int, defaultJob Job[T], opts ...Option[T]) (*TimeWheel[T], error)
New creates and initialises a new TimeWheel.
Parameters:
- interval: tick resolution; the minimum timer precision equals interval.
- slotNum: number of slots in the wheel. Together with interval this determines the maximum delay before circle counting kicks in: maxDirect = interval × slotNum.
- defaultJob: callback invoked for tasks that have no per-task job. May be nil if every timer is registered via TimeWheel.AddTimerWithJob.
- opts: zero or more Option values.
New returns an error if interval or slotNum are not positive.
func (*TimeWheel[T]) AddRepeating ¶
AddRepeating enqueues a recurring timer. After firing, the task is automatically re-enqueued with the same delay using the wheel's default job. It returns a key that can be passed to TimeWheel.RemoveTimer to stop the repetition, or to TimeWheel.NextFireTime to inspect the next scheduled fire time.
func (*TimeWheel[T]) AddRepeatingWithJob ¶
AddRepeatingWithJob is like TimeWheel.AddRepeating but uses a per-task job.
func (*TimeWheel[T]) AddTimer ¶
AddTimer enqueues a one-shot timer that fires after delay using the wheel's default job. It returns a key that can be passed to TimeWheel.RemoveTimer or TimeWheel.NextFireTime.
If delay is shorter than the wheel's tick interval it is rounded up to one tick.
func (*TimeWheel[T]) AddTimerFunc ¶
AddTimerFunc enqueues a one-shot timer that calls fn after delay. No payload is required; fn captures any needed state via closure. It returns a key that can be passed to TimeWheel.RemoveTimer or TimeWheel.NextFireTime.
func (*TimeWheel[T]) AddTimerWithJob ¶
AddTimerWithJob is like TimeWheel.AddTimer but uses job instead of the wheel's default job. job must not be nil.
func (*TimeWheel[T]) NextFireTime ¶
NextFireTime returns the wall-clock time at which the timer identified by key is next expected to fire, and whether the key is known to the wheel.
For repeating timers the returned time reflects the beginning of the current period (i.e. it advances after every execution).
The returned time is an estimate: it is computed when the task is enqueued and is not adjusted for scheduling jitter. The actual fire time may be up to one tick interval later than the returned value.
NextFireTime returns (zero, false) if the key does not exist — either because it was never registered, has already fired (one-shot), or was removed via TimeWheel.RemoveTimer.
func (*TimeWheel[T]) PendingTimers ¶
PendingTimers returns a snapshot of all timers currently queued in the wheel, sorted by ascending NextFireAt time.
The snapshot is taken under a read lock on the internal index, so it reflects a consistent moment in time. The returned slice is freshly allocated on every call; callers are free to modify it.
func (*TimeWheel[T]) RemoveTimer ¶
RemoveTimer cancels the timer identified by key. RemoveTimer is a no-op if the key is unknown or the timer has already fired.
func (*TimeWheel[T]) Start ¶
Start launches the event loop in a background goroutine. The wheel runs until ctx is cancelled. Call TimeWheel.Wait to block until the event loop has fully exited.
Start must be called exactly once.
func (*TimeWheel[T]) Wait ¶
func (tw *TimeWheel[T]) Wait()
Wait blocks until the event loop goroutine started by TimeWheel.Start exits.
type TimerInfo ¶
type TimerInfo struct {
// Key is the unique timer identifier returned by the Add* methods.
Key uint64
// NextFireAt is the wall-clock time at which the timer is next expected to fire.
// For repeating timers this reflects the start of the current period.
NextFireAt time.Time
// Delay is the original delay passed to the Add* method.
Delay time.Duration
// Repeating is true if the timer was registered via AddRepeating or
// AddRepeatingWithJob.
Repeating bool
}
TimerInfo describes a single live timer as returned by TimeWheel.PendingTimers.