Ratas Hierarchical Timer Wheel

repository·master·Indexed 42 days ago

https://github.com/jsnell/ratas

A high-performance hierarchical timer wheel implementation in C++11 designed for high occupancy rates and efficient event rescheduling and cancellation. It provides the TimerWheel class for managing scheduled events, TimerEvent for callback-based triggers, and MemberTimerEvent for tying events to class member functions.

Tokens
2.2K
Snippets
5
Records
13
Agent score
3%

What's inside Ratas

  1. How the hierarchical timer wheel works

    master

    Ratas uses a hierarchical timer wheel to manage events scheduled for the future. A timer wheel is a ring buffer of linked lists of events. As time advances, a pointer moves through the buffer, executing events in the slots it passes.

    To handle long-term scheduling, the implementation layers multiple wheels at different resolutions. When an event is scheduled too far in the future for the innermost (core) wheel, it is placed in an outer wheel. As the inner wheels rotate, events are 'promoted' from outer wheels to inner wheels until they reach the core wheel and are ready for execution.

  2. How Ratas hierarchical timer wheels work

    master

    Ratas uses a hierarchical timer wheel implementation to manage scheduled events. A timer wheel is a ring buffer of linked lists of events. As time advances, a pointer moves through the buffer, executing events in the slots it passes.

    A hierarchical wheel layers multiple wheels at different resolutions.

    • Core Wheel: The innermost wheel handles high-resolution (immediate) events.
    • Outer Wheels: Events scheduled too far in the future for the core wheel are placed in outer wheels.
    • Promotion: On each rotation of an inner wheel, one slot's worth of events is promoted to the next wheel up (e.g., from the second wheel to the core). This continues up the hierarchy.
  3. Query TimerWheel state

    master

    Use these methods to inspect the state of the TimerWheel:

    • now(): Returns the current tick value. Note that during a callback, this returns the tick the event was scheduled for.
    • ticks_to_next_event(Tick max = ..., int level = 0): Returns the number of ticks until the next event. If max is provided, it returns the smaller of the remaining ticks or max. If no events are scheduled, it returns max. Returns 0 if there are unprocessed events from a previous advance() call that hit a max_execute limit.
  4. Check time until next event with TimerWheel::ticks_to_next_event()

    master

    Use ticks_to_next_event(max, level) to determine how many ticks remain until the next event is scheduled to execute.

    • max: The maximum tick value to return. If no events are scheduled, it returns max.
    • level: The hierarchy level to start searching from (defaults to 0).
    • If advance() returned false in a previous call (meaning there is a backlog of unprocessed events), this returns 0.
  5. Use MemberTimerEvent to tie events to class members

    master

    If you want an event to invoke a specific member function of a class instance rather than a standalone callback, use MemberTimerEvent. This is useful for integrating timers directly into object lifecycles.

    template<typename T, void(T::*MFun)() >
    class MemberTimerEvent : public TimerEventInterface;
    
    // Usage:
    class Test {
      public:
        Test() : inc_timer_(this) {}
        void start(TimerWheel* timers) {
            timers->schedule(&inc_timer_, 10);
        }
        void on_inc() { count_++; }
      private:
        MemberTimerEvent<Test, &Test::on_inc> inc_timer_;
        int count_ = 0;
    };
    class Test {
      public:
          Test() : inc_timer_(this) {
          }
          void start(TimerWheel* timers) {
              timers->schedule(&inc_timer_, 10);
          }
          void on_inc() {
              count_++;
          }
          int count() { return count_; }
      private:
          MemberTimerEvent<Test, &Test::on_inc> inc_timer_;
          int count_ = 0;
      };
  6. Manage event lifecycle with TimerEventInterface

    master

    All events implement TimerEventInterface.

    • Automatic Cancellation: Events are automatically canceled when they are destroyed.
    • cancel(): Unschedules the event. It is safe to call this even if the event is already inactive.
    • active(): Returns true if the event is currently scheduled.
    • scheduled_at(): Returns the absolute tick value at which the event is set to execute.
  7. Cancel events with TimerEventInterface::cancel()

    master

    To prevent a scheduled event from executing, call the cancel() method on the event object. It is safe to call cancel() on an event that is not currently scheduled (inactive).

    Note: TimerEvent objects automatically call cancel() upon destruction, ensuring no dangling scheduled events.

  8. Schedule events with TimerWheel::schedule()

    master

    Use schedule() to register a TimerEvent or MemberTimerEvent to be executed a specific number of ticks from the current time. The delta (number of ticks) must be greater than 0.

    // Example: Scheduling a callback
    typedef std::function<void()> Callback;
    TimerWheel timers;
    Callback my_cb = []() { /* do something */ };
    TimerEvent<Callback> timer(my_cb);
    
    // Schedule to run 5 ticks from now
    timers.schedule(&timer, 5);
    typedef std::function<void()> Callback;
    TimerWheel timers;
    int count = 0;
    TimerEvent<Callback> timer([&count] () { ++count; });
    
    timers.schedule(&timer, 5);
    timers.advance(4);
    assert(count == 0);
    timers.advance(1);
    assert(count == 1);
  9. Schedule events in a range with TimerWheel::schedule_in_range()

    master

    Use schedule_in_range() to schedule an event to occur within a specific window of ticks from the current time. The TimerWheel will choose a specific time within the [start, end] range to minimize the overhead of rescheduling or promoting the event through the hierarchy.

    Constraints:

    • start and end must be non-zero.
    • end must be greater than start.
  10. Schedule and manage events with TimerWheel

    master

    The TimerWheel is the central entity used to schedule events and advance time.

    Scheduling

    • Use schedule(TimerEventInterface* event, Tick delta) to schedule an event to run a specific number of delta ticks from now.
    • Use schedule_in_range(TimerEventInterface* event, Tick start, Tick end) to schedule an event within a range. The wheel will choose the optimal time within that range to minimize overhead.

    Advancing Time

    • Use advance(Tick delta, size_t max_execute = ..., int level = 0) to move the clock forward. This triggers the execution of scheduled events.
    • Throttling: You can use max_execute to limit how many events run in a single advance() call. This allows you to interleave timer processing with your application logic. If the limit is reached, advance() returns false, and you should call advance() again to process the remaining events.

    Important Rules

    • Do not call advance() from within an event callback.
    • It is safe to call cancel() or schedule() from within an event callback.
    • During a callback, now() returns the tick the event was scheduled for, not the current advanced time.
    TimerWheel timers;
    TimerEvent<Callback> timer([]() { /* callback */ });
    
    // Schedule for 5 ticks from now
    timers.schedule(&timer, 5);
    
    // Advance time by 5 ticks
    timers.advance(5);
  11. Advance time with TimerWheel::advance()

    master

    The advance(delta, max_execute) method moves the timer wheel forward by a specified number of ticks and executes any events scheduled for those ticks.

    Key behaviors:

    • Interleaving: You can limit the number of events executed in a single call using max_execute. If the limit is reached, the function returns false, and remaining events will be processed on the next call to advance().
    • Safety: It is safe to cancel or schedule new events from within an event's callback. However, you must not call advance() from within an event callback.
    • Ordering: Events are executed in order; all events for tick $X$ run before events for tick $X+1$.
    • Observability: During a callback, now() returns the tick the event was scheduled for, not the current advanced time.
  12. Use TimerEvent for callback-based events

    master

    TimerEvent<CBType> is a concrete implementation of TimerEventInterface that takes a callback function of type CBType as a constructor parameter. When the timer triggers, the provided callback is executed.

    typedef std::function<void()> Callback;
    TimerEvent<Callback> timer([]() {
        // Your logic here
    });