cherryTimeWheel

package
v1.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 17, 2026 License: MIT Imports: 5 Imported by: 1

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

View Source
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 EverySchedule struct {
	Interval time.Duration
}

func (*EverySchedule) Next

func (s *EverySchedule) Next(prev time.Time) time.Time

type FixedDateSchedule

type FixedDateSchedule struct {
	Hour, Minute, Second int
}

func (*FixedDateSchedule) Next

func (s *FixedDateSchedule) Next(prev time.Time) time.Time

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

func NewTimeWheel(tick time.Duration) *TimeWheel

NewTimeWheel creates a timer wheel instance.

func NewTimeWheelWithHint added in v1.6.0

func NewTimeWheelWithHint(tick time.Duration, hint int) *TimeWheel

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

func (tw *TimeWheel) ActiveCount() int64

ActiveCount returns the number of active timers.

func (*TimeWheel) AddOnceTimer added in v1.6.0

func (tw *TimeWheel) AddOnceTimer(d time.Duration, f func()) *Timer

AddOnceTimer creates a one-shot timer.

func (*TimeWheel) AddScheduleTimer added in v1.6.0

func (tw *TimeWheel) AddScheduleTimer(s Scheduler, f func()) *Timer

AddScheduleTimer creates a timer driven by a Scheduler.

func (*TimeWheel) AddTimer added in v1.6.0

func (tw *TimeWheel) AddTimer(d time.Duration, f func()) *Timer

AddTimer creates a recurring timer and starts it.

func (*TimeWheel) RemoveTimer added in v1.6.0

func (tw *TimeWheel) RemoveTimer(id uint64)

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.

func (*TimeWheel) Start

func (tw *TimeWheel) Start()

Start begins the driver goroutine. Idempotent. Must not be called concurrently with Stop (lifecycle is serialized by the caller).

func (*TimeWheel) Stop

func (tw *TimeWheel) Stop()

Stop shuts down the driver goroutine. Idempotent and terminal: once stopped the wheel is closed and cannot be restarted. Must not be called concurrently with Start (lifecycle is serialized by the caller).

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) ID

func (t *Timer) ID() uint64

ID returns the timer's unique ID.

func (*Timer) IsOnce added in v1.6.0

func (t *Timer) IsOnce() bool

IsOnce reports whether the timer is one-shot (fires once, then finishes).

func (*Timer) IsRunning added in v1.6.0

func (t *Timer) IsRunning() bool

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL