reovim_kernel/sched/priority.rs
1//! Priority queue for ordered event processing.
2//!
3//! Linux equivalent: Priority scheduling in `kernel/sched/`
4//!
5//! This module provides a priority-ordered queue for events. Events with
6//! lower priority values are processed first. Events with the same priority
7//! are processed in FIFO order.
8//!
9//! # Example
10//!
11//! ```ignore
12//! use reovim_kernel::sched::PriorityQueue;
13//! use reovim_kernel::ipc::{DynEvent, Event};
14//!
15//! #[derive(Debug)]
16//! struct HighPriorityEvent;
17//! impl Event for HighPriorityEvent {
18//! fn priority(&self) -> u32 { 0 }
19//! }
20//!
21//! #[derive(Debug)]
22//! struct LowPriorityEvent;
23//! impl Event for LowPriorityEvent {
24//! fn priority(&self) -> u32 { 200 }
25//! }
26//!
27//! let queue = PriorityQueue::new();
28//!
29//! // Add events in reverse priority order
30//! queue.push(DynEvent::new(LowPriorityEvent));
31//! queue.push(DynEvent::new(HighPriorityEvent));
32//!
33//! // Pop returns highest priority (lowest value) first
34//! let event = queue.pop().unwrap();
35//! assert_eq!(event.priority(), 0); // HighPriorityEvent
36//! ```
37
38use std::{
39 cmp::Ordering,
40 collections::BinaryHeap,
41 sync::atomic::{AtomicU64, Ordering as AtomicOrdering},
42};
43
44use crate::{arch::sync::Mutex, ipc::DynEvent};
45
46/// Default capacity for priority queue.
47pub const DEFAULT_CAPACITY: usize = 256;
48
49/// Entry in the priority queue.
50///
51/// Wraps a `DynEvent` with ordering metadata for the binary heap.
52struct PriorityEntry {
53 /// Event priority (from `Event::priority()`).
54 priority: u32,
55
56 /// Insertion sequence for FIFO ordering within same priority.
57 sequence: u64,
58
59 /// The event.
60 event: DynEvent,
61}
62
63// LLVM coverage artifact: closing brace of PartialEq impl marked DA:0;
64// PriorityEntry equality is only used internally by BinaryHeap ordering.
65#[cfg_attr(coverage_nightly, coverage(off))]
66impl PartialEq for PriorityEntry {
67 fn eq(&self, other: &Self) -> bool {
68 self.priority == other.priority && self.sequence == other.sequence
69 }
70}
71
72impl Eq for PriorityEntry {}
73
74impl PartialOrd for PriorityEntry {
75 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
76 Some(self.cmp(other))
77 }
78}
79
80impl Ord for PriorityEntry {
81 fn cmp(&self, other: &Self) -> Ordering {
82 // BinaryHeap is a max-heap, so we reverse the ordering:
83 // - Lower priority value = higher precedence (comes first)
84 // - For same priority, lower sequence = earlier (FIFO)
85 //
86 // We want: smaller priority → "greater" in heap terms
87 // We want: smaller sequence → "greater" in heap terms (for same priority)
88 match other.priority.cmp(&self.priority) {
89 Ordering::Equal => other.sequence.cmp(&self.sequence),
90 ord => ord,
91 }
92 }
93}
94
95/// Priority queue for events.
96///
97/// Maintains events in priority order with FIFO ordering for events
98/// of the same priority. Thread-safe via internal mutex.
99///
100/// # Ordering
101///
102/// Events are ordered by:
103/// 1. Priority (lower value = higher precedence)
104/// 2. Insertion order (earlier = higher precedence)
105///
106/// This ensures that high-priority events (like user input) are always
107/// processed before low-priority events (like render signals).
108pub struct PriorityQueue {
109 /// Heap storage.
110 heap: Mutex<BinaryHeap<PriorityEntry>>,
111
112 /// Sequence counter for FIFO ordering.
113 sequence: AtomicU64,
114
115 /// Maximum capacity (0 = unlimited).
116 capacity: usize,
117}
118
119impl PriorityQueue {
120 /// Create a new priority queue with default capacity.
121 #[must_use]
122 pub fn new() -> Self {
123 Self::with_capacity(DEFAULT_CAPACITY)
124 }
125
126 /// Create a priority queue with specified capacity.
127 ///
128 /// A capacity of 0 means unlimited (not recommended).
129 #[must_use]
130 pub fn with_capacity(capacity: usize) -> Self {
131 Self {
132 heap: Mutex::new(BinaryHeap::with_capacity(capacity.min(4096))),
133 sequence: AtomicU64::new(0),
134 capacity,
135 }
136 }
137
138 /// Push an event onto the queue.
139 ///
140 /// Returns `false` if capacity is exceeded (event is dropped).
141 #[cfg_attr(coverage_nightly, coverage(off))]
142 pub fn push(&self, event: DynEvent) -> bool {
143 let mut heap = self.heap.lock();
144
145 if self.capacity > 0 && heap.len() >= self.capacity {
146 return false;
147 }
148
149 let entry = PriorityEntry {
150 priority: event.priority(),
151 sequence: self.sequence.fetch_add(1, AtomicOrdering::Relaxed),
152 event,
153 };
154 heap.push(entry);
155 true
156 }
157
158 /// Pop the highest priority event.
159 ///
160 /// Returns `None` if the queue is empty.
161 #[must_use]
162 pub fn pop(&self) -> Option<DynEvent> {
163 self.heap.lock().pop().map(|e| e.event)
164 }
165
166 /// Peek at the priority of the next event without removing it.
167 ///
168 /// Returns `None` if the queue is empty.
169 #[must_use]
170 pub fn peek_priority(&self) -> Option<u32> {
171 self.heap.lock().peek().map(|e| e.priority)
172 }
173
174 /// Get the number of events in the queue.
175 #[must_use]
176 pub fn len(&self) -> usize {
177 self.heap.lock().len()
178 }
179
180 /// Check if the queue is empty.
181 #[must_use]
182 pub fn is_empty(&self) -> bool {
183 self.heap.lock().is_empty()
184 }
185
186 /// Get the queue capacity.
187 #[inline]
188 #[must_use]
189 pub const fn capacity(&self) -> usize {
190 self.capacity
191 }
192
193 /// Clear all events from the queue.
194 pub fn clear(&self) {
195 self.heap.lock().clear();
196 }
197
198 /// Drain all events from the queue in priority order.
199 ///
200 /// Returns events sorted by priority (highest first).
201 #[must_use]
202 pub fn drain(&self) -> Vec<DynEvent> {
203 let mut heap = self.heap.lock();
204 let mut result = Vec::with_capacity(heap.len());
205 while let Some(entry) = heap.pop() {
206 result.push(entry.event);
207 }
208 result
209 }
210}
211
212impl Default for PriorityQueue {
213 fn default() -> Self {
214 Self::new()
215 }
216}
217
218impl std::fmt::Debug for PriorityQueue {
219 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
220 f.debug_struct("PriorityQueue")
221 .field("len", &self.len())
222 .field("capacity", &self.capacity)
223 .field("peek_priority", &self.peek_priority())
224 .finish_non_exhaustive()
225 }
226}