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