forked from nulang-org/nulang
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmailbox.rs
More file actions
127 lines (110 loc) · 3.65 KB
/
Copy pathmailbox.rs
File metadata and controls
127 lines (110 loc) · 3.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
//! Bounded mailbox: atomic ring buffer.
use crate::types::Value;
/// Message sent to an actor.
#[derive(Debug, Clone)]
pub struct Message {
pub behavior_id: u32,
pub payload: Vec<Value>,
pub sender: u32,
}
/// Lock-free bounded mailbox.
pub struct Mailbox {
buffer: Vec<Option<Message>>,
capacity: usize,
head: std::sync::atomic::AtomicUsize,
tail: std::sync::atomic::AtomicUsize,
}
impl Mailbox {
pub fn new(capacity: usize) -> Self {
let cap = capacity.next_power_of_two();
let mut buffer = Vec::with_capacity(cap);
for _ in 0..cap {
buffer.push(None);
}
Mailbox {
buffer,
capacity: cap,
head: std::sync::atomic::AtomicUsize::new(0),
tail: std::sync::atomic::AtomicUsize::new(0),
}
}
pub fn send(&self, msg: Message) -> Result<(), MailboxError> {
let tail = self.tail.load(std::sync::atomic::Ordering::Relaxed);
let head = self.head.load(std::sync::atomic::Ordering::Acquire);
if tail - head >= self.capacity {
return Err(MailboxError::Full);
}
let idx = tail & (self.capacity - 1);
self.buffer[idx].replace(msg);
self.tail.store(tail + 1, std::sync::atomic::Ordering::Release);
Ok(())
}
pub fn receive(&self) -> Option<Message> {
let head = self.head.load(std::sync::atomic::Ordering::Relaxed);
let tail = self.tail.load(std::sync::atomic::Ordering::Acquire);
if head >= tail {
return None;
}
let idx = head & (self.capacity - 1);
let msg = self.buffer[idx].take()?;
self.head.store(head + 1, std::sync::atomic::Ordering::Release);
Some(msg)
}
pub fn len(&self) -> usize {
let tail = self.tail.load(std::sync::atomic::Ordering::Acquire);
let head = self.head.load(std::sync::atomic::Ordering::Acquire);
tail - head
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn is_full(&self) -> bool {
self.len() >= self.capacity
}
}
#[derive(Debug)]
pub enum MailboxError {
Full,
}
impl std::fmt::Display for MailboxError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MailboxError::Full => write!(f, "Mailbox is full"),
}
}
}
impl std::error::Error for MailboxError {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mailbox_send_receive() {
let mb = Mailbox::new(4);
let msg = Message { behavior_id: 0, payload: vec![Value::int(42)], sender: 0 };
mb.send(msg.clone()).unwrap();
let received = mb.receive().unwrap();
assert_eq!(received.behavior_id, 0);
assert_eq!(received.payload[0].as_int(), Some(42));
}
#[test]
fn test_mailbox_fifo() {
let mb = Mailbox::new(4);
mb.send(Message { behavior_id: 0, payload: vec![Value::int(1)], sender: 0 }).unwrap();
mb.send(Message { behavior_id: 1, payload: vec![Value::int(2)], sender: 0 }).unwrap();
assert_eq!(mb.receive().unwrap().behavior_id, 0);
assert_eq!(mb.receive().unwrap().behavior_id, 1);
}
#[test]
fn test_mailbox_full() {
let mb = Mailbox::new(2);
mb.send(Message { behavior_id: 0, payload: vec![], sender: 0 }).unwrap();
mb.send(Message { behavior_id: 1, payload: vec![], sender: 0 }).unwrap();
assert!(mb.send(Message { behavior_id: 2, payload: vec![], sender: 0 }).is_err());
}
#[test]
fn test_mailbox_empty() {
let mb = Mailbox::new(4);
assert!(mb.receive().is_none());
assert!(mb.is_empty());
}
}