1use crossbeam_queue::ArrayQueue;
8#[cfg(loom)]
15use loom::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
16#[cfg(not(loom))]
17use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
18use std::sync::Arc;
19use tokio::sync::{Notify, Semaphore};
20
21use crate::Error;
22use crate::{ActorId, Message, Priority};
23
24#[derive(Debug, Clone)]
26pub struct MailboxConfig {
27 pub capacity: usize,
29 pub priority_queue: bool,
31 pub backpressure_threshold: f32,
33}
34
35impl Default for MailboxConfig {
36 fn default() -> Self {
37 Self {
38 capacity: 10_000,
39 priority_queue: true,
40 backpressure_threshold: 0.8,
41 }
42 }
43}
44
45impl MailboxConfig {
46 pub fn new(capacity: usize) -> Self {
48 Self {
49 capacity,
50 ..Default::default()
51 }
52 }
53
54 pub fn backpressure_count(&self) -> usize {
56 (self.capacity as f32 * self.backpressure_threshold) as usize
57 }
58}
59
60pub struct Mailbox {
65 actor_id: ActorId,
67 config: MailboxConfig,
69 queue: ArrayQueue<Message>,
71 size: AtomicUsize,
73 semaphore: Arc<Semaphore>,
75 backpressure: AtomicBool,
77 critical_queue: ArrayQueue<Message>,
79 notify: Arc<Notify>,
81}
82
83impl Mailbox {
84 pub fn new(actor_id: ActorId, config: MailboxConfig) -> Self {
86 let capacity = config.capacity;
87 Self {
88 actor_id,
89 config,
90 queue: ArrayQueue::new(capacity),
91 size: AtomicUsize::new(0),
92 semaphore: Arc::new(Semaphore::new(capacity)),
93 backpressure: AtomicBool::new(false),
94 critical_queue: ArrayQueue::new(capacity),
95 notify: Arc::new(Notify::new()),
96 }
97 }
98
99 pub fn try_send(&self, message: Message) -> Result<(), (Message, Error)> {
101 let semaphore = self.semaphore.clone();
102 let permit = semaphore.try_acquire_owned();
103 match permit {
104 Ok(permit) => {
105 permit.forget();
106 }
107 Err(_) => {
108 return Err((
109 message,
110 Error::resource_exhausted(format!(
111 "mailbox for actor {:?} is full",
112 self.actor_id
113 )),
114 ));
115 }
116 }
117
118 let size = self.size.fetch_add(1, Ordering::Relaxed) + 1;
119
120 if let Err(rejected) =
121 if self.config.priority_queue && message.priority == Priority::Critical {
122 self.critical_queue.push(message)
123 } else {
124 self.queue.push(message)
125 }
126 {
127 self.size.fetch_sub(1, Ordering::Relaxed);
128 self.semaphore.add_permits(1);
129 return Err((
130 rejected,
131 Error::resource_exhausted(format!(
132 "mailbox for actor {:?} internal queue is full",
133 self.actor_id
134 )),
135 ));
136 }
137
138 if size >= self.config.backpressure_count() {
139 self.backpressure.store(true, Ordering::Relaxed);
140 }
141
142 self.notify.notify_waiters();
143
144 Ok(())
145 }
146
147 pub async fn send(&self, message: Message) -> crate::Result<()> {
149 let semaphore = self.semaphore.clone();
150 let permit = semaphore
151 .acquire()
152 .await
153 .map_err(|e| Error::resource_exhausted(e.to_string()))?;
154 permit.forget();
155
156 let size = self.size.fetch_add(1, Ordering::Relaxed) + 1;
157
158 if if self.config.priority_queue && message.priority == Priority::Critical {
159 self.critical_queue.push(message)
160 } else {
161 self.queue.push(message)
162 }
163 .is_err()
164 {
165 self.size.fetch_sub(1, Ordering::Relaxed);
166 self.semaphore.add_permits(1);
167 return Err(Error::resource_exhausted(format!(
168 "mailbox for actor {:?} internal queue is full",
169 self.actor_id
170 )));
171 }
172
173 if size >= self.config.backpressure_count() {
174 self.backpressure.store(true, Ordering::Relaxed);
175 }
176
177 self.notify.notify_waiters();
178
179 Ok(())
180 }
181
182 pub fn try_recv(&self) -> Option<Message> {
184 if self.config.priority_queue {
185 if let Some(msg) = self.critical_queue.pop() {
186 self.size.fetch_sub(1, Ordering::Relaxed);
187 self.semaphore.add_permits(1);
188 self.check_backpressure();
189 return Some(msg);
190 }
191 }
192
193 if let Some(msg) = self.queue.pop() {
194 self.size.fetch_sub(1, Ordering::Relaxed);
195 self.semaphore.add_permits(1);
196 self.check_backpressure();
197 return Some(msg);
198 }
199
200 None
201 }
202
203 pub async fn recv(&self) -> Message {
210 loop {
211 let notified = self.notify.notified();
212 tokio::pin!(notified);
213 notified.as_mut().enable();
214 if let Some(msg) = self.try_recv() {
215 return msg;
216 }
217 notified.await;
218 }
219 }
220
221 pub fn len(&self) -> usize {
223 self.size.load(Ordering::Relaxed)
224 }
225
226 pub fn is_empty(&self) -> bool {
228 self.len() == 0
229 }
230
231 pub fn is_backpressured(&self) -> bool {
233 self.backpressure.load(Ordering::Relaxed)
234 }
235
236 pub fn capacity(&self) -> usize {
238 self.config.capacity
239 }
240
241 pub fn remaining_capacity(&self) -> usize {
243 self.config.capacity.saturating_sub(self.len())
244 }
245
246 pub fn clear(&self) {
248 let mut total = 0;
249
250 while self.critical_queue.pop().is_some() {
251 total += 1;
252 }
253 while self.queue.pop().is_some() {
254 total += 1;
255 }
256
257 if total > 0 {
258 self.size.fetch_sub(total, Ordering::Relaxed);
259 self.semaphore.add_permits(total);
260 }
261 self.backpressure.store(false, Ordering::Relaxed);
262 }
263
264 fn check_backpressure(&self) {
265 let size = self.size.load(Ordering::Relaxed);
266 if size < (self.config.backpressure_count() / 2) {
267 self.backpressure.store(false, Ordering::Relaxed);
268 }
269 }
270}
271
272#[cfg(test)]
273mod tests {
274 use super::*;
275 use crate::MessagePayload;
276
277 #[test]
278 fn test_mailbox_basic() {
279 let mailbox = Mailbox::new(ActorId::new(), MailboxConfig::default());
280
281 let msg = Message {
282 sender: None,
283 payload: MessagePayload::Start,
284 priority: Priority::Normal,
285 };
286
287 mailbox.try_send(msg).unwrap();
288 assert_eq!(mailbox.len(), 1);
289
290 let received = mailbox.try_recv();
291 assert!(received.is_some());
292 assert!(mailbox.is_empty());
293 }
294
295 #[test]
296 fn test_mailbox_backpressure() {
297 let config = MailboxConfig {
298 capacity: 10,
299 backpressure_threshold: 0.8,
300 ..Default::default()
301 };
302 let mailbox = Mailbox::new(ActorId::new(), config);
303
304 for i in 0..8 {
305 let msg = Message {
306 sender: None,
307 payload: MessagePayload::Custom(vec![i as u8]),
308 priority: Priority::Normal,
309 };
310 mailbox.try_send(msg).unwrap();
311 }
312
313 assert!(mailbox.is_backpressured());
314 }
315
316 #[test]
317 fn test_mailbox_priority() {
318 let config = MailboxConfig {
319 priority_queue: true,
320 ..Default::default()
321 };
322 let mailbox = Mailbox::new(ActorId::new(), config);
323
324 let normal_msg = Message {
325 sender: None,
326 payload: MessagePayload::Custom(vec![1]),
327 priority: Priority::Normal,
328 };
329 let critical_msg = Message {
330 sender: None,
331 payload: MessagePayload::Custom(vec![2]),
332 priority: Priority::Critical,
333 };
334
335 mailbox.try_send(normal_msg).unwrap();
336 mailbox.try_send(critical_msg).unwrap();
337
338 let first = mailbox.try_recv().unwrap();
339 assert_eq!(first.priority, Priority::Critical);
340 }
341
342 #[tokio::test]
343 async fn test_mailbox_async() {
344 let mailbox = Mailbox::new(ActorId::new(), MailboxConfig::default());
345
346 let msg = Message {
347 sender: None,
348 payload: MessagePayload::Start,
349 priority: Priority::Normal,
350 };
351
352 mailbox.send(msg).await.unwrap();
353 let received = mailbox.recv().await;
354 assert!(matches!(received.payload, MessagePayload::Start));
355 }
356
357 #[tokio::test]
358 async fn test_recv_waits_then_wakes() {
359 let mailbox = Arc::new(Mailbox::new(ActorId::new(), MailboxConfig::default()));
360 let mailbox2 = mailbox.clone();
361
362 let handle = tokio::spawn(async move { mailbox2.recv().await });
363
364 tokio::time::sleep(std::time::Duration::from_millis(10)).await;
365
366 let msg = Message {
367 sender: None,
368 payload: MessagePayload::Custom(vec![42]),
369 priority: Priority::Normal,
370 };
371 mailbox.try_send(msg).unwrap();
372
373 let received = handle.await.expect("recv task should not panic");
374 assert!(matches!(received.payload, MessagePayload::Custom(_)));
375 }
376
377 #[test]
378 fn test_mailbox_clear() {
379 let config = MailboxConfig {
380 capacity: 10,
381 priority_queue: true,
382 ..Default::default()
383 };
384 let mailbox = Mailbox::new(ActorId::new(), config);
385
386 for i in 0..5 {
387 let msg = Message {
388 sender: None,
389 payload: MessagePayload::Custom(vec![i]),
390 priority: Priority::Normal,
391 };
392 mailbox.try_send(msg).unwrap();
393 }
394 let critical_msg = Message {
395 sender: None,
396 payload: MessagePayload::Custom(vec![99]),
397 priority: Priority::Critical,
398 };
399 mailbox.try_send(critical_msg).unwrap();
400
401 assert_eq!(mailbox.len(), 6);
402 mailbox.clear();
403 assert!(mailbox.is_empty());
404 assert!(!mailbox.is_backpressured());
405 }
406}