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