1use std::collections::VecDeque;
2use std::sync::Mutex;
3
4use crate::bytecode::Value;
5
6use super::error::RuntimeError;
7use super::process::Flow;
8use super::sync_lock;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub(crate) enum WaitFilter {
16 Any,
18 Tag(u16),
20 Correlation {
28 expect_request_id: u64,
29 expect_sender: Option<u64>,
30 },
31}
32
33impl WaitFilter {
34 #[inline]
35 pub(crate) fn matches(&self, value: &Value) -> bool {
36 match *self {
37 Self::Any => true,
38 Self::Tag(expected_tag) => value
39 .as_message()
40 .map(|m| m.tag == expected_tag)
41 .unwrap_or(false),
42 Self::Correlation {
43 expect_request_id,
44 expect_sender,
45 } => match value.as_message() {
46 Some(m) => {
47 m.request_id == expect_request_id
48 && expect_sender.map(|s| m.sender == s).unwrap_or(true)
49 }
50 None => false,
51 },
52 }
53 }
54}
55
56pub struct Mailbox {
93 inner: Mutex<MailboxInner>,
94}
95
96struct MailboxInner {
97 queue: VecDeque<Value>,
98 parked: Option<Box<Flow>>,
99 parked_filter: WaitFilter,
101}
102
103impl Default for MailboxInner {
104 fn default() -> Self {
105 Self {
106 queue: VecDeque::new(),
107 parked: None,
108 parked_filter: WaitFilter::Any,
109 }
110 }
111}
112
113pub enum Delivery {
118 Queued,
119 Handoff(Box<Flow>),
120}
121
122impl Mailbox {
123 pub fn new() -> Self {
124 Mailbox {
125 inner: Mutex::new(MailboxInner::default()),
126 }
127 }
128
129 pub fn push(&self, value: Value) -> Result<Delivery, RuntimeError> {
137 let mut inner = sync_lock::lock(&self.inner, "Mailbox::push")?;
138 if let Some(flow) = inner.parked.take() {
139 if inner.parked_filter.matches(&value) {
140 inner.parked_filter = WaitFilter::Any;
141 return Ok(Delivery::Handoff(flow));
142 }
143 inner.parked = Some(flow);
145 inner.queue.push_back(value);
146 return Ok(Delivery::Queued);
147 }
148 inner.queue.push_back(value);
149 Ok(Delivery::Queued)
150 }
151
152 pub fn try_pop(&self) -> Result<Option<Value>, RuntimeError> {
154 self.try_pop_filter(WaitFilter::Any)
155 }
156
157 pub fn try_pop_match(&self, tag: u16) -> Result<Option<Value>, RuntimeError> {
159 self.try_pop_filter(WaitFilter::Tag(tag))
160 }
161
162 pub(crate) fn try_pop_filter(
164 &self,
165 filter: WaitFilter,
166 ) -> Result<Option<Value>, RuntimeError> {
167 let mut inner = sync_lock::lock(&self.inner, "Mailbox::try_pop_filter")?;
168 Ok(take_with_filter(&mut inner.queue, filter))
169 }
170
171 pub fn park(&self, flow: Box<Flow>) -> Result<Result<(), Box<Flow>>, RuntimeError> {
179 self.park_filter(flow, WaitFilter::Any)
180 }
181
182 pub fn park_match(
186 &self,
187 flow: Box<Flow>,
188 tag: u16,
189 ) -> Result<Result<(), Box<Flow>>, RuntimeError> {
190 self.park_filter(flow, WaitFilter::Tag(tag))
191 }
192
193 pub(crate) fn park_filter(
196 &self,
197 flow: Box<Flow>,
198 filter: WaitFilter,
199 ) -> Result<Result<(), Box<Flow>>, RuntimeError> {
200 let mut inner = sync_lock::lock(&self.inner, "Mailbox::park_filter")?;
201 if let Some(value) = take_with_filter(&mut inner.queue, filter) {
202 drop(inner);
203 return Ok(Err(with_pending(flow, value)));
204 }
205 inner.parked_filter = filter;
206 inner.parked = Some(flow);
207 Ok(Ok(()))
208 }
209
210 pub fn take_parked(&self) -> Result<Option<Box<Flow>>, RuntimeError> {
214 let mut inner = sync_lock::lock(&self.inner, "Mailbox::take_parked")?;
215 inner.parked_filter = WaitFilter::Any;
216 Ok(inner.parked.take())
217 }
218}
219
220impl Default for Mailbox {
221 fn default() -> Self {
222 Self::new()
223 }
224}
225
226fn take_with_filter(queue: &mut VecDeque<Value>, filter: WaitFilter) -> Option<Value> {
229 match filter {
230 WaitFilter::Any => queue.pop_front(),
231 other => {
232 let idx = queue.iter().position(|v| other.matches(v))?;
233 queue.remove(idx)
234 }
235 }
236}
237
238fn with_pending(mut flow: Box<Flow>, value: Value) -> Box<Flow> {
242 flow.pending_message = Some(value);
243 flow
244}
245
246#[cfg(test)]
247mod tests {
248 use super::*;
249 use crate::bytecode::Message;
250 use crate::scheduler::oneshot;
251 use crate::scheduler::process::{next_flow_id, RestartPolicy};
252 use crate::vm::{NativeTable, Vm};
253 use crate::ChunkBuilder;
254 use std::sync::Arc;
255
256 fn dummy_flow() -> Box<Flow> {
257 let mut b = ChunkBuilder::new("mb");
258 b.begin_function("main", 0, 1);
259 b.emit_return(0);
260 let chunk = b.finish();
261 let vm = Vm::new(Arc::new(chunk), NativeTable::empty(), 0, &[]).expect("vm");
262 let (tx, _rx) = oneshot::channel();
263 Box::new(Flow::new(
264 next_flow_id(),
265 vm,
266 Arc::new(Mailbox::new()),
267 RestartPolicy::Never,
268 tx,
269 ))
270 }
271
272 fn hop(sender: u64, request_id: u64, tag: u16, payload: u64) -> Value {
273 Value::Message(Message::new(sender, request_id, tag, payload))
274 }
275
276 fn msg(tag: u16, payload: u64) -> Value {
277 hop(1, 1, tag, payload)
278 }
279
280 #[test]
281 fn try_pop_match_skips_non_matching_fifo() {
282 let mb = Mailbox::new();
283 mb.push(msg(9, 1)).unwrap();
284 mb.push(msg(1, 42)).unwrap();
285 mb.push(msg(9, 2)).unwrap();
286 let got = mb.try_pop_match(1).unwrap().expect("match");
287 assert_eq!(got.as_message().unwrap().payload, 42);
288 assert_eq!(mb.try_pop().unwrap().unwrap().as_message().unwrap().tag, 9);
289 assert_eq!(mb.try_pop().unwrap().unwrap().as_message().unwrap().payload, 2);
290 }
291
292 #[test]
293 fn push_while_park_match_queues_junk_keeps_waiter() {
294 let mb = Mailbox::new();
295 let flow = dummy_flow();
296 assert!(mb.park_match(flow, 1).unwrap().is_ok());
297 assert!(matches!(mb.push(msg(9, 0)).unwrap(), Delivery::Queued));
298 assert!(matches!(mb.push(msg(1, 7)).unwrap(), Delivery::Handoff(_)));
299 assert_eq!(mb.try_pop().unwrap().unwrap().as_message().unwrap().tag, 9);
300 }
301
302 #[test]
303 fn ask_does_not_consume_reply_for_another_request() {
304 let mb = Mailbox::new();
305 mb.push(hop(10, 2, 2, 99)).unwrap();
306 mb.push(hop(10, 1, 2, 42)).unwrap();
307 let filter = WaitFilter::Correlation {
308 expect_request_id: 1,
309 expect_sender: Some(10),
310 };
311 let got = mb.try_pop_filter(filter).unwrap().expect("id=1");
312 assert_eq!(got.as_message().unwrap().payload, 42);
313 let left = mb.try_pop().unwrap().unwrap();
315 assert_eq!(left.as_message().unwrap().request_id, 2);
316 }
317
318 #[test]
319 fn ask_requires_reply_from_target() {
320 let mb = Mailbox::new();
321 let flow = dummy_flow();
322 let filter = WaitFilter::Correlation {
323 expect_request_id: 1,
324 expect_sender: Some(10), };
326 assert!(mb.park_filter(flow, filter).unwrap().is_ok());
327 assert!(matches!(
329 mb.push(hop(99, 1, 2, 0)).unwrap(),
330 Delivery::Queued
331 ));
332 assert!(matches!(
334 mb.push(hop(10, 1, 2, 42)).unwrap(),
335 Delivery::Handoff(_)
336 ));
337 assert_eq!(mb.try_pop().unwrap().unwrap().as_message().unwrap().sender, 99);
338 }
339}