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 {
198 loop {
199 if let Some(msg) = self.try_recv() {
200 return msg;
201 }
202 self.notify.notified().await;
203 }
204 }
205
206 pub fn len(&self) -> usize {
208 self.size.load(Ordering::Relaxed)
209 }
210
211 pub fn is_empty(&self) -> bool {
213 self.len() == 0
214 }
215
216 pub fn is_backpressured(&self) -> bool {
218 self.backpressure.load(Ordering::Relaxed)
219 }
220
221 pub fn capacity(&self) -> usize {
223 self.config.capacity
224 }
225
226 pub fn remaining_capacity(&self) -> usize {
228 self.config.capacity.saturating_sub(self.len())
229 }
230
231 pub fn clear(&self) {
233 let mut total = 0;
234
235 while self.critical_queue.pop().is_some() {
236 total += 1;
237 }
238 while self.queue.pop().is_some() {
239 total += 1;
240 }
241
242 if total > 0 {
243 self.size.fetch_sub(total, Ordering::Relaxed);
244 self.semaphore.add_permits(total);
245 }
246 self.backpressure.store(false, Ordering::Relaxed);
247 }
248
249 fn check_backpressure(&self) {
250 let size = self.size.load(Ordering::Relaxed);
251 if size < (self.config.backpressure_count() / 2) {
252 self.backpressure.store(false, Ordering::Relaxed);
253 }
254 }
255}
256
257#[cfg(test)]
258mod tests {
259 use super::*;
260 use crate::MessagePayload;
261
262 #[test]
263 fn test_mailbox_basic() {
264 let mailbox = Mailbox::new(ActorId::new(), MailboxConfig::default());
265
266 let msg = Message {
267 sender: None,
268 payload: MessagePayload::Start,
269 priority: Priority::Normal,
270 };
271
272 mailbox.try_send(msg).unwrap();
273 assert_eq!(mailbox.len(), 1);
274
275 let received = mailbox.try_recv();
276 assert!(received.is_some());
277 assert!(mailbox.is_empty());
278 }
279
280 #[test]
281 fn test_mailbox_backpressure() {
282 let config = MailboxConfig {
283 capacity: 10,
284 backpressure_threshold: 0.8,
285 ..Default::default()
286 };
287 let mailbox = Mailbox::new(ActorId::new(), config);
288
289 for i in 0..8 {
290 let msg = Message {
291 sender: None,
292 payload: MessagePayload::Custom(vec![i as u8]),
293 priority: Priority::Normal,
294 };
295 mailbox.try_send(msg).unwrap();
296 }
297
298 assert!(mailbox.is_backpressured());
299 }
300
301 #[test]
302 fn test_mailbox_priority() {
303 let config = MailboxConfig {
304 priority_queue: true,
305 ..Default::default()
306 };
307 let mailbox = Mailbox::new(ActorId::new(), config);
308
309 let normal_msg = Message {
310 sender: None,
311 payload: MessagePayload::Custom(vec![1]),
312 priority: Priority::Normal,
313 };
314 let critical_msg = Message {
315 sender: None,
316 payload: MessagePayload::Custom(vec![2]),
317 priority: Priority::Critical,
318 };
319
320 mailbox.try_send(normal_msg).unwrap();
321 mailbox.try_send(critical_msg).unwrap();
322
323 let first = mailbox.try_recv().unwrap();
324 assert_eq!(first.priority, Priority::Critical);
325 }
326
327 #[tokio::test]
328 async fn test_mailbox_async() {
329 let mailbox = Mailbox::new(ActorId::new(), MailboxConfig::default());
330
331 let msg = Message {
332 sender: None,
333 payload: MessagePayload::Start,
334 priority: Priority::Normal,
335 };
336
337 mailbox.send(msg).await.unwrap();
338 let received = mailbox.recv().await;
339 assert!(matches!(received.payload, MessagePayload::Start));
340 }
341
342 #[tokio::test]
343 async fn test_recv_waits_then_wakes() {
344 let mailbox = Arc::new(Mailbox::new(ActorId::new(), MailboxConfig::default()));
345 let mailbox2 = mailbox.clone();
346
347 let handle = tokio::spawn(async move { mailbox2.recv().await });
348
349 tokio::time::sleep(std::time::Duration::from_millis(10)).await;
350
351 let msg = Message {
352 sender: None,
353 payload: MessagePayload::Custom(vec![42]),
354 priority: Priority::Normal,
355 };
356 mailbox.try_send(msg).unwrap();
357
358 let received = handle.await.expect("recv task should not panic");
359 assert!(matches!(received.payload, MessagePayload::Custom(_)));
360 }
361
362 #[test]
363 fn test_mailbox_clear() {
364 let config = MailboxConfig {
365 capacity: 10,
366 priority_queue: true,
367 ..Default::default()
368 };
369 let mailbox = Mailbox::new(ActorId::new(), config);
370
371 for i in 0..5 {
372 let msg = Message {
373 sender: None,
374 payload: MessagePayload::Custom(vec![i]),
375 priority: Priority::Normal,
376 };
377 mailbox.try_send(msg).unwrap();
378 }
379 let critical_msg = Message {
380 sender: None,
381 payload: MessagePayload::Custom(vec![99]),
382 priority: Priority::Critical,
383 };
384 mailbox.try_send(critical_msg).unwrap();
385
386 assert_eq!(mailbox.len(), 6);
387 mailbox.clear();
388 assert!(mailbox.is_empty());
389 assert!(!mailbox.is_backpressured());
390 }
391}