1use std::cell::RefCell;
52use std::collections::{HashMap, VecDeque};
53
54use crate::objects;
55use crate::sched::{ProcessId, ProcessRequest};
56
57#[derive(Debug, Clone, PartialEq, Eq)]
59pub struct Message {
60 pub words: Box<[u64]>,
62 pub fingerprint: u64,
64 pub obj: bool,
67 pub coll: bool,
72}
73
74impl Message {
75 fn peeked(&self) -> Message {
78 if self.obj {
79 objects::retain(self.words[0]);
80 }
81 let mut m = self.clone();
82 if self.coll {
83 m.words[0] = crate::collections::clone(self.words[0]);
84 }
85 m
86 }
87
88 pub fn discard(self) {
90 if self.obj {
91 objects::release(self.words[0]);
92 }
93 if self.coll {
94 crate::collections::release(self.words[0]);
95 }
96 }
97}
98
99#[derive(Debug, PartialEq, Eq)]
101enum Delivery {
102 Message(Message),
103 Mismatch,
105}
106
107#[derive(Debug)]
108enum Pending {
109 Completed,
111 Waiting(u64),
113 Delivered(Delivery),
115}
116
117struct Getter {
118 proc: ProcessId,
119 peek: bool,
120 fingerprint: u64,
121}
122
123#[derive(Default)]
124struct Mailbox {
125 bound: usize,
127 messages: VecDeque<Message>,
128 getters: VecDeque<Getter>,
129 putters: VecDeque<(ProcessId, Message)>,
130}
131
132#[derive(Default)]
133struct Table {
134 mailboxes: HashMap<u64, Mailbox>,
135 pending: HashMap<ProcessId, Pending>,
136 stash: HashMap<(ProcessId, u32), u64>,
140}
141
142thread_local! {
145 static TABLE: RefCell<Table> = RefCell::new(Table::default());
146}
147
148fn with_table<R>(f: impl FnOnce(&mut Table) -> R) -> R {
149 TABLE.with(|t| f(&mut t.borrow_mut()))
150}
151
152#[derive(Default)]
154struct Releases {
155 handles: Vec<u64>,
156 colls: Vec<u64>,
158}
159
160impl Releases {
161 fn owned(&mut self, m: &Message) {
163 if m.obj {
164 self.handles.push(m.words[0]);
165 }
166 if m.coll {
167 self.colls.push(m.words[0]);
168 }
169 }
170
171 fn run(self) {
175 for h in self.handles {
176 objects::release(h);
177 }
178 for h in self.colls {
179 crate::collections::release(h);
180 }
181 super::notify_object_state_change();
182 }
183}
184
185pub fn init(h: u64, bound: i64) -> bool {
188 if !objects::is_live(h) {
189 return false;
190 }
191 let bound = usize::try_from(bound).unwrap_or(0);
192 with_table(|t| {
193 t.mailboxes.insert(
194 h,
195 Mailbox {
196 bound,
197 ..Mailbox::default()
198 },
199 );
200 });
201 true
202}
203
204pub fn num(h: u64) -> usize {
206 with_table(|t| t.mailboxes.get(&h).map_or(0, |m| m.messages.len()))
207}
208
209pub fn waiters(h: u64) -> usize {
211 with_table(|t| {
212 t.mailboxes
213 .get(&h)
214 .map_or(0, |m| m.getters.len() + m.putters.len())
215 })
216}
217
218fn is_full(m: &Mailbox) -> bool {
219 m.bound != 0 && m.messages.len() >= m.bound
220}
221
222fn wake(
226 t: &mut Table,
227 proc: ProcessId,
228 h: u64,
229 delivery: Option<Delivery>,
230 emit: &mut dyn FnMut(ProcessRequest),
231 rel: &mut Releases,
232) {
233 match delivery {
234 Some(d) => {
235 t.pending.insert(proc, Pending::Delivered(d));
236 }
237 None => {
238 t.pending.remove(&proc);
239 }
240 }
241 emit(ProcessRequest::SyncWake(proc));
242 rel.handles.push(h);
243}
244
245fn admit_putters(t: &mut Table, h: u64, emit: &mut dyn FnMut(ProcessRequest), rel: &mut Releases) {
247 loop {
248 let Some(m) = t.mailboxes.get_mut(&h) else {
249 return;
250 };
251 if is_full(m) {
252 return;
253 }
254 let Some((proc, msg)) = m.putters.pop_front() else {
255 return;
256 };
257 m.messages.push_back(msg);
258 wake(t, proc, h, None, emit, rel);
259 }
260}
261
262fn serve_getters(t: &mut Table, h: u64, emit: &mut dyn FnMut(ProcessRequest), rel: &mut Releases) {
268 loop {
269 let Some(m) = t.mailboxes.get_mut(&h) else {
270 return;
271 };
272 if m.messages.is_empty() {
273 return;
274 }
275 let Some(g) = m.getters.pop_front() else {
276 return;
277 };
278 let head_fp = m.messages[0].fingerprint;
279 let delivery = if head_fp != g.fingerprint {
280 Delivery::Mismatch
281 } else if g.peek {
282 Delivery::Message(m.messages[0].peeked())
283 } else {
284 let msg = m.messages.pop_front().expect("checked non-empty");
285 Delivery::Message(msg)
286 };
287 let removed = matches!(&delivery, Delivery::Message(_)) && !g.peek;
288 wake(t, g.proc, h, Some(delivery), emit, rel);
289 if removed {
290 admit_putters(t, h, emit, rel);
291 }
292 }
293}
294
295#[derive(Debug, PartialEq, Eq)]
297pub enum PutResult {
298 Stored,
300 Blocked,
303}
304
305pub fn put(
308 h: u64,
309 proc: ProcessId,
310 msg: Message,
311 emit: &mut dyn FnMut(ProcessRequest),
312) -> Result<PutResult, Message> {
313 let mut rel = Releases::default();
314 let result = with_table(|t| {
315 let Some(m) = t.mailboxes.get_mut(&h) else {
316 return Err(msg);
317 };
318 if !is_full(m) {
319 m.messages.push_back(msg);
320 serve_getters(t, h, emit, &mut rel);
321 return Ok(PutResult::Stored);
322 }
323 assert!(
324 !t.pending.contains_key(&proc),
325 "mailbox: process {proc} called put() while already blocked in a mailbox call"
326 );
327 objects::retain(h);
328 t.mailboxes
329 .get_mut(&h)
330 .expect("looked up above")
331 .putters
332 .push_back((proc, msg));
333 t.pending.insert(proc, Pending::Waiting(h));
334 emit(ProcessRequest::SyncBlock);
335 Ok(PutResult::Blocked)
336 });
337 rel.run();
338 result
339}
340
341pub fn try_put(h: u64, msg: Message, emit: &mut dyn FnMut(ProcessRequest)) -> Result<(), Message> {
345 let mut rel = Releases::default();
346 let result = with_table(|t| {
347 let Some(m) = t.mailboxes.get_mut(&h) else {
348 return Err(msg);
349 };
350 if is_full(m) {
351 return Err(msg);
352 }
353 m.messages.push_back(msg);
354 serve_getters(t, h, emit, &mut rel);
355 Ok(())
356 });
357 rel.run();
358 result
359}
360
361#[derive(Debug, PartialEq, Eq)]
363pub enum Taken {
364 Empty,
365 Mismatch,
367 Got(Message),
368}
369
370fn take_front(
371 t: &mut Table,
372 h: u64,
373 fingerprint: u64,
374 peek: bool,
375 emit: &mut dyn FnMut(ProcessRequest),
376 rel: &mut Releases,
377) -> Taken {
378 let Some(m) = t.mailboxes.get_mut(&h) else {
379 return Taken::Empty;
380 };
381 let Some(head) = m.messages.front() else {
382 return Taken::Empty;
383 };
384 if head.fingerprint != fingerprint {
385 return Taken::Mismatch;
386 }
387 if peek {
388 return Taken::Got(head.peeked());
389 }
390 let msg = m.messages.pop_front().expect("checked non-empty");
391 admit_putters(t, h, emit, rel);
392 Taken::Got(msg)
393}
394
395pub fn try_take(
397 h: u64,
398 fingerprint: u64,
399 peek: bool,
400 emit: &mut dyn FnMut(ProcessRequest),
401) -> Taken {
402 let mut rel = Releases::default();
403 let taken = with_table(|t| take_front(t, h, fingerprint, peek, emit, &mut rel));
404 rel.run();
405 taken
406}
407
408#[derive(Debug, PartialEq, Eq)]
410pub enum Got {
411 Nothing,
413 NoMailbox,
416 Message(Message),
417 Mismatch,
419 Blocked,
421}
422
423pub fn get(
428 h: u64,
429 proc: ProcessId,
430 fingerprint: u64,
431 peek: bool,
432 emit: &mut dyn FnMut(ProcessRequest),
433) -> Got {
434 let mut rel = Releases::default();
435 let got = with_table(|t| {
436 match t.pending.remove(&proc) {
437 Some(Pending::Completed) => return Got::Nothing,
438 Some(Pending::Delivered(Delivery::Message(m))) => return Got::Message(m),
439 Some(Pending::Delivered(Delivery::Mismatch)) => return Got::Mismatch,
440 Some(Pending::Waiting(on)) => {
441 panic!("mailbox: process {proc} resumed while still blocked on mailbox {on:#x}")
442 }
443 None => {}
444 }
445 if !t.mailboxes.contains_key(&h) {
446 t.pending.insert(proc, Pending::Completed);
447 return Got::NoMailbox;
448 }
449 match take_front(t, h, fingerprint, peek, emit, &mut rel) {
450 Taken::Got(m) => {
451 t.pending.insert(proc, Pending::Completed);
452 Got::Message(m)
453 }
454 Taken::Mismatch => {
455 t.pending.insert(proc, Pending::Completed);
456 Got::Mismatch
457 }
458 Taken::Empty => {
459 objects::retain(h);
460 t.mailboxes
461 .get_mut(&h)
462 .expect("looked up above")
463 .getters
464 .push_back(Getter {
465 proc,
466 peek,
467 fingerprint,
468 });
469 t.pending.insert(proc, Pending::Waiting(h));
470 emit(ProcessRequest::SyncBlock);
471 Got::Blocked
472 }
473 }
474 });
475 rel.run();
476 got
477}
478
479pub fn second_phase(proc: ProcessId) -> bool {
485 with_table(|t| t.pending.contains_key(&proc))
486}
487
488pub fn stash_set(proc: ProcessId, slot: u32, value: u64) {
490 with_table(|t| {
491 t.stash.insert((proc, slot), value);
492 });
493}
494
495pub fn stash_get(proc: ProcessId, slot: u32) -> u64 {
497 with_table(|t| t.stash.get(&(proc, slot)).copied().unwrap_or(0))
498}
499
500pub fn cancel(proc: ProcessId) {
504 let mut rel = Releases::default();
505 with_table(|t| t.stash.retain(|(p, _), _| *p != proc));
506 with_table(|t| match t.pending.remove(&proc) {
507 None | Some(Pending::Completed) => {}
508 Some(Pending::Delivered(Delivery::Mismatch)) => {}
509 Some(Pending::Delivered(Delivery::Message(m))) => {
510 rel.owned(&m);
511 }
512 Some(Pending::Waiting(h)) => {
513 if let Some(m) = t.mailboxes.get_mut(&h) {
514 m.getters.retain(|g| g.proc != proc);
515 let mut kept = VecDeque::new();
516 for (p, msg) in std::mem::take(&mut m.putters) {
517 if p == proc {
518 rel.owned(&msg);
519 } else {
520 kept.push_back((p, msg));
521 }
522 }
523 m.putters = kept;
524 }
525 rel.handles.push(h);
526 }
527 });
528 rel.run();
529}
530
531pub fn reset_for_new_run() {
534 let old = with_table(std::mem::take);
535 let mut rel = Releases::default();
536 for (_, p) in old.pending {
537 if let Pending::Delivered(Delivery::Message(m)) = p {
538 rel.owned(&m);
539 }
540 }
541 for (h, m) in old.mailboxes {
542 for _ in 0..(m.getters.len() + m.putters.len()) {
543 rel.handles.push(h);
544 }
545 for (_, msg) in m.putters {
546 rel.owned(&msg);
547 }
548 for msg in m.messages {
549 msg.discard();
550 }
551 }
552 rel.run();
553}
554
555#[cfg(test)]
556mod tests {
557 use super::*;
558 use crate::netlist::ClassTemplateId;
559
560 const FP: u64 = 0x11;
565 const OTHER_FP: u64 = 0x22;
566
567 fn mbx(bound: i64, template: u32) -> u64 {
568 let h = objects::alloc(ClassTemplateId(template), 1);
569 assert!(init(h, bound));
570 h
571 }
572
573 fn msg(v: u64) -> Message {
574 Message {
575 words: vec![v].into_boxed_slice(),
576 fingerprint: FP,
577 obj: false,
578 coll: false,
579 }
580 }
581
582 fn value(m: &Message) -> u64 {
583 m.words[0]
584 }
585
586 #[derive(Default)]
587 struct Log(Vec<ProcessRequest>);
588 impl Log {
589 fn sink(&mut self) -> impl FnMut(ProcessRequest) + '_ {
590 |r| self.0.push(r)
591 }
592 }
593
594 #[test]
595 fn messages_come_out_in_fifo_order() {
596 let h = mbx(0, 18_000);
597 let mut log = Log::default();
598 for v in [10, 20, 30] {
599 assert_eq!(
600 put(h, 2_000, msg(v), &mut log.sink()),
601 Ok(PutResult::Stored)
602 );
603 }
604 assert_eq!(num(h), 3);
605 for want in [10, 20, 30] {
606 let Taken::Got(m) = try_take(h, FP, false, &mut log.sink()) else {
607 panic!("a message was queued");
608 };
609 assert_eq!(value(&m), want);
610 }
611 assert_eq!(try_take(h, FP, false, &mut log.sink()), Taken::Empty);
612 assert!(log.0.is_empty());
613 }
614
615 #[test]
616 fn try_put_fails_on_a_full_bounded_mailbox_and_hands_the_message_back() {
617 let h = mbx(2, 18_001);
618 let mut log = Log::default();
619 assert!(try_put(h, msg(1), &mut log.sink()).is_ok());
620 assert!(try_put(h, msg(2), &mut log.sink()).is_ok());
621 let back = try_put(h, msg(3), &mut log.sink()).unwrap_err();
622 assert_eq!(value(&back), 3);
623 assert_eq!(num(h), 2);
624 }
625
626 #[test]
627 fn an_unbounded_mailbox_is_never_full() {
628 let h = mbx(0, 18_002);
629 let mut log = Log::default();
630 for v in 0..1000 {
631 assert!(try_put(h, msg(v), &mut log.sink()).is_ok());
632 }
633 assert_eq!(num(h), 1000);
634 let n = mbx(-3, 18_003);
636 assert!(try_put(n, msg(1), &mut log.sink()).is_ok());
637 assert!(try_put(n, msg(2), &mut log.sink()).is_ok());
638 }
639
640 #[test]
641 fn a_get_on_an_empty_mailbox_blocks_and_a_put_delivers_to_it() {
642 let h = mbx(0, 18_004);
643 let mut log = Log::default();
644 assert_eq!(get(h, 2_010, FP, false, &mut log.sink()), Got::Blocked);
645 assert_eq!(log.0, [ProcessRequest::SyncBlock]);
646 assert_eq!(waiters(h), 1);
647 log.0.clear();
648 assert_eq!(
649 put(h, 2_011, msg(7), &mut log.sink()),
650 Ok(PutResult::Stored)
651 );
652 assert_eq!(log.0, [ProcessRequest::SyncWake(2_010)]);
653 assert_eq!(num(h), 0, "the get took the message");
654 match get(h, 2_010, FP, false, &mut log.sink()) {
655 Got::Message(m) => assert_eq!(value(&m), 7),
656 other => panic!("second phase must deliver: {other:?}"),
657 }
658 assert_eq!(get(h, 2_010, FP, false, &mut log.sink()), Got::Blocked);
660 }
661
662 #[test]
663 fn a_get_that_finds_a_message_completes_and_its_second_phase_is_a_no_op() {
664 let h = mbx(0, 18_005);
665 let mut log = Log::default();
666 put(h, 2_020, msg(5), &mut log.sink()).unwrap();
667 match get(h, 2_021, FP, false, &mut log.sink()) {
668 Got::Message(m) => assert_eq!(value(&m), 5),
669 other => panic!("{other:?}"),
670 }
671 assert_eq!(get(h, 2_021, FP, false, &mut log.sink()), Got::Nothing);
672 assert!(log.0.is_empty());
673 assert_eq!(num(h), 0);
674 }
675
676 #[test]
677 fn waiters_are_served_in_arrival_order() {
678 let h = mbx(0, 18_006);
679 let mut log = Log::default();
680 for p in [2_030, 2_031, 2_032] {
681 assert_eq!(get(h, p, FP, false, &mut log.sink()), Got::Blocked);
682 }
683 log.0.clear();
684 put(h, 2_039, msg(1), &mut log.sink()).unwrap();
685 assert_eq!(log.0, [ProcessRequest::SyncWake(2_030)]);
686 put(h, 2_039, msg(2), &mut log.sink()).unwrap();
687 assert_eq!(
688 log.0,
689 [
690 ProcessRequest::SyncWake(2_030),
691 ProcessRequest::SyncWake(2_031)
692 ]
693 );
694 assert_eq!(waiters(h), 1);
695 }
696
697 #[test]
698 fn one_message_wakes_every_peeker_and_the_first_getter() {
699 let h = mbx(0, 18_007);
700 let mut log = Log::default();
701 assert_eq!(get(h, 2_040, FP, true, &mut log.sink()), Got::Blocked);
702 assert_eq!(get(h, 2_041, FP, true, &mut log.sink()), Got::Blocked);
703 assert_eq!(get(h, 2_042, FP, false, &mut log.sink()), Got::Blocked);
704 assert_eq!(get(h, 2_043, FP, true, &mut log.sink()), Got::Blocked);
705 log.0.clear();
706 put(h, 2_049, msg(9), &mut log.sink()).unwrap();
707 assert_eq!(
708 log.0,
709 [
710 ProcessRequest::SyncWake(2_040),
711 ProcessRequest::SyncWake(2_041),
712 ProcessRequest::SyncWake(2_042)
713 ],
714 "two peeks copy, the get removes it, the later peek stays blocked"
715 );
716 assert_eq!(num(h), 0);
717 assert_eq!(waiters(h), 1);
718 for p in [2_040, 2_041, 2_042] {
719 match get(h, p, FP, p == 2_040 || p == 2_041, &mut log.sink()) {
720 Got::Message(m) => assert_eq!(value(&m), 9),
721 other => panic!("process {p}: {other:?}"),
722 }
723 }
724 }
725
726 #[test]
727 fn peek_leaves_the_message_in_the_queue() {
728 let h = mbx(0, 18_008);
729 let mut log = Log::default();
730 put(h, 2_050, msg(4), &mut log.sink()).unwrap();
731 let Taken::Got(m) = try_take(h, FP, true, &mut log.sink()) else {
732 panic!("a message was queued");
733 };
734 assert_eq!(value(&m), 4);
735 assert_eq!(num(h), 1);
736 }
737
738 #[test]
739 fn a_put_on_a_full_mailbox_blocks_until_a_get_makes_room() {
740 let h = mbx(1, 18_009);
741 let mut log = Log::default();
742 put(h, 2_060, msg(1), &mut log.sink()).unwrap();
743 assert_eq!(
744 put(h, 2_061, msg(2), &mut log.sink()),
745 Ok(PutResult::Blocked)
746 );
747 assert_eq!(log.0, [ProcessRequest::SyncBlock]);
748 assert_eq!(num(h), 1, "the blocked message is not queued yet");
749 log.0.clear();
750 let Taken::Got(m) = try_take(h, FP, false, &mut log.sink()) else {
751 panic!("a message was queued");
752 };
753 assert_eq!(value(&m), 1);
754 assert_eq!(log.0, [ProcessRequest::SyncWake(2_061)]);
755 assert_eq!(num(h), 1, "the blocked putter's message moved in");
756 let Taken::Got(m) = try_take(h, FP, false, &mut log.sink()) else {
757 panic!("a message was queued");
758 };
759 assert_eq!(value(&m), 2);
760 }
761
762 #[test]
763 fn a_blocked_putter_is_not_overtaken_by_try_put() {
764 let h = mbx(1, 18_010);
765 let mut log = Log::default();
766 put(h, 2_070, msg(1), &mut log.sink()).unwrap();
767 put(h, 2_071, msg(2), &mut log.sink()).unwrap();
768 try_take(h, FP, false, &mut log.sink());
769 assert!(
770 try_put(h, msg(3), &mut log.sink()).is_err(),
771 "the freed slot went to the blocked putter"
772 );
773 }
774
775 #[test]
776 fn a_type_mismatch_takes_nothing() {
777 let h = mbx(0, 18_011);
778 let mut log = Log::default();
779 put(h, 2_080, msg(1), &mut log.sink()).unwrap();
780 assert_eq!(
781 try_take(h, OTHER_FP, false, &mut log.sink()),
782 Taken::Mismatch
783 );
784 assert_eq!(
785 try_take(h, OTHER_FP, true, &mut log.sink()),
786 Taken::Mismatch
787 );
788 assert_eq!(
789 get(h, 2_081, OTHER_FP, false, &mut log.sink()),
790 Got::Mismatch
791 );
792 assert_eq!(num(h), 1, "the message is still there");
793 let e = mbx(0, 18_012);
796 assert_eq!(
797 get(e, 2_082, OTHER_FP, false, &mut log.sink()),
798 Got::Blocked
799 );
800 assert_eq!(get(e, 2_083, FP, false, &mut log.sink()), Got::Blocked);
801 log.0.clear();
802 put(e, 2_084, msg(8), &mut log.sink()).unwrap();
803 assert_eq!(
804 log.0,
805 [
806 ProcessRequest::SyncWake(2_082),
807 ProcessRequest::SyncWake(2_083)
808 ]
809 );
810 assert_eq!(
811 get(e, 2_082, OTHER_FP, false, &mut log.sink()),
812 Got::Mismatch
813 );
814 assert!(matches!(
815 get(e, 2_083, FP, false, &mut log.sink()),
816 Got::Message(_)
817 ));
818 }
819
820 fn obj_msg(h: u64) -> Message {
821 Message {
822 words: vec![h].into_boxed_slice(),
823 fingerprint: FP,
824 obj: true,
825 coll: false,
826 }
827 }
828
829 #[test]
830 fn a_class_message_owns_one_reference_until_it_is_taken() {
831 let h = mbx(0, 18_013);
832 let payload = objects::alloc(ClassTemplateId(18_014), 1);
833 let mut log = Log::default();
834 put(h, 2_090, obj_msg(payload), &mut log.sink()).unwrap();
835 assert_eq!(objects::refcount(payload), Some(1), "the message holds it");
836 let Taken::Got(copy) = try_take(h, FP, true, &mut log.sink()) else {
837 panic!("a message was queued");
838 };
839 assert_eq!(
840 objects::refcount(payload),
841 Some(2),
842 "a peeked copy owns its own"
843 );
844 copy.discard();
845 let Taken::Got(taken) = try_take(h, FP, false, &mut log.sink()) else {
846 panic!("a message was queued");
847 };
848 assert_eq!(
849 objects::refcount(payload),
850 Some(1),
851 "taking transfers the queue's reference"
852 );
853 taken.discard();
854 assert_eq!(objects::refcount(payload), None, "the last holder freed it");
855 }
856
857 #[test]
858 fn cancelling_a_blocked_putter_releases_its_message_and_the_mailbox() {
859 let h = mbx(1, 18_015);
860 let payload = objects::alloc(ClassTemplateId(18_016), 1);
861 let mut log = Log::default();
862 put(h, 2_100, msg(1), &mut log.sink()).unwrap();
863 assert_eq!(
864 put(h, 2_101, obj_msg(payload), &mut log.sink()),
865 Ok(PutResult::Blocked)
866 );
867 assert_eq!(objects::refcount(h), Some(2));
868 cancel(2_101);
869 assert_eq!(objects::refcount(payload), None, "its message was dropped");
870 assert_eq!(objects::refcount(h), Some(1));
871 assert_eq!(waiters(h), 0);
872 cancel(2_101);
874 }
875
876 #[test]
877 fn cancelling_a_blocked_getter_removes_it_and_a_woken_one_loses_its_message() {
878 let h = mbx(0, 18_017);
879 let mut log = Log::default();
880 assert_eq!(get(h, 2_110, FP, false, &mut log.sink()), Got::Blocked);
881 cancel(2_110);
882 assert_eq!(waiters(h), 0);
883 assert_eq!(objects::refcount(h), Some(1));
884 put(h, 2_111, msg(1), &mut log.sink()).unwrap();
885 assert_eq!(num(h), 1, "nobody is waiting any more");
886
887 let payload = objects::alloc(ClassTemplateId(18_018), 1);
888 let e = mbx(0, 18_019);
889 assert_eq!(get(e, 2_112, FP, false, &mut log.sink()), Got::Blocked);
890 put(e, 2_113, obj_msg(payload), &mut log.sink()).unwrap();
891 cancel(2_112);
892 assert_eq!(
893 objects::refcount(payload),
894 None,
895 "the lost message was freed"
896 );
897 }
898
899 #[test]
900 fn a_waiter_keeps_its_mailbox_alive_until_it_leaves() {
901 let h = mbx(0, 18_020);
902 let mut log = Log::default();
903 assert_eq!(get(h, 2_120, FP, false, &mut log.sink()), Got::Blocked);
904 assert_eq!(objects::refcount(h), Some(2));
905 put(h, 2_121, msg(1), &mut log.sink()).unwrap();
906 assert_eq!(objects::refcount(h), Some(1), "the woken waiter let go");
907 }
908
909 #[test]
910 fn a_non_mailbox_handle_stores_and_delivers_nothing() {
911 let mut log = Log::default();
912 assert!(!init(0, 0));
913 assert_eq!(num(0), 0);
914 assert_eq!(put(0, 2_130, msg(1), &mut log.sink()), Err(msg(1)));
915 assert!(try_put(0, msg(1), &mut log.sink()).is_err());
916 assert_eq!(try_take(0, FP, false, &mut log.sink()), Taken::Empty);
917 assert_eq!(get(0, 2_131, FP, false, &mut log.sink()), Got::NoMailbox);
920 assert_eq!(get(0, 2_131, FP, false, &mut log.sink()), Got::Nothing);
921 assert!(log.0.is_empty());
922 }
923}