Kestrel Timer
High-performance async timer library based on Hierarchical Timing Wheel algorithm
📚 Table of Contents
- Overview
- Key Features
- Quick Start
- Architecture
- Usage Examples
- Configuration
- Benchmarks
- Use Cases
- License
Overview
kestrel-timer is a high-performance async timer library based on the Hierarchical Timing Wheel algorithm, designed for Rust and Tokio runtime. It provides O(1) time complexity for timer operations and easily handles 10,000+ concurrent timers.
Core Advantages:
- Hierarchical timing wheel architecture that automatically separates short-delay and long-delay tasks
- 2-12x performance improvement over traditional heap-based implementations
- Support for timer postponement, batch operations, and completion notifications
- Production-ready with comprehensive testing
Key Features
🏗️ Hierarchical Timing Wheel
- Dual-layer design: L0 layer (high-precision short-delay) + L1 layer (long-delay) with automatic layering
- Smart demotion: L1 tasks automatically demote to L0 layer when due
- No round checks: L0 layer eliminates rounds checking for 90% of tasks
⚡ High Performance
- O(1) time complexity: Insert, delete, and trigger operations are all O(1)
- Optimized data structures:
FxHashMap+parking_lot::Mutex - Bitwise optimization: Slot count is power of 2 for fast modulo operations
- Large-scale support: Easily handles 10,000+ concurrent timers
🔄 Complete Features
- ✅ Async callback support (based on Tokio)
- ✅ Timer postponement (keep or replace callback)
- ✅ Batch operations (schedule, cancel, postpone)
- ✅ Completion notification mechanism
- ✅ TimerService (Actor-based management)
- ✅ Thread-safe
Quick Start
Installation
Add to your Cargo.toml:
[]
= "0.2.0"
= { = "1.48", = ["full"] }
Basic Usage
use ;
use Duration;
async
Batch Operations
use ;
use Duration;
let timer = with_defaults;
// Batch create + register
let tasks: =
.map
.collect;
let batch_handle = timer.register_batch;
// Batch cancel
batch_handle.cancel_all;
Postpone Timer
use ;
use Duration;
let timer = with_defaults;
let callback = Some;
let task = new_oneshot;
let task_id = task.get_id;
let handle = timer.register;
// Postpone and keep original callback
timer.postpone;
// Postpone and replace callback
let new_callback = Some;
timer.postpone;
TimerService Usage
use ;
use ServiceConfig;
use Duration;
let timer = with_defaults;
let mut service = timer.create_service;
// Batch schedule
let tasks: = vec!;
service.register_batch.unwrap;
// Receive timeout notifications
let mut timeout_rx = service.take_receiver.unwrap;
while let Some = timeout_rx.recv.await
service.shutdown.await;
Architecture
Hierarchical Timing Wheel Design
┌─────────────────────────────────────────────┐
│ L1 Layer (Upper) │
│ Slots: 64 | Tick: 1s | Range: 64s │
│ ↓ Demote to L0 │
└─────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────┐
│ L0 Layer (Lower) │
│ Slots: 512 | Tick: 10ms | Range: 5.12s │
│ ▲ Current Pointer │
└─────────────────────────────────────────────┘
L0 Layer (Lower - High Precision):
- Slots: 512 (default), Tick: 10ms
- Coverage: 5.12 seconds
- Handles 80-90% of short-delay tasks
L1 Layer (Upper - Long Duration):
- Slots: 64 (default), Tick: 1000ms
- Coverage: 64 seconds
- Handles long-delay tasks with rounds mechanism
Workflow:
- Short delay (< 5.12s) → Insert directly into L0 layer
- Long delay (≥ 5.12s) → Insert into L1 layer
- L1 task due → Automatically demote to L0 layer
- L0 task due → Trigger immediately
Performance Optimizations
- Hierarchical architecture: Avoids single-layer round checks, L0 layer requires no rounds checking
- Efficient locking:
parking_lot::Mutexis faster than standard Mutex - Bitwise optimization: Slot count is power of 2, uses
& (n-1)for fast modulo - Cache optimization: Pre-compute slot masks, tick durations, and other frequently used values
- Batch optimization: Reduce lock contention with smart small-batch handling
Usage Examples
Full API documentation at docs.rs/kestrel-timer
Main APIs
TimerTask:
TimerTask::new_oneshot(delay, callback)- Create one-shot taskTimerTask::new_periodic(initial_delay, interval, callback, buffer_size)- Create periodic taskget_id()- Get task IDget_task_type()- Get task typeget_interval()- Get interval for periodic tasks
TimerWheel:
TimerWheel::with_defaults()- Create with default configTimerWheel::new(config)- Create with custom configregister(task)- Register taskregister_batch(tasks)- Batch registercancel(task_id)- Cancel taskcancel_batch(task_ids)- Batch cancelpostpone(task_id, delay, callback)- Postpone taskpostpone_batch(updates)- Batch postpone
TimerHandle:
cancel()- Cancel timertask_id()- Get task IDinto_parts()- Get completion receiver and handle
TimerService:
register(task)- Register taskregister_batch(tasks)- Batch registertake_receiver()- Get timeout notification receivercancel_task(task_id)- Cancel taskcancel_batch(task_ids)- Batch cancelpostpone(task_id, delay, callback)- Postpone taskpostpone_batch(updates)- Batch postponeshutdown()- Shutdown service
Configuration
Default Configuration
let timer = with_defaults;
// L0: 512 slots × 10ms = 5.12 seconds
// L1: 64 slots × 1s = 64 seconds
Custom Configuration
use WheelConfig;
let config = builder
.l0_tick_duration // L0 tick
.l0_slot_count // L0 slots (must be power of 2)
.l1_tick_duration // L1 tick
.l1_slot_count // L1 slots (must be power of 2)
.build?;
let timer = new;
Recommended Configurations
High Precision (Network Timeouts):
let config = builder
.l0_tick_duration
.l0_slot_count
.l1_tick_duration
.l1_slot_count
.build?;
Low Precision (Heartbeat Detection):
let config = builder
.l0_tick_duration
.l0_slot_count
.l1_tick_duration
.l1_slot_count
.build?;
Benchmarks
Run Benchmarks
Performance Comparison
Compared to traditional heap-based (BinaryHeap) timer implementations:
| Operation | Hierarchical Wheel | Heap Implementation | Advantage |
|---|---|---|---|
| Insert Single | O(1) ~5μs | O(log n) ~10-20μs | 2-4x faster |
| Batch Insert 1000 | ~2ms | ~15-25ms | 7-12x faster |
| Cancel Task | O(1) ~2μs | O(n) ~50-100μs | 25-50x faster |
| Postpone Task | O(1) ~4μs | O(log n) ~15-30μs | 4-7x faster |
Large Scale Testing
- ✅ 10,000 concurrent timers
- ✅ Creation time < 100ms
- ✅ All timers fire correctly
Use Cases
1. Network Timeout Management
use ;
use Duration;
async
2. Heartbeat Detection
use ;
use ServiceConfig;
use Duration;
let timer = with_defaults;
let mut service = timer.create_service;
for client_id in client_ids
3. Cache Expiration
use ;
use Arc;
use Duration;
async
4. Game Buff System
use ;
use Duration;
async
// Extend buff duration
timer.postpone;
5. Retry Mechanism
use ;
use Duration;
async
License
This project is licensed under either of:
- MIT License (LICENSE-MIT or http://opensource.org/licenses/MIT)
- Apache License 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
Acknowledgments
The timing wheel algorithm was first proposed by George Varghese and Tony Lauck in the paper "Hashed and Hierarchical Timing Wheels" (SOSP '87).
Full Documentation: docs.rs/kestrel-timer