orinium_browser/engine/port/
mailbox.rs1use std::sync::Arc;
9
10pub struct Undeliverable<T>(pub T);
16
17impl<T> std::fmt::Debug for Undeliverable<T> {
20 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21 f.write_str("Undeliverable(..)")
22 }
23}
24
25impl<T: PartialEq> PartialEq for Undeliverable<T> {
26 fn eq(&self, other: &Self) -> bool {
27 self.0 == other.0
28 }
29}
30
31impl<T: Eq> Eq for Undeliverable<T> {}
32
33impl<T> std::fmt::Display for Undeliverable<T> {
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 f.write_str("the receiving end of the channel is gone")
36 }
37}
38
39impl<T> std::error::Error for Undeliverable<T> {}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
43pub struct Disconnected;
44
45impl std::fmt::Display for Disconnected {
46 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47 f.write_str("the sending end of the channel is gone")
48 }
49}
50
51impl std::error::Error for Disconnected {}
52
53pub struct Outbox<T> {
58 inner: Arc<dyn Produce<T> + Send + Sync>,
59}
60
61impl<T: Send + 'static> Outbox<T> {
62 pub fn of<P: Produce<T> + Send + Sync + 'static>(producer: P) -> Self {
67 Self {
68 inner: Arc::new(producer),
69 }
70 }
71
72 pub fn send(&self, message: T) -> Result<(), Undeliverable<T>> {
77 self.inner.send(message)
78 }
79}
80
81impl<T> std::fmt::Debug for Outbox<T> {
82 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83 f.write_str("Outbox")
84 }
85}
86
87impl<T> Clone for Outbox<T> {
88 fn clone(&self) -> Self {
89 Self {
90 inner: Arc::clone(&self.inner),
91 }
92 }
93}
94
95pub struct Mailbox<T> {
102 inner: Arc<dyn Consume<T> + Send + Sync>,
103}
104
105impl<T: Send + 'static> Mailbox<T> {
106 pub fn of<C: Consume<T> + Send + Sync + 'static>(consumer: C) -> Self {
111 Self {
112 inner: Arc::new(consumer),
113 }
114 }
115
116 pub fn recv(&self) -> Result<T, Disconnected> {
122 self.inner.recv()
123 }
124
125 pub fn try_recv(&self) -> Option<T> {
130 self.inner.try_recv()
131 }
132}
133
134impl<T> std::fmt::Debug for Mailbox<T> {
135 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136 f.write_str("Mailbox")
137 }
138}
139
140impl<T> Clone for Mailbox<T> {
141 fn clone(&self) -> Self {
142 Self {
143 inner: Arc::clone(&self.inner),
144 }
145 }
146}
147
148pub trait Produce<T>: Send + Sync {
150 fn send(&self, message: T) -> Result<(), Undeliverable<T>>;
151}
152
153pub trait Consume<T>: Send + Sync {
160 fn recv(&self) -> Result<T, Disconnected>;
161 fn try_recv(&self) -> Option<T>;
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167 use crate::engine::port::transport::{InProcess, Transport};
168 use crate::test_support::{assert_send, assert_sync};
169
170 fn pair() -> (Outbox<u32>, Mailbox<u32>) {
174 InProcess.channel()
175 }
176
177 #[test]
178 fn messages_arrive_in_the_order_they_were_posted() {
179 let (outbox, mailbox) = pair();
180 for value in 0..3 {
181 outbox.send(value).expect("consumer is present");
182 }
183 let received: Vec<u32> = std::iter::from_fn(|| mailbox.try_recv()).collect();
184 assert_eq!(received, [0, 1, 2]);
185 }
186
187 #[test]
190 fn try_recv_on_an_idle_channel_reports_nothing() {
191 let (_outbox, mailbox) = pair();
192 assert!(mailbox.try_recv().is_none());
193 }
194
195 #[test]
196 fn recv_waits_for_a_message_that_has_not_been_posted_yet() {
197 let (outbox, mailbox) = pair();
198 let producer = std::thread::spawn(move || {
199 outbox.send(7).expect("consumer is present");
200 });
201 assert_eq!(mailbox.recv().expect("producer is alive"), 7);
202 producer.join().expect("producer thread finished");
203 }
204
205 #[test]
206 fn dropping_the_outbox_drains_before_reporting_disconnection() {
207 let (outbox, mailbox) = pair();
208 outbox.send(1).expect("consumer is present");
209 drop(outbox);
210 assert_eq!(mailbox.recv().expect("a message is still queued"), 1);
211 assert_eq!(mailbox.recv(), Err(Disconnected));
212 }
213
214 #[test]
215 fn sending_to_a_dropped_consumer_hands_the_message_back() {
216 let (outbox, mailbox) = pair();
217 drop(mailbox);
218 assert_eq!(outbox.send(3), Err(Undeliverable(3)));
219 }
220
221 #[test]
225 fn every_clone_of_an_outbox_reaches_the_same_mailbox() {
226 let (outbox, mailbox) = pair();
227 let mut producers = Vec::new();
228 for value in 0..4u32 {
229 let outbox = outbox.clone();
230 producers.push(std::thread::spawn(move || {
231 outbox.send(value).expect("consumer is present");
232 }));
233 }
234 for producer in producers {
235 producer.join().expect("producer thread finished");
236 }
237 let mut received: Vec<u32> = std::iter::from_fn(|| mailbox.try_recv()).collect();
238 received.sort_unstable();
239 assert_eq!(received, [0, 1, 2, 3]);
240 }
241
242 #[test]
245 fn ends_are_shareable_across_threads() {
246 assert_send::<Outbox<u32>>();
247 assert_sync::<Outbox<u32>>();
248 assert_send::<Mailbox<u32>>();
249 assert_sync::<Mailbox<u32>>();
250 }
251}