Skip to main content

shape_runtime/simulation/
event_scheduler.rs

1//! EventQueue - Discrete Event Scheduling
2//!
3//! This module provides a priority queue for discrete events in simulation,
4//! using a min-heap to efficiently schedule and retrieve events by time.
5
6use std::cmp::{Ordering, Reverse};
7use std::collections::BinaryHeap;
8
9/// A scheduled event for discrete event simulation.
10#[derive(Debug, Clone)]
11pub struct ScheduledEvent {
12    /// Scheduled time (Unix microseconds)
13    pub time: i64,
14    /// Event type ID (user-defined)
15    pub event_type: u32,
16    /// Event payload (NaN-boxed value)
17    pub payload: u64,
18    /// Sequence number for stable ordering of same-time events
19    sequence: u64,
20}
21
22impl ScheduledEvent {
23    /// Create a new scheduled event.
24    pub fn new(time: i64, event_type: u32, payload: u64, sequence: u64) -> Self {
25        Self {
26            time,
27            event_type,
28            payload,
29            sequence,
30        }
31    }
32}
33
34// Ordering for BinaryHeap (min-heap via Reverse)
35impl PartialEq for ScheduledEvent {
36    fn eq(&self, other: &Self) -> bool {
37        self.time == other.time && self.sequence == other.sequence
38    }
39}
40
41impl Eq for ScheduledEvent {}
42
43impl PartialOrd for ScheduledEvent {
44    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
45        Some(self.cmp(other))
46    }
47}
48
49impl Ord for ScheduledEvent {
50    fn cmp(&self, other: &Self) -> Ordering {
51        // Primary: time (earlier first)
52        // Secondary: sequence (earlier scheduled first)
53        match self.time.cmp(&other.time) {
54            Ordering::Equal => self.sequence.cmp(&other.sequence),
55            ord => ord,
56        }
57    }
58}
59
60/// Priority queue for discrete events in simulation.
61///
62/// Uses a min-heap to efficiently schedule and retrieve events by time.
63/// Events at the same time are ordered by their sequence number (FIFO within same time).
64#[derive(Debug)]
65pub struct EventQueue {
66    /// Min-heap of scheduled events (Reverse for min-heap behavior)
67    heap: BinaryHeap<Reverse<ScheduledEvent>>,
68    /// Sequence counter for stable ordering
69    sequence: u64,
70}
71
72impl EventQueue {
73    /// Create a new empty event queue.
74    pub fn new() -> Self {
75        Self {
76            heap: BinaryHeap::new(),
77            sequence: 0,
78        }
79    }
80
81    /// Create with initial capacity.
82    pub fn with_capacity(capacity: usize) -> Self {
83        Self {
84            heap: BinaryHeap::with_capacity(capacity),
85            sequence: 0,
86        }
87    }
88
89    /// Schedule an event at a future time.
90    ///
91    /// # Arguments
92    /// * `time` - Scheduled time (Unix microseconds)
93    /// * `event_type` - User-defined event type ID
94    /// * `payload` - Event payload (NaN-boxed value)
95    pub fn schedule(&mut self, time: i64, event_type: u32, payload: u64) {
96        let event = ScheduledEvent::new(time, event_type, payload, self.sequence);
97        self.sequence += 1;
98        self.heap.push(Reverse(event));
99    }
100
101    /// Pop the next event that is due (scheduled_time <= current_time).
102    ///
103    /// Returns `None` if no events are due.
104    pub fn pop_due(&mut self, current_time: i64) -> Option<ScheduledEvent> {
105        if let Some(Reverse(event)) = self.heap.peek() {
106            if event.time <= current_time {
107                return self.heap.pop().map(|r| r.0);
108            }
109        }
110        None
111    }
112
113    /// Peek at the next event without removing it.
114    pub fn peek(&self) -> Option<&ScheduledEvent> {
115        self.heap.peek().map(|r| &r.0)
116    }
117
118    /// Check if any events are due.
119    pub fn has_due_events(&self, current_time: i64) -> bool {
120        self.peek().is_some_and(|e| e.time <= current_time)
121    }
122
123    /// Number of pending events.
124    pub fn len(&self) -> usize {
125        self.heap.len()
126    }
127
128    /// Check if queue is empty.
129    pub fn is_empty(&self) -> bool {
130        self.heap.is_empty()
131    }
132
133    /// Clear all pending events.
134    pub fn clear(&mut self) {
135        self.heap.clear();
136        self.sequence = 0;
137    }
138
139    /// Drain all events due at or before current_time.
140    pub fn drain_due(&mut self, current_time: i64) -> Vec<ScheduledEvent> {
141        let mut events = Vec::new();
142        while let Some(event) = self.pop_due(current_time) {
143            events.push(event);
144        }
145        events
146    }
147}
148
149impl Default for EventQueue {
150    fn default() -> Self {
151        Self::new()
152    }
153}
154
155#[cfg(test)]
156mod tests {
157    use super::*;
158
159    #[test]
160    fn test_event_queue_basic() {
161        let mut queue = EventQueue::new();
162
163        queue.schedule(1000, 1, 0);
164        queue.schedule(500, 2, 0);
165        queue.schedule(1500, 3, 0);
166
167        assert_eq!(queue.len(), 3);
168
169        // Should get event at time 500 first
170        let event = queue.pop_due(1000).unwrap();
171        assert_eq!(event.time, 500);
172        assert_eq!(event.event_type, 2);
173
174        // Then event at time 1000
175        let event = queue.pop_due(1000).unwrap();
176        assert_eq!(event.time, 1000);
177        assert_eq!(event.event_type, 1);
178
179        // Event at 1500 shouldn't be due yet
180        assert!(queue.pop_due(1000).is_none());
181
182        // But should be due at time 1500
183        let event = queue.pop_due(1500).unwrap();
184        assert_eq!(event.time, 1500);
185        assert_eq!(event.event_type, 3);
186
187        assert!(queue.is_empty());
188    }
189
190    #[test]
191    fn test_event_ordering_same_time() {
192        let mut queue = EventQueue::new();
193
194        // Schedule 3 events at the same time
195        queue.schedule(1000, 1, 0);
196        queue.schedule(1000, 2, 0);
197        queue.schedule(1000, 3, 0);
198
199        // Should process in FIFO order (by sequence number)
200        let e1 = queue.pop_due(1000).unwrap();
201        let e2 = queue.pop_due(1000).unwrap();
202        let e3 = queue.pop_due(1000).unwrap();
203
204        assert_eq!(e1.event_type, 1);
205        assert_eq!(e2.event_type, 2);
206        assert_eq!(e3.event_type, 3);
207    }
208}