1use crossbeam_deque::{Injector, Steal, Stealer, Worker};
6
7use crate::{ActorId, Message, Priority};
8
9#[derive(Debug)]
15pub struct Task {
16 pub actor_id: ActorId,
18 pub message: Message,
20 pub priority: Priority,
22 pub additional_messages: Vec<Message>,
24}
25
26pub struct WorkQueue {
28 injector: Injector<Task>,
30 worker_count: std::sync::atomic::AtomicUsize,
32}
33
34impl WorkQueue {
35 pub fn new() -> Self {
37 Self {
38 injector: Injector::new(),
39 worker_count: std::sync::atomic::AtomicUsize::new(0),
40 }
41 }
42
43 pub fn push(&self, task: Task) {
45 self.injector.push(task);
46 }
47
48 pub fn push_batch(&self, tasks: Vec<Task>) {
50 for task in tasks {
51 self.injector.push(task);
52 }
53 }
54
55 pub fn steal_global(&self) -> Option<Task> {
57 loop {
58 match self.injector.steal() {
59 Steal::Success(task) => return Some(task),
60 Steal::Empty => return None,
61 Steal::Retry => continue,
62 }
63 }
64 }
65
66 pub fn injector(&self) -> &Injector<Task> {
68 &self.injector
69 }
70
71 pub fn register_worker(&self) {
73 self.worker_count
74 .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
75 }
76
77 pub fn unregister_worker(&self) {
79 self.worker_count
80 .fetch_sub(1, std::sync::atomic::Ordering::Relaxed);
81 }
82}
83
84impl Default for WorkQueue {
85 fn default() -> Self {
86 Self::new()
87 }
88}
89
90pub fn create_local_queue() -> (Worker<Task>, Stealer<Task>) {
92 let worker = Worker::new_fifo();
93 let stealer = worker.stealer();
94 (worker, stealer)
95}
96
97pub struct WorkStealer {
99 stealers: Vec<Stealer<Task>>,
101 index: std::sync::atomic::AtomicUsize,
103}
104
105impl WorkStealer {
106 pub fn new(stealers: Vec<Stealer<Task>>) -> Self {
108 Self {
109 stealers,
110 index: std::sync::atomic::AtomicUsize::new(0),
111 }
112 }
113
114 pub fn steal(&self) -> Option<Task> {
116 if self.stealers.is_empty() {
117 return None;
118 }
119
120 let start = self
121 .index
122 .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
123 let len = self.stealers.len();
124
125 for i in 0..len {
126 let idx = (start + i) % len;
127 loop {
128 match self.stealers[idx].steal() {
129 Steal::Success(task) => {
130 return Some(task);
131 }
132 Steal::Empty => break,
133 Steal::Retry => continue,
134 }
135 }
136 }
137 None
138 }
139
140 pub fn steal_batch(&self, dest: &Worker<Task>, max: usize) -> usize {
142 if self.stealers.is_empty() {
143 return 0;
144 }
145
146 let start = self
147 .index
148 .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
149 let len = self.stealers.len();
150 let mut stolen = 0;
151
152 for i in 0..len {
153 if stolen >= max {
154 break;
155 }
156 let idx = (start + i) % len;
157 loop {
158 match self.stealers[idx].steal_batch_and_pop(dest) {
159 Steal::Success(task) => {
160 dest.push(task);
161 stolen += 1;
162 }
163 Steal::Empty => break,
164 Steal::Retry => continue,
165 }
166 }
167 }
168 stolen
169 }
170}
171
172pub struct PriorityQueue {
174 critical: Injector<Task>,
176 high: Injector<Task>,
178 normal: Injector<Task>,
180 low: Injector<Task>,
182}
183
184impl PriorityQueue {
185 pub fn new() -> Self {
187 Self {
188 critical: Injector::new(),
189 high: Injector::new(),
190 normal: Injector::new(),
191 low: Injector::new(),
192 }
193 }
194
195 pub fn push(&self, task: Task) {
197 match task.priority {
198 Priority::Critical => self.critical.push(task),
199 Priority::High => self.high.push(task),
200 Priority::Normal => self.normal.push(task),
201 Priority::Low => self.low.push(task),
202 }
203 }
204
205 pub fn pop(&self) -> Option<Task> {
207 fn try_steal(injector: &Injector<Task>) -> Option<Task> {
208 loop {
209 match injector.steal() {
210 Steal::Success(task) => return Some(task),
211 Steal::Empty => return None,
212 Steal::Retry => continue,
213 }
214 }
215 }
216
217 try_steal(&self.critical)
218 .or_else(|| try_steal(&self.high))
219 .or_else(|| try_steal(&self.normal))
220 .or_else(|| try_steal(&self.low))
221 }
222}
223
224impl Default for PriorityQueue {
225 fn default() -> Self {
226 Self::new()
227 }
228}
229
230#[cfg(test)]
231mod tests {
232 use super::*;
233 use crate::MessagePayload;
234
235 #[test]
236 fn test_work_queue_basic() {
237 let queue = WorkQueue::new();
238 let task = Task {
239 actor_id: ActorId::new(),
240 message: Message {
241 sender: None,
242 payload: MessagePayload::Start,
243 priority: Priority::Normal,
244 },
245 priority: Priority::Normal,
246 additional_messages: Vec::new(),
247 };
248
249 queue.push(task);
250 let stolen = queue.steal_global();
251 assert!(stolen.is_some());
252 }
253
254 #[test]
255 fn test_local_queue() {
256 let (worker, _stealer) = create_local_queue();
257 let task = Task {
258 actor_id: ActorId::new(),
259 message: Message {
260 sender: None,
261 payload: MessagePayload::Start,
262 priority: Priority::Normal,
263 },
264 priority: Priority::Normal,
265 additional_messages: Vec::new(),
266 };
267
268 worker.push(task);
269 assert_eq!(worker.len(), 1);
270 let popped = worker.pop();
271 assert!(popped.is_some());
272 assert!(worker.is_empty());
273 }
274
275 #[test]
276 fn test_priority_queue() {
277 let queue = PriorityQueue::new();
278
279 let low_task = Task {
280 actor_id: ActorId::new(),
281 message: Message {
282 sender: None,
283 payload: MessagePayload::Start,
284 priority: Priority::Low,
285 },
286 priority: Priority::Low,
287 additional_messages: Vec::new(),
288 };
289 let high_task = Task {
290 actor_id: ActorId::new(),
291 message: Message {
292 sender: None,
293 payload: MessagePayload::Start,
294 priority: Priority::High,
295 },
296 priority: Priority::High,
297 additional_messages: Vec::new(),
298 };
299
300 queue.push(low_task);
301 queue.push(high_task);
302
303 let first = queue.pop().unwrap();
304 assert_eq!(first.priority, Priority::High);
305
306 let second = queue.pop().unwrap();
307 assert_eq!(second.priority, Priority::Low);
308 }
309}