Documentation
¶
Overview ¶
Package cherryTimeWheel implements a 5-level hierarchical timer wheel.
Architecture:
- 512 slots (256 near + 4×64 levels)
- Single driver goroutine, lock-free
- MPSC submission via timerCmd interface
- timerNode is owned 1:1 by its Timer handle (no object pool; a node lives as long as its handle and is reclaimed by GC)
- Dispatch runs synchronously in the driver goroutine and only fires timers; callbacks must be lightweight (business logic is pushed to actor queues by the upper layer, not executed inside the wheel)
Concurrency:
- Start/Stop are lifecycle methods and MUST be called serially from a single goroutine (the caller serializes them); they are not synchronized internally.
- submitAdd/submitRemove are safe to call from any number of goroutines concurrently.
- A *Timer handle is NOT goroutine-safe; drive it from the single goroutine that created it.
- Timer.Stop is immediate: the node's running flag is cleared atomically, so no new callback starts after Stop returns (a callback already executing is not interrupted). A remove command is still queued to reclaim the node.
- timerNode / nodeMap / wheel slots are owned by the driver goroutine and are only touched there (the atomic running flag is also written by Stop).
Cascade (bitwise, branch-free):
near (256t): current & 0xFF == 0 → cascade(0) t[0] (16Kt): current & 0x3FFF == 0 → cascade(1) t[1] (1Mt): current & 0xFFFFF == 0 → cascade(2) t[2] (67Mt): current & 0x3FFFFFF == 0 → cascade(3)
Limitations:
- Fixed 5 levels cap the maximum expressible delay at 2^32 ticks (~497 days at the 10ms default tick, ~49 days at 1ms). Longer delays wrap to a wrong slot and fire early, so keep delays within this ceiling.
- The wheel advances on a fixed ticker instead of sleeping until the next expiry, so it burns a little CPU even when idle (negligible at 10ms).
- submitAdd/submitRemove use a bounded MPSC buffer; AddTimer/RemoveTimer block under backpressure, which only happens if the driver goroutine stalls.
- Stop is terminal: once stopped, the wheel is closed and cannot be restarted (Start becomes a no-op); create a new wheel instead.
Index ¶
- Constants
- type EverySchedule
- type FixedDateSchedule
- type Scheduler
- type TimeWheel
- func (tw *TimeWheel) ActiveCount() int64
- func (tw *TimeWheel) AddOnceTimer(d time.Duration, f func()) *Timer
- func (tw *TimeWheel) AddScheduleTimer(s Scheduler, f func()) *Timer
- func (tw *TimeWheel) AddTimer(d time.Duration, f func()) *Timer
- func (tw *TimeWheel) RemoveTimer(id uint64)
- func (tw *TimeWheel) Start()
- func (tw *TimeWheel) Stop()
- type Timer
Constants ¶
const ( NEAR_SHIFT = 8 // 2^8 = 256 slots NEAR_SIZE = 1 << NEAR_SHIFT // 256 LEVEL_SHIFT = 6 // 2^6 = 64 slots per level LEVEL_SIZE = 1 << LEVEL_SHIFT // 64 NEAR_MASK = NEAR_SIZE - 1 // 0xFF LEVEL_MASK = LEVEL_SIZE - 1 // 0x3F SUBMIT_CAP = 4096 // MPSC submit channel buffer DefaultTick = 10 * time.Millisecond // minimum tick )
5-level timer wheel geometry
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type EverySchedule ¶
type FixedDateSchedule ¶
type FixedDateSchedule struct {
Hour, Minute, Second int
}
type Scheduler ¶
type Scheduler interface {
// Next returns the next execution time after the given (previous) time.
// It will return a zero time if no next time is scheduled.
//
// Times use the local clock (AddScheduleTimer seeds with time.Now()).
Next(time.Time) time.Time
}
Scheduler determines the execution plan of a task.
type TimeWheel ¶
type TimeWheel struct {
// contains filtered or unexported fields
}
TimeWheel is a 5-level hierarchical timer wheel. Wheel advance is serialized in a single driver goroutine; command submission (submitAdd/submitRemove) is concurrent from many goroutines.
func NewTimeWheel ¶
NewTimeWheel creates a timer wheel instance.
func NewTimeWheelWithHint ¶ added in v1.6.0
NewTimeWheelWithHint creates a timer wheel instance with a pre-allocated nodeMap capacity hint (recommended for million-scale workloads).
func (*TimeWheel) ActiveCount ¶ added in v1.6.0
ActiveCount returns the number of active timers.
func (*TimeWheel) AddOnceTimer ¶ added in v1.6.0
AddOnceTimer creates a one-shot timer.
func (*TimeWheel) AddScheduleTimer ¶ added in v1.6.0
AddScheduleTimer creates a timer driven by a Scheduler.
func (*TimeWheel) RemoveTimer ¶ added in v1.6.0
RemoveTimer stops the timer with the given id. Asynchronous: the removal is queued to the driver, so a callback already dispatching may still fire once. Use Timer.Stop for an immediate cancel via the handle.
type Timer ¶
type Timer struct {
// contains filtered or unexported fields
}
Timer is a user-facing handle with Start/Stop/Clear. It is NOT goroutine-safe: a Timer must be driven from a single goroutine (the owner that created it). The underlying *timerNode is owned by the wheel's driver goroutine and only mutated through the command channel.
func (*Timer) Clear ¶ added in v1.6.0
func (t *Timer) Clear()
Clear stops the timer and releases references for safe reuse.
func (*Timer) IsOnce ¶ added in v1.6.0
IsOnce reports whether the timer is one-shot (fires once, then finishes).
func (*Timer) IsRunning ¶ added in v1.6.0
IsRunning reports whether the timer is active (added/started and not yet stopped or completed).
func (*Timer) Start ¶ added in v1.6.0
func (t *Timer) Start()
Start restarts the timer, keeping its original type: a one-shot timer stays one-shot, a recurring timer stays recurring.
func (*Timer) Stop ¶
func (t *Timer) Stop()
Stop cancels the timer immediately: the running flag is cleared atomically, so no new callback starts after Stop returns. A remove command is queued to detach and reset the node promptly. A callback already executing at the moment Stop is called cannot be interrupted. Stop clears the node immediately, so a subsequent Start builds a fresh node.