TimerEvent System

Relevant source files

This document covers the timer event system that defines how callbacks are executed when timers expire in the timer_list crate. It focuses on the TimerEvent trait and its implementations, particularly the TimerEventFn wrapper for closure-based events.

For information about the underlying timer storage and scheduling mechanisms, see TimerList Data Structure.

Purpose and Scope

The TimerEvent system provides the interface and implementations for executable timer callbacks in the timer_list crate. It defines how events are structured and executed when their deadlines are reached, supporting both custom event types and simple closure-based events.

TimerEvent Trait

The TimerEvent trait serves as the core interface that all timer events must implement. It defines a single callback method that consumes the event when executed.

Trait Definition

The trait is defined with a simple interface at src/lib.rs(L15 - L19) :

#![allow(unused)]
fn main() {
pub trait TimerEvent {
    fn callback(self, now: TimeValue);
}
}

Key Characteristics

AspectDescription
ConsumptionThecallbackmethod takesselfby value, consuming the event
Time ParameterReceives the current time asTimeValue(alias forDuration)
Return TypeReturns()- events perform side effects rather than return values
Execution ModelCalled exactly once when the timer expires

The trait design ensures that events cannot be accidentally reused after execution, providing memory safety and preventing common timer-related bugs.

TimerEvent Trait Architecture

classDiagram
class TimerEvent {
    <<trait>>
    
    +callback(self, now: TimeValue)
}

class TimerEventFn {
    
    -Box~dyn FnOnce(TimeValue) ~
    +new(f: F) TimerEventFn
    +callback(self, now: TimeValue)
}

class CustomEvent {
    <<user-defined>>
    
    +callback(self, now: TimeValue)
}

TimerEvent  ..|>  TimerEventFn : implements
TimerEvent  ..|>  CustomEvent : implements

Sources: src/lib.rs(L15 - L19)  src/lib.rs(L108 - L127) 

TimerEventFn Implementation

The TimerEventFn struct provides a convenient wrapper that allows closures to be used as timer events without requiring custom trait implementations.

Structure and Construction

The TimerEventFn wraps a boxed closure at src/lib.rs(L111) :

pub struct TimerEventFn(Box<dyn FnOnce(TimeValue) + 'static>);

Construction is handled by the new method at src/lib.rs(L114 - L121) :

ParameterTypeDescription
fF: FnOnce(TimeValue) + 'staticClosure to execute when timer expires
ReturnTimerEventFnWrapped timer event ready for scheduling

TimerEvent Implementation

The trait implementation at src/lib.rs(L123 - L127)  simply extracts and calls the wrapped closure:

#![allow(unused)]
fn main() {
impl TimerEvent for TimerEventFn {
    fn callback(self, now: TimeValue) {
        (self.0)(now)
    }
}
}

This design enables functional programming patterns while maintaining the trait's consumption semantics.

TimerEventFn Data Flow

flowchart TD
A["closure: FnOnce(TimeValue)"]
B["TimerEventFn::new(closure)"]
C["TimerEventFn(Box::new(closure))"]
D["TimerList::set(deadline, event)"]
E["TimerEventWrapper { deadline, event }"]
F["BinaryHeap::push(wrapper)"]
G["TimerList::expire_one(now)"]
H["BinaryHeap::pop()"]
I["event.callback(now)"]
J["(closure)(now)"]

A --> B
B --> C
C --> D
D --> E
E --> F
G --> H
H --> I
I --> J

Sources: src/lib.rs(L108 - L127)  src/lib.rs(L69 - L71) 

Event Lifecycle Integration

Timer events integrate with the TimerList through a well-defined lifecycle that ensures proper execution timing and resource management.

Event Scheduling Process

Events are scheduled through the TimerList::set method, which wraps them in TimerEventWrapper structures for heap management:

StepComponentAction
1Client CodeCreates event implementingTimerEvent
2TimerList::setWraps event inTimerEventWrapperwith deadline
3BinaryHeapStores wrapper using min-heap ordering
4TimerList::expire_oneChecks earliest deadline against current time
5Event CallbackExecutesevent.callback(now)if expired

Execution Semantics

When events expire, they follow a strict execution pattern defined at src/lib.rs(L92 - L99) :

  • Events are processed one at a time in deadline order
  • Only events with deadline <= now are eligible for execution
  • Events are removed from the heap before callback execution
  • Callbacks receive the actual current time, not the original deadline

Event Execution Flow

sequenceDiagram
    participant ClientCode as "Client Code"
    participant TimerList as "TimerList"
    participant BinaryHeapTimerEventWrapper as "BinaryHeap<TimerEventWrapper>"
    participant TimerEventcallback as "TimerEvent::callback"

    Note over ClientCode,TimerEventcallback: Event Scheduling
    ClientCode ->> TimerList: set(deadline, event)
    TimerList ->> BinaryHeapTimerEventWrapper: push(TimerEventWrapper)
    Note over ClientCode,TimerEventcallback: Event Processing
    loop Processing Loop
        ClientCode ->> TimerList: expire_one(now)
        TimerList ->> BinaryHeapTimerEventWrapper: peek()
    alt deadline <= now
        BinaryHeapTimerEventWrapper -->> TimerList: earliest event
        TimerList ->> BinaryHeapTimerEventWrapper: pop()
        TimerList ->> TimerEventcallback: callback(now)
        TimerEventcallback -->> ClientCode: side effects
        TimerList -->> ClientCode: Some((deadline, event))
    else deadline > now
        TimerList -->> ClientCode: None
    end
    end

Sources: src/lib.rs(L69 - L71)  src/lib.rs(L92 - L99)  src/lib.rs(L21 - L24) 

Custom Event Implementation

While TimerEventFn handles simple closure cases, custom event types can implement TimerEvent directly for more complex scenarios. The test suite demonstrates this pattern at src/lib.rs(L140 - L153) :

Example Pattern

#![allow(unused)]
fn main() {
struct TestTimerEvent(usize, TimeValue);

impl TimerEvent for TestTimerEvent {
    fn callback(self, now: TimeValue) {
        // Custom logic with access to event data
        println!("Event {} executed at {:?}", self.0, now);
    }
}
}

This approach allows events to carry additional data and implement complex callback logic while maintaining the same execution guarantees as TimerEventFn.

Code Entity Mapping

flowchart TD
subgraph subGraph2["Test Examples"]
    H["TestTimerEvent[src/lib.rs:140-153]"]
    I["test_timer_list_fn[src/lib.rs:183-206]"]
end
subgraph subGraph1["Integration Points"]
    E["TimerList::set()[src/lib.rs:69-71]"]
    F["TimerList::expire_one()[src/lib.rs:92-99]"]
    G["TimerEventWrapper[src/lib.rs:21-24]"]
end
subgraph subGraph0["TimerEvent System API"]
    A["TimerEvent trait[src/lib.rs:15-19]"]
    B["TimerEventFn struct[src/lib.rs:111]"]
    C["TimerEventFn::new()[src/lib.rs:114-121]"]
    D["TimeValue type[src/lib.rs:10-13]"]
end

A --> B
A --> D
A --> H
B --> C
B --> I
E --> G
F --> A

Sources: src/lib.rs(L15 - L19)  src/lib.rs(L108 - L127)  src/lib.rs(L69 - L71)  src/lib.rs(L92 - L99)  src/lib.rs(L140 - L153)  src/lib.rs(L183 - L206)