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