Skip to main content

vmo_fifo/
lib.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5mod ring_allocator;
6pub use ring_allocator::AllocationToken;
7use ring_allocator::RingAllocator;
8
9mod signal;
10use fuchsia_sync::Mutex;
11pub use signal::SIG_SHUTDOWN;
12use signal::{
13    EventSignal, SIG_DATA_AVAILABLE_0, SIG_DATA_AVAILABLE_1, SIG_SPACE_AVAILABLE_0,
14    SIG_SPACE_AVAILABLE_1,
15};
16use std::fmt::{Debug, Formatter};
17use std::sync::atomic::{AtomicU64, Ordering};
18use storage_ptr_slice::{MutPtrByteSlice, PtrByteSlice};
19use zerocopy::{FromBytes, IntoBytes, KnownLayout};
20
21struct MappedVmo {
22    vmo: zx::Vmo,
23    addr: usize,
24    size: usize,
25}
26
27impl MappedVmo {
28    fn new(vmo: zx::Vmo) -> Result<Self, zx::Status> {
29        let size = vmo.get_size()? as usize;
30        let addr = fuchsia_runtime::vmar_root_self().map(
31            0,
32            &vmo,
33            0,
34            size,
35            zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE,
36        )?;
37        Ok(Self { vmo, addr, size })
38    }
39
40    fn vmo(&self) -> &zx::Vmo {
41        &self.vmo
42    }
43
44    fn addr(&self) -> usize {
45        self.addr
46    }
47
48    fn size(&self) -> usize {
49        self.size
50    }
51}
52
53impl Drop for MappedVmo {
54    fn drop(&mut self) {
55        // SAFETY: We are dropping the owner of the mapping, invalidating the address range.
56        unsafe {
57            // Unmap the VMO from the VMAR.
58            let _ = fuchsia_runtime::vmar_root_self().unmap(self.addr, self.size);
59        }
60    }
61}
62
63// A 64-bit word that packs a 63-bit sequential index in bits 0..62 and a waiter flag in bit 63.
64#[repr(transparent)]
65#[derive(Copy, Clone)]
66struct State(u64);
67
68impl State {
69    // The most significant bit. When set, indicates a peer is blocked waiting on this head.
70    const HAS_WAITER: u64 = 1 << 63;
71    // The lower 63 bits which holds the index of the next item to be written/read.
72    const INDEX_MASK: u64 = !Self::HAS_WAITER;
73
74    fn index(self) -> u64 {
75        self.0 & Self::INDEX_MASK
76    }
77
78    fn has_waiter(self) -> bool {
79        (self.0 & Self::HAS_WAITER) != 0
80    }
81
82    fn with_waiter(self) -> State {
83        State(self.0 | Self::HAS_WAITER)
84    }
85
86    fn clear_waiter_and_increment(self) -> State {
87        State(((self.0 & Self::INDEX_MASK) + 1) & Self::INDEX_MASK)
88    }
89}
90
91// The header structure at the beginning of the VMO in `SharedQueue`.
92//
93// This metadata is shared between the writer and reader to track the state of the queue.
94#[repr(C)]
95#[derive(FromBytes, IntoBytes, KnownLayout)]
96struct QueueHeader {
97    // Bits 0-62: The index where the writer will write the next item.
98    // Bit 63: Set if the reader is asleep and waiting for data.
99    write_head: u64,
100    // Bits 0-62: The index where the reader will read the next item.
101    // Bit 63: Set if the writer is asleep and waiting for space.
102    read_head: u64,
103}
104
105// Offset in bytes from the start of the VMO where the slots region begins.
106// We allocate 64 bytes for the header to align the slots to a typical cache line boundary.
107const HEADER_SIZE: usize = 64;
108
109// A generic VMO-backed FIFO queue.
110//
111// **Note**: This queue is Single-Producer Single-Consumer.
112//
113// The VMO is conceptually divided into three regions:
114// 1. **Header**: Contains the read and write indices (and peer asleep flags).
115// 2. **Slots**: An array of `capacity` items of type `T`.
116// 3. **Payload Region**: Any remaining space in the VMO after the slots. This region is
117//     page-aligned and can be used to store data referenced by the queue entries.
118struct SharedQueue<T> {
119    mapping: MappedVmo,
120    header: *mut QueueHeader,
121    // Pointer to the start of the data region where queue items are stored.
122    slots: *mut T,
123    // The maximum number of items the queue can hold.
124    // Must be a power of two: The queue index resets to zero at 2^63. The capacity must be a power
125    // of two so that the overflow reset aligns with the modulo arithmetic boundary used for slot
126    // calculation (`index % capacity`).
127    capacity: u32,
128
129    // Used by the consumer to wait for and by the producer to signal that the queue is non-empty.
130    data_available: EventSignal,
131    // Used by the producer to wait for and by the consumer to signal that space is available.
132    space_available: EventSignal,
133
134    _phantom: std::marker::PhantomData<T>,
135}
136
137// SAFETY: `SharedQueue` contains raw pointers to the VMO which strips `Send`/`Sync` by default. It
138// is safe to transfer ownership (`Send`) because concurrent manipulations of the pointers are
139// strictly synchronized via `AtomicU64`. It is safe to share by reference (`Sync`) because it
140// exposes no `&self` methods that allow non-atomic mutation. We propagate the traits to `T` to
141// ensure the underlying payload is not structurally thread-unsafe.
142unsafe impl<T: Send> Send for SharedQueue<T> {}
143unsafe impl<T: Sync> Sync for SharedQueue<T> {}
144
145impl<T> SharedQueue<T>
146where
147    T: FromBytes + IntoBytes + KnownLayout + Copy,
148{
149    // Creates a new `SharedQueue` backed by the provided `vmo` with the given `capacity`.
150    //
151    // `capacity` specifies the maximum number of items the queue can hold. It must be a power of
152    // two to ensure correct index wrap-around behavior.
153    fn new(vmo: zx::Vmo, capacity: u32) -> Result<Self, zx::Status> {
154        // Capacity must be a power of two to ensure smooth index wrap-around.
155        if !capacity.is_power_of_two() {
156            return Err(zx::Status::INVALID_ARGS);
157        }
158
159        // Map the VMO to allow direct memory access to the queue header and slots.
160        let mapping = MappedVmo::new(vmo)?;
161
162        // Ensure the VMO is large enough.
163        let required_size = (capacity as usize)
164            .checked_mul(std::mem::size_of::<T>())
165            .and_then(|val| val.checked_add(HEADER_SIZE))
166            .ok_or(zx::Status::INVALID_ARGS)?;
167        if mapping.size < required_size {
168            return Err(zx::Status::BUFFER_TOO_SMALL);
169        }
170
171        let header = mapping.addr as *mut QueueHeader;
172        let slots = (mapping.addr + HEADER_SIZE) as *mut T;
173
174        Ok(Self {
175            mapping,
176            header,
177            slots,
178            capacity,
179            data_available: EventSignal::new(SIG_DATA_AVAILABLE_0, SIG_DATA_AVAILABLE_1),
180            space_available: EventSignal::new(SIG_SPACE_AVAILABLE_0, SIG_SPACE_AVAILABLE_1),
181            _phantom: std::marker::PhantomData,
182        })
183    }
184
185    fn vmo(&self) -> &zx::Vmo {
186        self.mapping.vmo()
187    }
188
189    fn capacity(&self) -> u32 {
190        self.capacity
191    }
192
193    fn addr(&self) -> usize {
194        self.mapping.addr()
195    }
196
197    fn vmo_size(&self) -> usize {
198        self.mapping.size()
199    }
200
201    // Returns the byte offset where the payload region starts (the region of the VMO immediately
202    // following the queue entries region, rounded up to the nearest page).
203    fn payload_region_offset(&self) -> usize {
204        let raw_offset = HEADER_SIZE + self.capacity as usize * std::mem::size_of::<T>();
205        let page_size = zx::system_get_page_size() as usize;
206        (raw_offset + page_size - 1) & !(page_size - 1)
207    }
208
209    fn write_head_atomic(&self) -> &AtomicU64 {
210        // SAFETY: `self.header` points to a valid memory-mapped VMO region of at least HEADER_SIZE
211        // bytes. We use `addr_of_mut!` and `AtomicU64::from_ptr` to avoid creating a Rust reference
212        // (`&QueueHeader`) to the entire struct in untrusted shared memory, preventing undefined
213        // behavior from concurrent modifications or aliasing violations.
214        unsafe {
215            let ptr = std::ptr::addr_of_mut!((*self.header).write_head);
216            AtomicU64::from_ptr(ptr)
217        }
218    }
219
220    fn read_head_atomic(&self) -> &AtomicU64 {
221        // SAFETY: `self.header` points to a valid memory-mapped VMO region of at least HEADER_SIZE.
222        unsafe {
223            let ptr = std::ptr::addr_of_mut!((*self.header).read_head);
224            AtomicU64::from_ptr(ptr)
225        }
226    }
227
228    fn load_write_head(&self, ordering: Ordering) -> State {
229        State(self.write_head_atomic().load(ordering))
230    }
231
232    fn load_read_head(&self, ordering: Ordering) -> State {
233        State(self.read_head_atomic().load(ordering))
234    }
235
236    fn write_index(&self) -> u64 {
237        self.load_write_head(Ordering::Acquire).index()
238    }
239
240    fn read_index(&self) -> u64 {
241        self.load_read_head(Ordering::Acquire).index()
242    }
243
244    fn is_full_at(&self, write_idx: u64, read_idx: u64) -> bool {
245        (write_idx.wrapping_sub(read_idx) & State::INDEX_MASK) >= self.capacity as u64
246    }
247
248    fn is_full(&self) -> bool {
249        self.is_full_at(self.write_index(), self.read_index())
250    }
251
252    fn is_empty(&self) -> bool {
253        self.load_write_head(Ordering::Acquire).index()
254            == self.load_read_head(Ordering::Acquire).index()
255    }
256
257    fn get_slot_ptr(&self, index: u64) -> *mut T {
258        let slot_idx = ((index & State::INDEX_MASK) % self.capacity as u64) as isize;
259        // SAFETY: `slot_idx` is calculated using modulo `capacity`, guaranteeing it stays within
260        // the validated memory bounds of `self.slots`.
261        unsafe { self.slots.offset(slot_idx) }
262    }
263}
264
265impl<T> SharedQueue<T> {
266    // Trigger shutdown, waking up all waiters.
267    fn shutdown(&self) -> Result<(), zx::Status> {
268        self.mapping.vmo().signal(zx::Signals::empty(), SIG_SHUTDOWN)?;
269        Ok(())
270    }
271}
272
273// The core of a Sender endpoint containing helper functions for the `SyncSender` and `AsyncSender`.
274struct SenderInner<T> {
275    inner: SharedQueue<T>,
276    allocator: RingAllocator,
277}
278impl<T: FromBytes + IntoBytes + KnownLayout + Copy> SenderInner<T> {
279    // Creates a new sender endpoint mapping the provided `vmo`.
280    //
281    // * `vmo` - The shared mapping destination.
282    // * `alignment` - The byte boundaries all payload allocations must adhere to. Modulo padding
283    //                 will be applied so that all payloads begin aligned to this size. Must be a
284    //                 power of two.
285    // * `capacity` - Maximum queue node capacity.
286    fn new(vmo: zx::Vmo, alignment: usize, capacity: u32) -> Result<Self, zx::Status> {
287        let inner = SharedQueue::new(vmo, capacity)?;
288        let payload_size = inner.vmo_size().saturating_sub(inner.payload_region_offset());
289        let allocator = RingAllocator::new(payload_size, inner.capacity() as usize, alignment);
290        Ok(Self { inner, allocator })
291    }
292
293    fn is_full(&self) -> bool {
294        self.inner.is_full()
295    }
296
297    fn capacity(&self) -> u32 {
298        self.inner.capacity()
299    }
300
301    fn index(&self) -> u64 {
302        self.inner.write_index()
303    }
304
305    fn vmo(&self) -> &zx::Vmo {
306        self.inner.vmo()
307    }
308
309    // Attempts to reserve the next available slot in the queue.
310    //
311    // If there is available capacity, returns `Some(write_index)` to indicate the reservation was
312    // successful. If the queue is full, asserts the `WAITER` flag on the reader state and returns
313    // `None`, indicating the caller must wait for the reader to make space available.
314    fn try_reserve_slot(
315        &mut self,
316        curr_write_head: State,
317        curr_read_head: &mut State,
318    ) -> Option<u64> {
319        let write_idx = curr_write_head.index();
320        loop {
321            if self.inner.is_full_at(write_idx, curr_read_head.index()) {
322                // Inform the reader that we are waiting for space.
323                let next_read_head = curr_read_head.with_waiter();
324                match self.inner.read_head_atomic().compare_exchange_weak(
325                    curr_read_head.0,
326                    next_read_head.0,
327                    // `Ordering::Relaxed`: We are just publishing a sleep signal.
328                    Ordering::Relaxed,
329                    // `Ordering::Acquire`: exchange failed (reader likely popped an item) - the
330                    // updated state is loaded here. This Acquire barrier ensures the reader fully
331                    // completed reading the popped item before we perform any operations.
332                    Ordering::Acquire,
333                ) {
334                    Ok(_) => return None,
335                    Err(actual) => {
336                        // Start over - flag or index was changed.
337                        *curr_read_head = State(actual);
338                    }
339                }
340            } else {
341                // There is space available.
342                return Some(write_idx);
343            }
344        }
345    }
346
347    // Writes the message into the specified slot. This should only be called after a successful
348    // `try_reserve_slot`.
349    fn push_to_slot(&mut self, msg: T, mut curr_write_head: State) -> Result<u64, zx::Status> {
350        let write_idx = curr_write_head.index();
351
352        // Write the new entry.
353        let slot_ptr = self.inner.get_slot_ptr(write_idx);
354        // SAFETY: `slot_ptr` was obtained from `get_slot_ptr` which enforces VMO bounds.
355        // Single-producer semantics (`&mut self` on `Sender`) guarantee we solely own `write_idx`
356        // without local data races. `T: IntoBytes` ensures no uninitialized padding is written,
357        // preventing kernel info leaks across the boundary.
358        unsafe {
359            std::ptr::write(slot_ptr, msg);
360        }
361
362        // Increment the new index and wake reader if asleep.
363        loop {
364            let next_write_head = curr_write_head.clear_waiter_and_increment();
365            match self.inner.write_head_atomic().compare_exchange_weak(
366                curr_write_head.0,
367                next_write_head.0,
368                // `Ordering::Release`: ensures the new entry written to the VMO earlier is visible
369                // to the reader before the reader sees this new index.
370                Ordering::Release,
371                // `Ordering::Relaxed`: exchange failed (most likely due to WAITER flag being
372                // modified by the reader - the writer didn't publish anything), so no memory
373                // barriers are needed before retrying.
374                Ordering::Relaxed,
375            ) {
376                Ok(prev_write_head) => {
377                    // Wake the reader if they fell asleep waiting for new items.
378                    if State(prev_write_head).has_waiter() {
379                        let vmo = self.inner.mapping.vmo();
380                        self.inner.data_available.signal(vmo)?;
381                    }
382                    return Ok(write_idx);
383                }
384                Err(actual) => curr_write_head = State(actual),
385            }
386        }
387    }
388
389    fn try_allocate_payload(
390        &mut self,
391        size: usize,
392        curr_read_head: &mut State,
393    ) -> Result<Option<AllocationToken>, zx::Status> {
394        if !self.allocator.is_within_capacity(size) {
395            return Err(zx::Status::NO_MEMORY);
396        }
397        loop {
398            let current_read_index = curr_read_head.index();
399            self.allocator.reclaim_consumed_slots(current_read_index);
400            match self.allocator.allocate(size) {
401                Some(token) => return Ok(Some(token)),
402                None => {
403                    // Inform the reader that we are waiting for space.
404                    let next_read_head = curr_read_head.with_waiter();
405                    match self.inner.read_head_atomic().compare_exchange_weak(
406                        curr_read_head.0,
407                        next_read_head.0,
408                        // `Ordering::Relaxed`: We are just publishing a sleep signal.
409                        Ordering::Relaxed,
410                        // `Ordering::Acquire`: exchange failed (reader likely popped an item).
411                        Ordering::Acquire,
412                    ) {
413                        Ok(_) => return Ok(None),
414                        Err(actual) => {
415                            *curr_read_head = State(actual);
416                        }
417                    }
418                }
419            }
420        }
421    }
422}
423
424impl<T> SenderInner<T> {
425    fn shutdown(&self) -> Result<(), zx::Status> {
426        self.inner.shutdown()
427    }
428}
429
430impl<T> Drop for SenderInner<T> {
431    fn drop(&mut self) {
432        let _ = self.shutdown();
433    }
434}
435
436/// The synchronous sender endpoint for a VMO-backed FIFO queue.
437pub struct SyncSender<T>(SenderInner<T>);
438impl<T: FromBytes + IntoBytes + KnownLayout + Copy> SyncSender<T> {
439    /// Creates a new `SyncSender`.
440    ///
441    /// * `vmo` - The shared VMO used for queue messages and payload buffers.
442    /// * `alignment` - The byte alignment boundaries required for allocations. Modulo padding
443    ///                 is applied so that all payload offsets begin aligned to this size. Must be a
444    ///                 power of two.
445    /// * `queue_capacity` - The maximum number of queue slots.
446    pub fn new(vmo: zx::Vmo, alignment: usize, queue_capacity: u32) -> Result<Self, zx::Status> {
447        SenderInner::new(vmo, alignment, queue_capacity).map(Self)
448    }
449
450    pub fn capacity(&self) -> u32 {
451        self.0.capacity()
452    }
453
454    pub fn is_full(&self) -> bool {
455        self.0.is_full()
456    }
457
458    pub fn index(&self) -> u64 {
459        self.0.index()
460    }
461
462    pub fn vmo(&self) -> &zx::Vmo {
463        self.0.vmo()
464    }
465
466    pub fn shutdown(&self) -> Result<(), zx::Status> {
467        self.0.shutdown()
468    }
469
470    pub fn push(&mut self, msg: T) -> Result<u64, zx::Status> {
471        // `Ordering::Relaxed`: this function is called by the writer which owns the write index, no
472        // synchronization needed to read its own state.
473        let curr_write_head = self.0.inner.load_write_head(Ordering::Relaxed);
474        // `Ordering::Acquire`: guarantees the reader has finished reading the old data before the
475        // writer overwrites the slot with new data.
476        let mut curr_read_head = self.0.inner.load_read_head(Ordering::Acquire);
477
478        loop {
479            // Attempt to reserve the next queue slot. If the queue is full, block and wait for the
480            // reader to signal that space has become available.
481            match self.0.try_reserve_slot(curr_write_head, &mut curr_read_head) {
482                Some(_) => break,
483                None => {
484                    let vmo = self.0.inner.mapping.vmo();
485                    self.0.inner.space_available.wait(vmo, SIG_SHUTDOWN)?;
486                    curr_read_head = self.0.inner.load_read_head(Ordering::Acquire);
487                }
488            }
489        }
490        self.0.push_to_slot(msg, curr_write_head)
491    }
492
493    /// Reserves capacity in the payload region for a payload of `size` bytes.
494    /// Returns a `PayloadBuffer` referencing the payload region so that data can be copied or
495    /// written directly into it. The returned `PayloadBuffer` locks the `Sender` until the buffer
496    /// is either committed or dropped, preventing multiple concurrent allocations.
497    pub fn reserve_payload(&mut self, size: usize) -> Result<PayloadBuffer<'_, T>, zx::Status> {
498        // `Ordering::Acquire`: ensures any data writes made by the consumer in payload space
499        // prior to them updating the reader head is visible here.
500        let mut curr_read_head = self.0.inner.load_read_head(Ordering::Acquire);
501
502        let token = loop {
503            // Attempt to allocate space in the payload region. If no space is available, block
504            // until the reader processes a message and frees its memory block.
505            match self.0.try_allocate_payload(size, &mut curr_read_head)? {
506                Some(token) => break token,
507                None => {
508                    let vmo = self.0.inner.mapping.vmo();
509                    self.0.inner.space_available.wait(vmo, SIG_SHUTDOWN)?;
510                    curr_read_head = self.0.inner.load_read_head(Ordering::Acquire);
511                }
512            }
513        };
514
515        // SAFETY:
516        // 1. `self.0.inner.addr()` is a valid base pointer to a mapped VMO memory space active
517        //    for the entire lifetime of this instance.
518        // 2. The `RingAllocator` math strictly guarantees that `token.offset() + size` fits
519        //    within the allocated payload boundaries, preventing out-of-bounds pointer derivation.
520        // 3. Returning a `MutPtrByteSlice` wrapper instead of a native `&mut [u8]` avoids violating
521        //    Rust's strict aliasing rules for shared memory, thereby preventing Undefined Behavior.
522        unsafe {
523            let abs_offset = self.0.inner.payload_region_offset() + token.offset() as usize;
524            let dest_ptr = (self.0.inner.addr() + abs_offset) as *mut u8;
525            let slice = std::ptr::slice_from_raw_parts_mut(dest_ptr, size);
526            Ok(PayloadBuffer {
527                sender: self,
528                token: Some(token),
529                slice: MutPtrByteSlice::new(slice),
530            })
531        }
532    }
533}
534
535/// The asynchronous sender endpoint for a VMO-backed FIFO queue.
536pub struct AsyncSender<T>(SenderInner<T>);
537impl<T: FromBytes + IntoBytes + KnownLayout + Copy> AsyncSender<T> {
538    /// Creates a new `AsyncSender`.
539    ///
540    /// * `vmo` - The shared VMO used for queue messages and payload buffers.
541    /// * `alignment` - The byte alignment boundaries required for allocations. Modulo padding
542    ///                 is applied so that all payload offsets begin aligned to this size. Must be a
543    ///                 power of two.
544    /// * `queue_capacity` - The maximum number of queue slots.
545    pub fn new(vmo: zx::Vmo, alignment: usize, queue_capacity: u32) -> Result<Self, zx::Status> {
546        SenderInner::new(vmo, alignment, queue_capacity).map(Self)
547    }
548
549    /// Returns the maximum number of items the queue can hold.
550    pub fn capacity(&self) -> u32 {
551        self.0.capacity()
552    }
553
554    pub fn is_full(&self) -> bool {
555        self.0.is_full()
556    }
557
558    pub fn vmo(&self) -> &zx::Vmo {
559        self.0.vmo()
560    }
561
562    /// Pushes a standalone message onto the queue, yielding to the executor if the queue is full.
563    pub async fn push(&mut self, msg: T) -> Result<u64, zx::Status> {
564        // `Ordering::Relaxed`: this function is called by the writer which owns the write index, no
565        // thread synchronization is needed for reading it.
566        let curr_write_head = self.0.inner.load_write_head(Ordering::Relaxed);
567        // `Ordering::Acquire`: guarantees the reader has finished reading the old data before the
568        // writer overwrites it.
569        let mut curr_read_head = self.0.inner.load_read_head(Ordering::Acquire);
570
571        loop {
572            // Attempt to reserve the next queue slot. If the queue is full, yield until the reader
573            // signals that space has become available.
574            match self.0.try_reserve_slot(curr_write_head, &mut curr_read_head) {
575                Some(_) => break,
576                None => {
577                    let vmo = self.0.inner.mapping.vmo();
578                    // Successfully marked writer as asleep. Yield control to the async executor
579                    // so the current thread can do other work until space becomes available.
580                    self.0.inner.space_available.wait_async(vmo, SIG_SHUTDOWN).await?;
581                    curr_read_head = self.0.inner.load_read_head(Ordering::Acquire);
582                }
583            }
584        }
585
586        self.0.push_to_slot(msg, curr_write_head)
587    }
588
589    /// Reserves a payload buffer in the VMO. Yields to the executor if there is insufficient
590    /// payload capacity available. Returns an `AsyncPayloadBuffer` that commits on success
591    /// or rolls back on drop.
592    pub async fn reserve_payload(
593        &mut self,
594        size: usize,
595    ) -> Result<AsyncPayloadBuffer<'_, T>, zx::Status> {
596        // `Ordering::Acquire`: ensures any data writes made by the consumer in payload space
597        // prior to them updating the reader head is visible here.
598        let mut curr_read_head = self.0.inner.load_read_head(Ordering::Acquire);
599        let token = loop {
600            // Attempt to allocate space in the payload region. If no contiguous space is available,
601            // yield until the reader signals that space is available.
602            match self.0.try_allocate_payload(size, &mut curr_read_head)? {
603                Some(token) => break token,
604                None => {
605                    let vmo = self.0.inner.mapping.vmo();
606                    self.0.inner.space_available.wait_async(vmo, SIG_SHUTDOWN).await?;
607                    curr_read_head = self.0.inner.load_read_head(Ordering::Acquire);
608                }
609            }
610        };
611
612        // SAFETY:
613        // 1. `self.0.inner.addr()` is a valid base pointer to a mapped VMO memory space active
614        //    for the entire lifetime of this instance.
615        // 2. The `RingAllocator` math strictly guarantees that `token.offset() + size` fits
616        //    within the allocated payload boundaries, preventing out-of-bounds pointer derivation.
617        // 3. Returning a `MutPtrByteSlice` wrapper instead of a native `&mut [u8]` avoids violating
618        //    Rust's strict aliasing rules for shared memory, thereby preventing Undefined Behavior.
619        let slice = unsafe {
620            let abs_offset = self.0.inner.payload_region_offset() + token.offset() as usize;
621            let dest_ptr = (self.0.inner.addr() + abs_offset) as *mut u8;
622            MutPtrByteSlice::new(std::ptr::slice_from_raw_parts_mut(dest_ptr, size))
623        };
624        Ok(AsyncPayloadBuffer { sender: self, token: Some(token), slice })
625    }
626}
627
628/// An RAII buffer for committing a payload to the FIFO. Dropping this buffer automatically rolls
629/// back the payload allocation.
630pub struct PayloadBuffer<'a, T: FromBytes + IntoBytes + KnownLayout + Copy> {
631    // Holds an exclusive mutable borrow on the `Sender` to statically prevent the caller from
632    // reserving a second payload before this one is either committed or dropped.
633    sender: &'a mut SyncSender<T>,
634    token: Option<AllocationToken>,
635    slice: MutPtrByteSlice<'a>,
636}
637
638impl<'a, T: FromBytes + IntoBytes + KnownLayout + Copy> PayloadBuffer<'a, T> {
639    /// Returns a mutable slice to the payload data.
640    pub fn data(&mut self) -> &mut MutPtrByteSlice<'a> {
641        &mut self.slice
642    }
643
644    /// Returns the physical byte offset of the payload in the VMO.
645    pub fn offset(&self) -> u32 {
646        self.token.as_ref().unwrap().offset()
647    }
648
649    /// Pushes the given message to the FIFO and commits this payload allocation, linking the
650    /// payload to the queue slot.
651    pub fn commit(mut self, msg: T) -> Result<(), zx::Status> {
652        let token = self.token.take().unwrap();
653        match self.sender.push(msg) {
654            Ok(slot) => {
655                self.sender.0.allocator.commit_allocation_to_slot(slot, token);
656                Ok(())
657            }
658            Err(e) => {
659                self.sender.0.allocator.cancel_allocation(token);
660                Err(e)
661            }
662        }
663    }
664}
665
666impl<'a, T: FromBytes + IntoBytes + KnownLayout + Copy> Drop for PayloadBuffer<'a, T> {
667    fn drop(&mut self) {
668        if let Some(token) = self.token.take() {
669            self.sender.0.allocator.cancel_allocation(token);
670        }
671    }
672}
673
674/// An asynchronous version of `PayloadBuffer` that yields when committing if the queue is full.
675/// Dropping this buffer automatically rolls back the payload allocation if not committed.
676pub struct AsyncPayloadBuffer<'a, T: FromBytes + IntoBytes + KnownLayout + Copy> {
677    sender: &'a mut AsyncSender<T>,
678    token: Option<AllocationToken>,
679    slice: MutPtrByteSlice<'a>,
680}
681
682impl<'a, T: FromBytes + IntoBytes + KnownLayout + Copy> AsyncPayloadBuffer<'a, T> {
683    /// Returns a mutable slice to the reserved payload bytes.
684    pub fn data(&mut self) -> &mut MutPtrByteSlice<'a> {
685        &mut self.slice
686    }
687
688    /// Returns the absolute VMO offset where this payload reservation begins.
689    pub fn offset(&self) -> u32 {
690        self.token.as_ref().unwrap().offset()
691    }
692
693    /// Pushes the given message to the FIFO and commits this payload allocation, linking the
694    /// payload to the queue slot. Yielding if the queue is full.
695    pub async fn commit(mut self, msg: T) -> Result<(), zx::Status> {
696        let token = self.token.take().unwrap();
697        match self.sender.push(msg).await {
698            Ok(slot) => {
699                self.sender.0.allocator.commit_allocation_to_slot(slot, token);
700                Ok(())
701            }
702            Err(e) => {
703                self.sender.0.allocator.cancel_allocation(token);
704                Err(e)
705            }
706        }
707    }
708}
709
710impl<'a, T: FromBytes + IntoBytes + KnownLayout + Copy> Drop for AsyncPayloadBuffer<'a, T> {
711    fn drop(&mut self) {
712        if let Some(token) = self.token.take() {
713            self.sender.0.allocator.cancel_allocation(token);
714        }
715    }
716}
717
718const PORT_KEY_SIGNAL: u64 = 1;
719const PORT_KEY_WAKE: u64 = 2;
720
721struct ReceiverState {
722    claim_index: u64,
723    committed_read_index: u64,
724    last_claim_write_index: u64,
725    completed_slots: Box<[bool]>,
726    data_available: EventSignal,
727    space_available: EventSignal,
728    wait_async_armed: bool,
729    shutting_down: bool,
730    sleeping_workers: usize,
731    active_workers: usize,
732}
733
734impl ReceiverState {
735    // Creates a new `ReceiverState` with the given slot `capacity` and starting `initial_index`.
736    fn new(capacity: u32, initial_index: u64) -> Self {
737        Self {
738            claim_index: initial_index,
739            committed_read_index: initial_index,
740            last_claim_write_index: initial_index,
741            completed_slots: vec![false; capacity as usize].into_boxed_slice(),
742            data_available: EventSignal::new(SIG_DATA_AVAILABLE_0, SIG_DATA_AVAILABLE_1),
743            space_available: EventSignal::new(SIG_SPACE_AVAILABLE_0, SIG_SPACE_AVAILABLE_1),
744            wait_async_armed: false,
745            shutting_down: false,
746            sleeping_workers: 0,
747            active_workers: 0,
748        }
749    }
750}
751
752/// A receiver endpoint for a VMO-backed FIFO queue.
753pub struct Receiver<T> {
754    inner: SharedQueue<T>,
755    port: zx::Port,
756    state: Mutex<ReceiverState>,
757}
758
759pub struct Message<'a, T: FromBytes + IntoBytes + KnownLayout + Copy> {
760    receiver: &'a Receiver<T>,
761    slot_index: u64,
762    claimed: bool,
763    popped: bool,
764    item: T,
765}
766
767impl<'a, T: FromBytes + IntoBytes + KnownLayout + Copy> std::ops::Deref for Message<'a, T> {
768    type Target = T;
769
770    fn deref(&self) -> &Self::Target {
771        &self.item
772    }
773}
774
775impl<'a, T: FromBytes + IntoBytes + KnownLayout + Copy + Debug> Debug for Message<'a, T> {
776    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
777        Debug::fmt(&self.item, f)
778    }
779}
780
781impl<'a, T: FromBytes + IntoBytes + KnownLayout + Copy> Message<'a, T> {
782    pub fn payload_region_offset(&self) -> usize {
783        self.receiver.payload_region_offset()
784    }
785
786    pub fn payload_slice(&self, vmo_offset: u32, len: u32) -> PtrByteSlice<'_> {
787        self.receiver.payload_slice(vmo_offset, len)
788    }
789
790    /// Returns a raw mutable slice pointer to the specified payload region in the shared VMO.
791    ///
792    /// # Panics
793    ///
794    /// Panics if `vmo_offset + len` exceeds the bounds of the VMO.
795    pub fn payload_mut_slice(&mut self, vmo_offset: u32, len: u32) -> MutPtrByteSlice<'_> {
796        // SAFETY: Each `Message` represents an exclusive claim over its payload range until
797        // `pop()` retires the slot.
798        unsafe { self.receiver.payload_mut_slice_unchecked(vmo_offset, len) }
799    }
800
801    /// Commit the read, freeing the slot.
802    pub fn pop(mut self) -> Result<(), zx::Status> {
803        self.pop_internal()
804    }
805
806    // Marks this message as popped and retires `self.slot_index` in the receiver.
807    fn pop_internal(&mut self) -> Result<(), zx::Status> {
808        if self.popped {
809            return Ok(());
810        }
811        self.popped = true;
812        self.receiver.retire_slot(self.slot_index, self.claimed)
813    }
814}
815
816impl<'a, T: FromBytes + IntoBytes + KnownLayout + Copy> Drop for Message<'a, T> {
817    fn drop(&mut self) {
818        if self.claimed && !self.popped {
819            let _ = self.pop_internal();
820        }
821    }
822}
823
824impl<T: FromBytes + IntoBytes + KnownLayout + Copy> Receiver<T> {
825    pub fn new(vmo: zx::Vmo, capacity: u32) -> Result<Self, zx::Status> {
826        let inner = SharedQueue::new(vmo, capacity)?;
827        let port = zx::Port::create();
828        Ok(Self { inner, port, state: Mutex::new(ReceiverState::new(capacity, 0)) })
829    }
830
831    // Creates a Receiver that initializes its cached index from the provided VMO. This is solely
832    // used for tests. Receiver should normally not support a populated VMO.
833    #[cfg(test)]
834    fn new_from_populated_vmo(vmo: zx::Vmo, capacity: u32) -> Result<Self, zx::Status> {
835        let inner = SharedQueue::new(vmo, capacity)?;
836        let initial_index = inner.read_index();
837        let port = zx::Port::create();
838        Ok(Self { inner, port, state: Mutex::new(ReceiverState::new(capacity, initial_index)) })
839    }
840
841    pub fn is_empty(&self) -> bool {
842        self.inner.is_empty()
843    }
844
845    pub fn capacity(&self) -> u32 {
846        self.inner.capacity()
847    }
848
849    pub fn index(&self) -> u64 {
850        self.inner.read_index()
851    }
852
853    pub fn vmo(&self) -> &zx::Vmo {
854        self.inner.vmo()
855    }
856
857    pub fn payload_region_offset(&self) -> usize {
858        self.inner.payload_region_offset()
859    }
860
861    /// Peek at the next message without incrementing the read index.
862    pub fn peek(&mut self) -> Result<Message<'_, T>, zx::Status> {
863        self.next_internal(false, |_| ()).map(|(msg, ())| msg)
864    }
865
866    /// Claims the next message from the queue for concurrent processing across a pool of threads.
867    pub fn claim(&self) -> Result<Message<'_, T>, zx::Status> {
868        self.next_internal(true, |_| ()).map(|(msg, ())| msg)
869    }
870
871    /// Claims the next message from the queue and invokes `on_claim` before releasing the queue's
872    /// claim lock, allowing ordered setup steps (such as blob registration) to complete before
873    /// subsequent messages are claimed by other worker threads.
874    pub fn claim_with<F, R>(&self, on_claim: F) -> Result<(Message<'_, T>, R), zx::Status>
875    where
876        F: FnOnce(&mut Message<'_, T>) -> R,
877    {
878        self.next_internal(true, on_claim)
879    }
880
881    /// Notifies the receiver that the current worker thread (after popping its message) is about to
882    /// perform an operation that may block waiting for a subsequent message on this queue. If all
883    /// other workers are sleeping and unread messages were already queued before the waiter flag
884    /// was armed, wakes a sleeping worker to process them.
885    pub fn will_maybe_reenter(&self) {
886        let state = self.state.lock();
887        if state.active_workers == 0
888            && state.sleeping_workers > 0
889            && state.claim_index != state.last_claim_write_index
890        {
891            let _ = self.port.queue(&zx::Packet::from_user_packet(
892                PORT_KEY_WAKE,
893                0,
894                zx::UserPacket::from_u8_array([0; 32]),
895            ));
896        }
897    }
898
899    // Waits for the next available message in the FIFO, optionally advancing `claim_index` and
900    // running `on_claim` while holding `self.state`.
901    fn next_internal<F, R>(
902        &self,
903        claim: bool,
904        on_claim: F,
905    ) -> Result<(Message<'_, T>, R), zx::Status>
906    where
907        F: FnOnce(&mut Message<'_, T>) -> R,
908    {
909        let mut state = self.state.lock();
910        loop {
911            // `Ordering::Acquire`: guarantees the writer has finished writing the new data to the
912            // VMO before the reader attempts to read it.
913            let mut curr_write_head = self.inner.load_write_head(Ordering::Acquire);
914            if state.claim_index != curr_write_head.index() {
915                let slot_index = state.claim_index;
916                let slot_ptr = self.inner.get_slot_ptr(slot_index);
917                // SAFETY: `slot_ptr` enforces VMO bounds. The `Acquire` ordering guarantees the
918                // sender has fully completed writing before we read. Furthermore, `T: FromBytes`
919                // ensures any arbitrary bits provided by the peer safely map to a valid struct.
920                let item = unsafe { std::ptr::read(slot_ptr) };
921                if claim {
922                    state.claim_index = (state.claim_index + 1) & State::INDEX_MASK;
923                    state.active_workers += 1;
924                }
925
926                let mut msg =
927                    Message { receiver: self, slot_index, claimed: claim, popped: false, item };
928                let res = on_claim(&mut msg);
929
930                // If another worker thread is still asleep, re-arm `HAS_WAITER` and `wait_async`
931                // so that if more items are pushed while this thread is busy processing `msg`,
932                // the sender will signal the VMO and wake the sleeping worker.
933                if claim {
934                    while state.sleeping_workers > 0
935                        && !state.wait_async_armed
936                        && !state.shutting_down
937                    {
938                        let asleep_write_head = curr_write_head.with_waiter();
939                        match self.inner.write_head_atomic().compare_exchange_weak(
940                            curr_write_head.0,
941                            asleep_write_head.0,
942                            Ordering::Relaxed,
943                            Ordering::Acquire,
944                        ) {
945                            Ok(_) => {
946                                let vmo = self.inner.mapping.vmo();
947                                let mask = state.data_available.current_mask(SIG_SHUTDOWN);
948                                vmo.wait_async(
949                                    &self.port,
950                                    PORT_KEY_SIGNAL,
951                                    mask,
952                                    zx::WaitAsyncOpts::empty(),
953                                )?;
954                                state.wait_async_armed = true;
955                            }
956                            Err(actual) => curr_write_head = State(actual),
957                        }
958                    }
959                    state.last_claim_write_index = curr_write_head.index();
960                }
961
962                return Ok((msg, res));
963            }
964
965            if state.shutting_down {
966                return Err(zx::Status::CANCELED);
967            }
968
969            // The queue is empty. Inform the writer that we are going to sleep and waiting for a
970            // signal when new items are pushed.
971            if !state.wait_async_armed {
972                let asleep_write_head = curr_write_head.with_waiter();
973                if self
974                    .inner
975                    .write_head_atomic()
976                    .compare_exchange_weak(
977                        curr_write_head.0,
978                        asleep_write_head.0,
979                        // `Ordering::Relaxed`: We are just publishing a sleep signal.
980                        Ordering::Relaxed,
981                        // `Ordering::Acquire`: exchange failed (writer likely pushed an item).
982                        Ordering::Acquire,
983                    )
984                    .is_err()
985                {
986                    continue;
987                }
988
989                let vmo = self.inner.mapping.vmo();
990                let mask = state.data_available.current_mask(SIG_SHUTDOWN);
991                vmo.wait_async(&self.port, PORT_KEY_SIGNAL, mask, zx::WaitAsyncOpts::empty())?;
992                state.wait_async_armed = true;
993            }
994
995            state.sleeping_workers += 1;
996            drop(state);
997
998            let wait_res = self.port.wait(zx::MonotonicInstant::INFINITE);
999            state = self.state.lock();
1000            state.sleeping_workers -= 1;
1001            let packet = wait_res?;
1002
1003            if packet.key() == PORT_KEY_SIGNAL {
1004                state.wait_async_armed = false;
1005                if let zx::PacketContents::SignalOne(signals) = packet.contents() {
1006                    let observed = signals.observed();
1007                    if observed.contains(state.data_available.current_mask(zx::Signals::empty())) {
1008                        state.data_available.advance();
1009                    }
1010                    if observed.contains(SIG_SHUTDOWN) && !state.shutting_down {
1011                        state.shutting_down = true;
1012                        for _ in 0..state.sleeping_workers {
1013                            let _ = self.port.queue(&zx::Packet::from_user_packet(
1014                                PORT_KEY_WAKE,
1015                                0,
1016                                zx::UserPacket::from_u8_array([0; 32]),
1017                            ));
1018                        }
1019                    }
1020                }
1021            }
1022        }
1023    }
1024
1025    // Records `slot_index` as completed and advances the shared `read_head` across all contiguous
1026    // completed slots from `committed_read_index`.
1027    fn retire_slot(&self, slot_index: u64, claimed: bool) -> Result<(), zx::Status> {
1028        let mut state = self.state.lock();
1029        if claimed {
1030            state.active_workers -= 1;
1031        } else if state.claim_index == slot_index {
1032            state.claim_index = (state.claim_index + 1) & State::INDEX_MASK;
1033        }
1034        let cap = self.inner.capacity() as u64;
1035        state.completed_slots[(slot_index % cap) as usize] = true;
1036        let mut advanced = false;
1037        loop {
1038            let committed_slot = (state.committed_read_index % cap) as usize;
1039            if !state.completed_slots[committed_slot] {
1040                break;
1041            }
1042            state.completed_slots[committed_slot] = false;
1043            state.committed_read_index = (state.committed_read_index + 1) & State::INDEX_MASK;
1044            advanced = true;
1045        }
1046
1047        if advanced {
1048            let target_read_index = state.committed_read_index;
1049            let mut curr_read_head = self.inner.load_read_head(Ordering::Relaxed);
1050            loop {
1051                let next_read_head = State(target_read_index);
1052                match self.inner.read_head_atomic().compare_exchange_weak(
1053                    curr_read_head.0,
1054                    next_read_head.0,
1055                    // `Ordering::Release`: ensure the reader has finished reading the old data from
1056                    // the VMO before the writer sees this new index and overwrites the slot.
1057                    Ordering::Release,
1058                    // `Ordering::Relaxed`: swap failed (most likely due to WAITER flag being
1059                    // modified).
1060                    Ordering::Relaxed,
1061                ) {
1062                    Ok(prev_read_head) => {
1063                        // Wake the writer if they fell asleep waiting for a free slot.
1064                        if State(prev_read_head).has_waiter() {
1065                            let vmo = self.inner.mapping.vmo();
1066                            state.space_available.signal(vmo)?;
1067                        }
1068                        break;
1069                    }
1070                    Err(actual) => curr_read_head = State(actual),
1071                }
1072            }
1073        }
1074        Ok(())
1075    }
1076
1077    /// Returns a raw slice pointer to the specified payload region.
1078    ///
1079    /// # Panics
1080    ///
1081    /// Panics if the `vmo_offset + len` exceeds the bounds of the VMO.
1082    // TODO(https://fxbug.dev/530494057): Refactor to encode the payload layout as part of the FIFO
1083    // messaging envelope to safely resolve bounds.
1084    pub fn payload_slice(&self, vmo_offset: u32, len: u32) -> PtrByteSlice<'_> {
1085        // SAFETY:
1086        // 1. `self.inner.addr()` is a valid base pointer to a mapped VMO memory space active
1087        //    for the entire lifetime of this instance.
1088        // 2. The explicit assertion guarantees that `offset + len` physically fits within
1089        //    the bounded VMO boundaries, preventing out-of-bounds pointer derivation.
1090        // 3. Returning a `PtrByteSlice` wrapper instead of a native `&[u8]` avoids violating
1091        //    Rust's strict aliasing rules for shared memory, thereby preventing Undefined Behavior.
1092        unsafe {
1093            let offset = self.inner.payload_region_offset() + vmo_offset as usize;
1094            assert!(offset + len as usize <= self.inner.vmo_size(), "Payload out of bounds");
1095            let dest_ptr = (self.inner.addr() + offset) as *const u8;
1096            let slice = std::ptr::slice_from_raw_parts(dest_ptr, len as usize);
1097            PtrByteSlice::new(slice)
1098        }
1099    }
1100
1101    /// Returns a raw mutable slice pointer to the specified payload region.
1102    ///
1103    /// # Panics
1104    ///
1105    /// Panics if the `vmo_offset + len` exceeds the bounds of the VMO.
1106    pub fn payload_mut_slice(&mut self, vmo_offset: u32, len: u32) -> MutPtrByteSlice<'_> {
1107        // SAFETY: `&mut self` guarantees exclusive access on the receiver side.
1108        unsafe { self.payload_mut_slice_unchecked(vmo_offset, len) }
1109    }
1110
1111    // Returns a raw mutable slice pointer to `[vmo_offset .. vmo_offset + len)` in the payload
1112    // region. The caller must ensure exclusive access to that payload subrange.
1113    unsafe fn payload_mut_slice_unchecked(&self, vmo_offset: u32, len: u32) -> MutPtrByteSlice<'_> {
1114        // SAFETY:
1115        // 1. `self.inner.addr()` is a valid base pointer to a mapped VMO memory space active
1116        //    for the entire lifetime of this instance.
1117        // 2. The explicit assertion guarantees that `offset + len` physically fits within
1118        //    the bounded VMO boundaries, preventing out-of-bounds pointer derivation.
1119        // 3. Returning a `MutPtrByteSlice` wrapper instead of a native `&mut [u8]` avoids violating
1120        //    Rust's strict aliasing rules for shared memory, thereby preventing Undefined Behavior.
1121        unsafe {
1122            let offset = self.inner.payload_region_offset() + vmo_offset as usize;
1123            assert!(offset + len as usize <= self.inner.vmo_size(), "Payload out of bounds");
1124            let dest_ptr = (self.inner.addr() + offset) as *mut u8;
1125            let slice = std::ptr::slice_from_raw_parts_mut(dest_ptr, len as usize);
1126            MutPtrByteSlice::new(slice)
1127        }
1128    }
1129}
1130
1131impl<T> Receiver<T> {
1132    pub fn shutdown(&self) -> Result<(), zx::Status> {
1133        self.inner.shutdown()
1134    }
1135}
1136
1137impl<T> Drop for Receiver<T> {
1138    fn drop(&mut self) {
1139        let _ = self.shutdown();
1140    }
1141}
1142
1143#[cfg(test)]
1144mod tests {
1145    use super::*;
1146    use std::sync::Arc;
1147    use std::thread;
1148    use std::time::Duration;
1149
1150    #[derive(FromBytes, IntoBytes, KnownLayout, Clone, Copy, Debug, PartialEq)]
1151    #[repr(C)]
1152    struct TestMessage {
1153        val: u32,
1154    }
1155
1156    #[fuchsia::test]
1157    fn test_push_pop() {
1158        let vmo = zx::Vmo::create(4096).expect("VMO creation failed");
1159        let vmo_dup =
1160            vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).expect("duplicate_handle failed");
1161
1162        let mut sender = SyncSender::<TestMessage>::new(vmo, 1, 4).expect("Sender new failed");
1163        let mut receiver = Receiver::<TestMessage>::new(vmo_dup, 4).expect("Receiver new failed");
1164
1165        assert!(receiver.is_empty());
1166        assert!(!sender.is_full());
1167
1168        let expected = TestMessage { val: 42 };
1169        sender.push(expected).expect("push failed");
1170
1171        assert!(!receiver.is_empty());
1172
1173        let next_msg = receiver.peek().expect("peek failed");
1174        assert_eq!(*next_msg, expected);
1175
1176        next_msg.pop().expect("pop failed");
1177        assert!(receiver.is_empty());
1178    }
1179
1180    #[fuchsia::test]
1181    fn test_blocking_push_pop() {
1182        let vmo = zx::Vmo::create(4096).expect("VMO creation failed");
1183        let vmo_writer =
1184            vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).expect("VMO duplicate_handle failed");
1185
1186        let mut receiver = Receiver::<TestMessage>::new(vmo, 2).expect("receiver creation failed");
1187        let mut sender =
1188            SyncSender::<TestMessage>::new(vmo_writer, 8, 2).expect("sender creation failed");
1189
1190        let handle = std::thread::spawn(move || {
1191            let msg1 = TestMessage { val: 1 };
1192            let msg2 = TestMessage { val: 2 };
1193            let msg3 = TestMessage { val: 3 };
1194
1195            sender.push(msg1).expect("push failed");
1196            sender.push(msg2).expect("push failed");
1197            // This third push blocks because capacity is 2; it remains blocked until the receiver
1198            // commits a pop.
1199            sender.push(msg3).expect("push failed");
1200        });
1201
1202        // Allow time for the writer thread to fill the queue and fall asleep waiting for space.
1203        thread::sleep(Duration::from_millis(100));
1204
1205        let r1 = receiver.peek().expect("peek failed");
1206        assert_eq!(r1.val, 1);
1207        // Pop frees a slot, waking the blocked sender.
1208        r1.pop().expect("pop failed");
1209
1210        let r2 = receiver.peek().expect("peek failed");
1211        assert_eq!(r2.val, 2);
1212        r2.pop().expect("pop failed");
1213
1214        let r3 = receiver.peek().expect("peek failed");
1215        assert_eq!(r3.val, 3);
1216        r3.pop().expect("pop failed");
1217
1218        handle.join().expect("writer thread panicked");
1219    }
1220
1221    #[fuchsia::test]
1222    fn test_index_wrap_around() {
1223        let vmo = zx::Vmo::create(4096).expect("VMO creation failed");
1224
1225        // Seed the write and read indexes to near INDEX_MASK.
1226        let start_idx = State::INDEX_MASK - 1;
1227        // `write_index` is at byte offset 0
1228        vmo.write(&start_idx.to_ne_bytes(), 0).expect("write failed");
1229        // `read_index` is at byte offset 8
1230        vmo.write(&start_idx.to_ne_bytes(), 8).expect("write failed");
1231
1232        let vmo_dup =
1233            vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).expect("duplicate_handle failed");
1234
1235        let mut sender = SyncSender::<TestMessage>::new(vmo, 8, 4).expect("Sender new failed");
1236        let mut receiver = Receiver::<TestMessage>::new_from_populated_vmo(vmo_dup, 4)
1237            .expect("Receiver new_from_populated_vmo failed");
1238
1239        // Verify they started at the seeded value.
1240        assert_eq!(receiver.index(), start_idx);
1241        assert_eq!(sender.index(), start_idx);
1242
1243        // Push some items to trigger wrap-around.
1244        sender.push(TestMessage { val: 10 }).expect("push failed");
1245        assert_eq!(sender.index(), State::INDEX_MASK);
1246
1247        sender.push(TestMessage { val: 20 }).expect("push failed");
1248        assert_eq!(sender.index(), 0);
1249
1250        sender.push(TestMessage { val: 30 }).expect("push failed");
1251        assert_eq!(sender.index(), 1);
1252
1253        // Pop them and verify read_index also wraps around.
1254        let next_msg = receiver.peek().expect("peek failed");
1255        assert_eq!(next_msg.val, 10);
1256        next_msg.pop().expect("pop failed");
1257        assert_eq!(receiver.index(), State::INDEX_MASK);
1258
1259        let next_msg = receiver.peek().expect("peek failed");
1260        assert_eq!(next_msg.val, 20);
1261        next_msg.pop().expect("pop failed");
1262        assert_eq!(receiver.index(), 0);
1263
1264        let next_msg = receiver.peek().expect("peek failed");
1265        assert_eq!(next_msg.val, 30);
1266        next_msg.pop().expect("pop failed");
1267        assert_eq!(receiver.index(), 1);
1268
1269        assert!(receiver.is_empty());
1270    }
1271
1272    #[fuchsia::test]
1273    fn test_receiver_wakes_on_sender_drop() {
1274        let vmo = zx::Vmo::create(4096).expect("VMO creation failed");
1275        let vmo_dup =
1276            vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).expect("VMO duplicate_handle failed");
1277
1278        let sender = SyncSender::<TestMessage>::new(vmo, 8, 2).expect("Sender creation failed");
1279        let mut receiver =
1280            Receiver::<TestMessage>::new(vmo_dup, 2).expect("Receiver creation failed");
1281
1282        let handle = std::thread::spawn(move || {
1283            // Receiver blocks because the queue is empty
1284            assert_eq!(receiver.peek().unwrap_err(), zx::Status::CANCELED);
1285        });
1286
1287        // Give the receiver thread a moment to block on wait()
1288        thread::sleep(Duration::from_millis(100));
1289
1290        // Dropping sender should trigger shutdown
1291        drop(sender);
1292
1293        handle.join().expect("thread panicked");
1294    }
1295
1296    #[fuchsia::test]
1297    fn test_sender_wakes_on_receiver_drop() {
1298        let vmo = zx::Vmo::create(4096).expect("VMO creation failed");
1299        let vmo_dup =
1300            vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).expect("VMO duplicate_handle failed");
1301
1302        let mut sender = SyncSender::<TestMessage>::new(vmo, 8, 2).expect("Sender creation failed");
1303        let receiver = Receiver::<TestMessage>::new(vmo_dup, 2).expect("Receiver creation failed");
1304
1305        // Fill up the queue so the next push blocks
1306        sender.push(TestMessage { val: 1 }).expect("push failed");
1307        sender.push(TestMessage { val: 2 }).expect("push failed");
1308
1309        let handle = std::thread::spawn(move || {
1310            // Sender blocks because the queue is full
1311            sender.push(TestMessage { val: 3 })
1312        });
1313
1314        // Give the sender thread a moment to block on wait()
1315        thread::sleep(Duration::from_millis(100));
1316
1317        // Dropping receiver should trigger shutdown
1318        drop(receiver);
1319
1320        let result = handle.join().expect("thread panicked");
1321        assert_eq!(result.unwrap_err(), zx::Status::CANCELED);
1322    }
1323
1324    // Wire Protocol Struct - this is the struct sent to queue
1325    #[derive(FromBytes, IntoBytes, KnownLayout, Clone, Copy, Debug, PartialEq)]
1326    #[repr(C)]
1327    struct WireTestCommand {
1328        opcode: u32,
1329        vmo_offset: u32,
1330        len: u32, // Used by WithPayload (opcode 0)
1331        _pad: u32,
1332        val: u64, // Used by NoPayload (opcode 1)
1333    }
1334
1335    #[derive(Debug, PartialEq)]
1336    enum TestCommand {
1337        WithPayload { vmo_offset: u32, len: u32 },
1338        NoPayload { val: u64 },
1339    }
1340
1341    impl TestCommand {
1342        fn to_wire(&self) -> WireTestCommand {
1343            match self {
1344                TestCommand::WithPayload { vmo_offset, len } => WireTestCommand {
1345                    opcode: 0,
1346                    vmo_offset: *vmo_offset,
1347                    len: *len,
1348                    _pad: 0,
1349                    val: 0,
1350                },
1351                TestCommand::NoPayload { val } => {
1352                    WireTestCommand { opcode: 1, vmo_offset: 0, len: 0, _pad: 0, val: *val }
1353                }
1354            }
1355        }
1356
1357        fn from_wire(wire: WireTestCommand) -> Self {
1358            match wire.opcode {
1359                0 => TestCommand::WithPayload { vmo_offset: wire.vmo_offset, len: wire.len },
1360                _ => TestCommand::NoPayload { val: wire.val },
1361            }
1362        }
1363    }
1364
1365    #[fuchsia::test]
1366    fn test_payload_drop_releases_allocation() {
1367        let page_size = zx::system_get_page_size() as u64;
1368        let vmo = zx::Vmo::create(page_size * 2).expect("VMO creation failed");
1369
1370        let mut sender =
1371            SyncSender::<WireTestCommand>::new(vmo, 8, 4).expect("Sender creation failed");
1372
1373        let buffer = sender.reserve_payload(10).expect("reserve failed");
1374        assert_eq!(buffer.offset(), 0);
1375
1376        // Roll it back instead of committing
1377        drop(buffer);
1378
1379        // Allocate again, ensure it gives the same offset
1380        let buffer2 = sender.reserve_payload(10).expect("reserve failed");
1381        assert_eq!(buffer2.offset(), 0);
1382    }
1383
1384    #[fuchsia::test]
1385    fn test_payload_buffer_drops_uncommitted_allocations() {
1386        let page_size = zx::system_get_page_size() as u64;
1387        let vmo = zx::Vmo::create(page_size * 2).expect("VMO creation failed");
1388
1389        let mut sender =
1390            SyncSender::<WireTestCommand>::new(vmo, 8, 4).expect("Sender creation failed");
1391
1392        // Simulate a function that reserves a payload but returns early (e.g., from encountering
1393        // an error).
1394        let _ = (|| -> Result<(), ()> {
1395            let _buffer = sender.reserve_payload(10).expect("reserve failed");
1396
1397            // Assuming an error is encountered before `_buffer` can be committed,
1398            // the early return causes the buffer to be dropped, triggering cancellation.
1399            Err(())
1400        })();
1401
1402        // Verifies that the early return successfully dropped `_buffer` and cancelled the
1403        // allocation. The queue is now empty, placing this allocation at offset 0.
1404        let buffer_after_bail = sender.reserve_payload(10).expect("reserve failed");
1405        assert_eq!(buffer_after_bail.offset(), 0);
1406    }
1407
1408    #[fuchsia::test]
1409    fn test_with_payload_commands() {
1410        let page_size = zx::system_get_page_size() as u64;
1411        let vmo = zx::Vmo::create(page_size * 2).expect("VMO creation failed");
1412        let vmo_dup =
1413            vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).expect("VMO duplicate_handle failed");
1414
1415        let mut sender =
1416            SyncSender::<WireTestCommand>::new(vmo, 8, 4).expect("Sender creation failed");
1417        let mut receiver =
1418            Receiver::<WireTestCommand>::new(vmo_dup, 4).expect("Receiver creation failed");
1419
1420        let mut buffer = sender.reserve_payload(10).expect("reserve failed");
1421        buffer.data().fill(0xAA);
1422        let wire_msg = TestCommand::WithPayload { vmo_offset: buffer.offset(), len: 10 }.to_wire();
1423        buffer.commit(wire_msg).expect("commit failed");
1424
1425        let standalone = TestCommand::NoPayload { val: 456 };
1426        sender.push(standalone.to_wire()).expect("push failed");
1427
1428        let msg1 = receiver.peek().expect("peek failed");
1429        let app_msg1 = TestCommand::from_wire(*msg1);
1430        if let TestCommand::WithPayload { vmo_offset, len } = app_msg1 {
1431            assert_eq!(len, 10);
1432            let read_data = msg1.payload_slice(vmo_offset, len);
1433            assert_eq!(read_data.to_vec(), vec![0xAA; 10]);
1434        } else {
1435            panic!("Expected TestCommand::WithPayload");
1436        }
1437        msg1.pop().expect("pop failed");
1438
1439        let msg2 = receiver.peek().expect("peek failed");
1440        let app_msg2 = TestCommand::from_wire(*msg2);
1441        assert_eq!(app_msg2, TestCommand::NoPayload { val: 456 });
1442        msg2.pop().expect("pop failed");
1443    }
1444
1445    #[fuchsia::test]
1446    fn test_pop_handles_canceled_race_condition() {
1447        // Run ten thousand times to force thread-scheduler starvation naturally
1448        for _ in 0..10000 {
1449            let vmo = zx::Vmo::create(4096).unwrap();
1450            let vmo_dup = vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
1451            let mut sender = SyncSender::<u32>::new(vmo, 4, 1).unwrap();
1452            let mut receiver = Receiver::<u32>::new(vmo_dup, 1).unwrap();
1453
1454            let handle = std::thread::spawn(move || {
1455                // Spin loop until the receiver sets the WAITER flag indicating it is blocked.
1456                loop {
1457                    let write_head = sender.0.inner.load_write_head(Ordering::Acquire);
1458                    if write_head.has_waiter() {
1459                        break;
1460                    }
1461                    std::thread::yield_now();
1462                }
1463
1464                // Immediately push (which asserts SIG_DATA_AVAILABLE and wakes the receiver).
1465                sender.push(42).expect("push failed");
1466
1467                // Drop the sender immediately (which asserts SIG_SHUTDOWN on the VMO). The receiver
1468                // will occasionally observe both signals, and must not incorrectly return
1469                // ZX_ERR_CANCELED since valid data is waiting in the queue.
1470                drop(sender);
1471            });
1472
1473            // This call will set the WAITER flag, go to sleep, sometimes wakes up with both data
1474            // and shutdown signals, and should successfully pop the payload.
1475            let val = receiver.peek().expect("peek failed on race condition");
1476            assert_eq!(*val, 42);
1477            val.pop().expect("pop failed");
1478
1479            // The queue is now genuinely empty and the peer has dropped.
1480            assert_eq!(receiver.peek().unwrap_err(), zx::Status::CANCELED);
1481
1482            handle.join().unwrap();
1483        }
1484    }
1485
1486    #[fuchsia::test]
1487    async fn test_async_push_yields_when_full() {
1488        let page_size = zx::system_get_page_size() as u64;
1489        let vmo = zx::Vmo::create(page_size * 2).expect("failed to create VMO");
1490        let vmo_dup =
1491            vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).expect("failed to duplicate VMO handle");
1492
1493        let queue_capacity = 4;
1494        let mut sender = AsyncSender::<TestMessage>::new(vmo, 4, queue_capacity)
1495            .expect("failed to create AsyncSender");
1496        let mut receiver = Receiver::<TestMessage>::new(vmo_dup, queue_capacity)
1497            .expect("failed to create Receiver");
1498
1499        for i in 0..queue_capacity {
1500            sender.push(TestMessage { val: i }).await.expect("failed to push message");
1501        }
1502
1503        let push_task = async {
1504            sender
1505                .push(TestMessage { val: 42 })
1506                .await
1507                .expect("background push task failed after yielding");
1508        };
1509        let pop_thread = std::thread::spawn(move || {
1510            thread::sleep(Duration::from_millis(5));
1511            let msg = receiver.peek().expect("failed to peek message");
1512            assert_eq!(msg.val, 0); // we expect 0 to be popped since it was pushed first
1513            msg.pop().expect("failed to pop message");
1514            receiver
1515        });
1516
1517        push_task.await;
1518        let mut receiver = pop_thread.join().unwrap();
1519
1520        for expected in [1, 2, 3, 42] {
1521            let msg = receiver.peek().expect("failed to peek message");
1522            assert_eq!(msg.val, expected);
1523            msg.pop().expect("failed to pop message");
1524        }
1525    }
1526
1527    #[fuchsia::test]
1528    async fn test_reserve_payload_async_yields() {
1529        let page_size = zx::system_get_page_size() as u64;
1530        let vmo = zx::Vmo::create(page_size * 2).expect("failed to create VMO");
1531        let vmo_dup =
1532            vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).expect("failed to duplicate VMO handle");
1533
1534        let mut sender =
1535            AsyncSender::<TestMessage>::new(vmo, 4, 4).expect("failed to create AsyncSender");
1536        let mut receiver =
1537            Receiver::<TestMessage>::new(vmo_dup, 4).expect("failed to create Receiver");
1538
1539        // The VMO is 2 pages long. The first page is reserved for queue headers and message slots,
1540        // leaving `page_size` bytes of remaining payload capacity. Reserving `page_size` bytes here
1541        // completely exhausts the payload buffer.
1542        let block1 = sender
1543            .reserve_payload(page_size as usize)
1544            .await
1545            .expect("failed to reserve payload capacity");
1546        block1.commit(TestMessage { val: 1 }).await.expect("failed to commit message block");
1547
1548        // With 0 bytes of payload capacity remaining, the next reserve is forced to yield.
1549        let reserve_task = async {
1550            let buffer = sender
1551                .reserve_payload(4)
1552                .await
1553                .expect("background reserve payload task failed after yielding");
1554            buffer.commit(TestMessage { val: 2 }).await.expect("failed to commit message block");
1555        };
1556        let pop_thread = std::thread::spawn(move || {
1557            // Give the reserve_task a tiny bit of time to yield
1558            thread::sleep(Duration::from_millis(5));
1559            let msg = receiver.peek().expect("failed to peek message");
1560            assert_eq!(msg.val, 1);
1561            msg.pop().expect("failed to pop message");
1562            receiver
1563        });
1564
1565        reserve_task.await;
1566        let mut receiver = pop_thread.join().unwrap();
1567
1568        // Pop the second item out
1569        let msg = receiver.peek().expect("failed to peek message");
1570        assert_eq!(msg.val, 2);
1571        msg.pop().expect("failed to pop message");
1572    }
1573
1574    #[fuchsia::test]
1575    async fn test_async_commit_yields_when_full() {
1576        let page_size = zx::system_get_page_size() as u64;
1577        let vmo = zx::Vmo::create(page_size * 2).expect("failed to create VMO");
1578        let vmo_dup =
1579            vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).expect("failed to duplicate VMO handle");
1580
1581        let queue_capacity = 4;
1582        let mut sender = AsyncSender::<WireTestCommand>::new(vmo, 4, queue_capacity)
1583            .expect("failed to create AsyncSender");
1584        let mut receiver = Receiver::<WireTestCommand>::new(vmo_dup, queue_capacity)
1585            .expect("failed to create Receiver");
1586
1587        // Fill up all the queue slots.
1588        for i in 0..queue_capacity {
1589            let standalone = TestCommand::NoPayload { val: i as u64 };
1590            sender.push(standalone.to_wire()).await.expect("failed to push message");
1591        }
1592
1593        // Even though payload capacity is available, the queue slot capacity is fully saturated.
1594        let block = sender.reserve_payload(4).await.expect("failed to reserve payload capacity");
1595
1596        let commit_task = async {
1597            let msg = TestCommand::WithPayload { vmo_offset: block.offset(), len: 4 };
1598            block
1599                .commit(msg.to_wire())
1600                .await
1601                .expect("background commit task failed after yielding");
1602        };
1603
1604        let pop_thread = std::thread::spawn(move || {
1605            // Give the commit_task a tiny bit of time to yield
1606            thread::sleep(Duration::from_millis(5));
1607
1608            // Pop the 4 standalone messages we initially filled the queue with.
1609            for expected in 0..4 {
1610                let msg = receiver.peek().expect("failed to peek message");
1611                let app_msg = TestCommand::from_wire(*msg);
1612                assert!(matches!(app_msg, TestCommand::NoPayload { val } if val == expected));
1613                msg.pop().expect("failed to pop message");
1614            }
1615
1616            // Pop the actually pushed payload command to verify it.
1617            let msg2 = receiver.peek().expect("failed to pop payload message");
1618            let app_msg2 = TestCommand::from_wire(*msg2);
1619            assert!(matches!(app_msg2, TestCommand::WithPayload { .. }));
1620            msg2.pop().expect("failed to pop message");
1621        });
1622
1623        commit_task.await;
1624        pop_thread.join().unwrap();
1625    }
1626
1627    #[fuchsia::test]
1628    fn test_push_when_receiver_drops() {
1629        let vmo = zx::Vmo::create(4096).expect("VMO creation failed");
1630        let vmo_dup =
1631            vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).expect("VMO duplicate_handle failed");
1632
1633        let mut sender = SyncSender::<TestMessage>::new(vmo, 8, 4).expect("Sender creation failed");
1634        let receiver = Receiver::<TestMessage>::new(vmo_dup, 4).expect("Receiver creation failed");
1635
1636        // The receiver goes away unexpectedly before the queue is full.
1637        drop(receiver);
1638
1639        // Even though the remote peer is dead, the queue still has capacity and doesn't know peer
1640        // has died.
1641        sender.push(TestMessage { val: 1 }).expect("push failed");
1642        sender.push(TestMessage { val: 2 }).expect("push failed");
1643        sender.push(TestMessage { val: 3 }).expect("push failed");
1644        sender.push(TestMessage { val: 4 }).expect("push failed");
1645
1646        // Now the queue is full. The sender blocks until receiver signals there is space again, but
1647        // discovers that the receiver has died with `SIG_SHUTDOWN`.
1648        assert_eq!(sender.push(TestMessage { val: 5 }), Err(zx::Status::CANCELED));
1649    }
1650
1651    #[fuchsia::test]
1652    async fn test_async_push_when_receiver_drops() {
1653        let vmo = zx::Vmo::create(4096).expect("VMO creation failed");
1654        let vmo_dup =
1655            vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).expect("VMO duplicate_handle failed");
1656
1657        let mut sender =
1658            AsyncSender::<TestMessage>::new(vmo, 8, 4).expect("Sender creation failed");
1659        let receiver = Receiver::<TestMessage>::new(vmo_dup, 4).expect("Receiver creation failed");
1660
1661        // The receiver goes away unexpectedly before the queue is full.
1662        drop(receiver);
1663
1664        // Even though the remote peer is dead, the queue still has capacity and doesn't know peer
1665        // has died.
1666        sender.push(TestMessage { val: 1 }).await.expect("push failed");
1667        sender.push(TestMessage { val: 2 }).await.expect("push failed");
1668        sender.push(TestMessage { val: 3 }).await.expect("push failed");
1669        sender.push(TestMessage { val: 4 }).await.expect("push failed");
1670
1671        // Now the queue is full. The sender blocks until receiver signals there is space again, but
1672        // discovers that the receiver has died with `SIG_SHUTDOWN`.
1673        assert_eq!(sender.push(TestMessage { val: 5 }).await.unwrap_err(), zx::Status::CANCELED);
1674    }
1675
1676    #[fuchsia::test]
1677    fn test_reserve_payload_sync_exceeds_capacity() {
1678        let page_size = zx::system_get_page_size() as u64;
1679        let vmo = zx::Vmo::create(page_size * 2).expect("failed to create VMO");
1680        let vmo_dup =
1681            vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).expect("failed to duplicate VMO handle");
1682
1683        let mut sender =
1684            SyncSender::<TestMessage>::new(vmo, 4, 4).expect("failed to create Sender");
1685        let _receiver =
1686            Receiver::<TestMessage>::new(vmo_dup, 4).expect("failed to create Receiver");
1687
1688        // The first page is reserved for queue headers and message slots. We attempt to reserve
1689        // more than the capacity.
1690        let result = sender.reserve_payload((page_size + 10) as usize);
1691        assert_eq!(result.err(), Some(zx::Status::NO_MEMORY));
1692    }
1693
1694    #[fuchsia::test]
1695    async fn test_reserve_payload_async_exceeds_capacity() {
1696        let page_size = zx::system_get_page_size() as u64;
1697        let vmo = zx::Vmo::create(page_size * 2).expect("failed to create VMO");
1698        let vmo_dup =
1699            vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).expect("failed to duplicate VMO handle");
1700
1701        let mut sender =
1702            AsyncSender::<TestMessage>::new(vmo, 4, 4).expect("failed to create AsyncSender");
1703        let _receiver =
1704            Receiver::<TestMessage>::new(vmo_dup, 4).expect("failed to create Receiver");
1705
1706        // The first page is reserved for queue headers and message slots. We attempt to reserve
1707        // more than the capacity.
1708        let result = sender.reserve_payload((page_size + 10) as usize).await;
1709        assert_eq!(result.err(), Some(zx::Status::NO_MEMORY));
1710    }
1711
1712    #[fuchsia::test]
1713    fn test_concurrent_claim_out_of_order_retire() {
1714        let vmo = zx::Vmo::create(65536).expect("VMO creation failed");
1715        let vmo_dup =
1716            vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).expect("VMO duplicate_handle failed");
1717
1718        let mut sender = SyncSender::<TestMessage>::new(vmo, 8, 8).expect("Sender creation failed");
1719        let receiver =
1720            Arc::new(Receiver::<TestMessage>::new(vmo_dup, 8).expect("Receiver creation failed"));
1721
1722        sender.push(TestMessage { val: 10 }).expect("push 1 failed");
1723        sender.push(TestMessage { val: 20 }).expect("push 2 failed");
1724
1725        let msg1 = receiver.claim().expect("claim 1 failed");
1726        let msg2 = receiver.claim().expect("claim 2 failed");
1727        assert_eq!(msg1.val, 10);
1728        assert_eq!(msg2.val, 20);
1729        assert_eq!(receiver.index(), 0);
1730
1731        // Retire slot 1 before slot 0: committed read_index must remain 0 until slot 0 finishes.
1732        msg2.pop().expect("pop 2 failed");
1733        assert_eq!(receiver.index(), 0);
1734
1735        // Retiring slot 0 advances committed read_index to 2.
1736        msg1.pop().expect("pop 1 failed");
1737        assert_eq!(receiver.index(), 2);
1738
1739        // Verify a pool of 2 worker threads can concurrently drain items and shut down cleanly.
1740        let seen = Arc::new(Mutex::new(Vec::new()));
1741        let mut threads = Vec::new();
1742        for _ in 0..2 {
1743            let rx = receiver.clone();
1744            let seen = seen.clone();
1745            threads.push(std::thread::spawn(move || {
1746                while let Ok(msg) = rx.claim() {
1747                    seen.lock().push(msg.val);
1748                    msg.pop().expect("pop failed");
1749                }
1750            }));
1751        }
1752
1753        for i in 0..100 {
1754            sender.push(TestMessage { val: i }).expect("push failed");
1755        }
1756        drop(sender);
1757
1758        for t in threads {
1759            t.join().expect("worker panicked");
1760        }
1761
1762        let mut results = seen.lock().clone();
1763        results.sort_unstable();
1764        assert_eq!(results, (0..100).collect::<Vec<u32>>());
1765    }
1766}