Skip to main content

orbit_stream/
lib.rs

1//! Bounded byte streams and paired exchanges between members of an Orbit
2//! fleet.
3//!
4//! A stream is a place with two ends. [`Streams::create`] takes a slot in
5//! this process's lane, hands back the endpoint for side A and a [`Ticket`]
6//! for side B; whoever in the fleet [`Streams::open`]s that ticket holds the
7//! other end. Each direction is its own bounded ring: bytes go in at one end
8//! and come out, in order and without loss, at the other; when the ring is
9//! full the writer waits, when it is empty the reader waits. A writer that
10//! is done shuts its direction down (FIN) and the reader drains to a clean
11//! end; a writer that gives up resets it and the reader sees an error.
12//!
13//! Standalone the rings live in process memory; in a shared-memory fleet
14//! they live in the fleet's segment and the two ends may be in different
15//! processes. The address that travels is a [`NetId64`]: its kind is the
16//! segment's, its node the creator's lane, its counter the slot and the
17//! slot's generation; the [`Ticket`] around it adds the table's epoch. An
18//! address kept past the stream's end, or past a reset, answers
19//! [`Error::Stale`] instead of reaching whoever took the slot next.
20//!
21//! The blocking calls park on the direction's own change word through the
22//! platform's shared address wait, like a cell. With the `tokio` feature the
23//! halves implement `AsyncRead` and `AsyncWrite`; a poll that finds nothing
24//! registers its waker and returns `Pending`, and one thread per process
25//! turns the fleet's doorbell into those wakes. No thread and no descriptor
26//! per stream.
27//!
28//! [`exchange`] builds a symmetric event program above that lossless control
29//! path. Payload bytes live in one shared arena whose per-node lanes make
30//! allocation ownership explicit: fixed-size slots are physical allocation
31//! units, while each published chunk is one descriptor spanning as many
32//! contiguous slots as that runtime decision needs. Both directions have
33//! independent START, FIN and RESET state, so either may begin first without
34//! imposing an application sequencing rule on the transport.
35
36use std::str::FromStr;
37use std::sync::atomic::{AtomicU32, Ordering};
38use std::sync::{Arc, Mutex, MutexGuard};
39use std::time::{Duration, Instant};
40use std::{fmt, io};
41
42use bytes::{Bytes, BytesMut};
43use orbit_core::{Fleet, NetId64};
44
45#[cfg(unix)]
46mod bell;
47pub mod exchange;
48mod layout;
49#[cfg(feature = "tokio")]
50mod poll;
51mod table;
52mod wake;
53
54use layout::{
55    Direction, FLAG_FIN, FLAG_READER_GONE, FLAG_RESET, GENERATION_MASK, SIDE_CLAIMED, SIDE_FREE,
56    SIDE_RELEASED, SLOT_BITS, SLOT_EMPTY, SLOT_LIVE, SLOT_MASK, Slot
57};
58use orbit_core::NodeId;
59#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
60pub use orbit_core::readiness::Readiness;
61use table::Table;
62pub use table::{segment_size, segment_size_for};
63use wake::Interest;
64
65/// Reserved Orbit SHM kind for the default stream segment. Another table
66/// names its own through a [`StreamSpec`].
67pub const STREAM_KIND: u8 = 246;
68/// Streams one fleet node can hold open at once, in the default spec.
69///
70/// Compile-time geometry: `ORBIT_STREAM_LANE_CAPACITY` in the application's
71/// `.cargo/config.toml` overrides the default. A power of two, at most
72/// 65 536. It is the *default* table's value; a [`StreamSpec`] gives
73/// another table another one. Peers opening the same kind with a different
74/// value are refused at the segment.
75pub const STREAM_LANE_CAPACITY: usize =
76    orbit_core::compile::usize_from_env(option_env!("ORBIT_STREAM_LANE_CAPACITY"), 256);
77/// Bytes each direction of a stream can hold before its writer waits, in
78/// the default spec.
79///
80/// Compile-time geometry: `ORBIT_STREAM_BUFFER_BYTES`. A power of two. Part
81/// of the same wire contract as the lane capacity, and like it a per-table
82/// choice through [`StreamSpec`].
83pub const STREAM_BUFFER_BYTES: usize =
84    orbit_core::compile::usize_from_env(option_env!("ORBIT_STREAM_BUFFER_BYTES"), 64 * 1024);
85
86/// Which segment a [`Streams`] table uses, and how big its lanes and
87/// rings are.
88///
89/// One fleet can hold several independent stream tables. They are not
90/// interchangeable: a relay carrying response bodies wants a ring sized to
91/// a body, while a control channel carrying short frames is faster with a
92/// small one, and a lane is a ceiling on concurrent streams per node. Each
93/// names its own kind, and a kind is a fleet-wide identity — every process
94/// opening it must pass the same geometry, and the segment's header
95/// refuses a peer that does not. [`StreamSpec::DEFAULT`] is what
96/// [`Streams::new`] opens; its values are the compile-time geometry.
97#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
98pub struct StreamSpec {
99    /// The Orbit SHM kind, and with it the segment name.
100    pub kind: u8,
101    /// Streams one fleet node can hold open at once. A power of two, at
102    /// most 65 536.
103    pub lane_capacity: usize,
104    /// Bytes each direction holds before its writer waits. A power of two.
105    pub buffer_bytes: usize
106}
107
108impl StreamSpec {
109    pub const DEFAULT: Self = Self::new(STREAM_KIND, STREAM_LANE_CAPACITY, STREAM_BUFFER_BYTES);
110
111    pub const fn new(
112        kind: u8,
113        lane_capacity: usize,
114        buffer_bytes: usize
115    ) -> Self {
116        Self { kind, lane_capacity, buffer_bytes }
117    }
118
119    /// The segment one node's streams occupy at most: every slot's two
120    /// rings. Memory is backed as it is touched, but this is the ceiling
121    /// a deployment is choosing when it picks a lane and a ring.
122    pub const fn lane_bytes(&self) -> usize {
123        self.lane_capacity * 2 * self.buffer_bytes
124    }
125
126    /// What the compile-time geometry used to assert. A spec is checked
127    /// once, when its table is opened.
128    fn validate(self) -> Result<()> {
129        if self.lane_capacity == 0
130            || self.buffer_bytes == 0
131            || !self.lane_capacity.is_power_of_two()
132            || !self.buffer_bytes.is_power_of_two()
133            || self.lane_capacity > 1 << SLOT_BITS
134            || self.buffer_bytes > u32::MAX as usize
135        {
136            return Err(Error::Malformed(format!(
137                "stream spec kind={} lane_capacity={} buffer_bytes={}: both are powers of two, a lane holds at most {} streams and a ring at most {} bytes",
138                self.kind,
139                self.lane_capacity,
140                self.buffer_bytes,
141                1_usize << SLOT_BITS,
142                u32::MAX
143            )));
144        }
145        Ok(())
146    }
147}
148
149impl Default for StreamSpec {
150    fn default() -> Self {
151        Self::DEFAULT
152    }
153}
154
155pub type Result<T, E = Error> = std::result::Result<T, E>;
156
157/// Additions are expected: a cause discovered later lands here rather
158/// than in a new major version, so a caller matches what it handles and
159/// leaves the rest to a catch-all.
160#[non_exhaustive]
161#[derive(Debug)]
162pub enum Error {
163    /// The address names a slot nothing occupies, or a generation that ended.
164    Stale(StreamId),
165    /// That side of the stream is already held, or was held and released.
166    AlreadyClaimed(Ticket),
167    /// Every slot in this process's lane is in use.
168    Full {
169        capacity: usize
170    },
171    /// The producer's payload arena has no contiguous run large enough.
172    PayloadFull {
173        requested_slots: usize
174    },
175    /// One chunk cannot fit in a lane of the selected payload arena.
176    PayloadTooLarge {
177        len: usize,
178        capacity: usize
179    },
180    /// The text or id is not a stream address.
181    Malformed(String),
182    /// This write half was shut down; nothing more goes in.
183    Closed,
184    /// The other end reset the direction; buffered bytes were discarded.
185    Reset,
186    /// The other end's reader is gone; nothing written here will be read.
187    PeerGone,
188    /// Nothing to read, or no room to write, right now.
189    WouldBlock,
190    Io(io::Error)
191}
192
193impl fmt::Display for Error {
194    fn fmt(
195        &self,
196        f: &mut fmt::Formatter<'_>
197    ) -> fmt::Result {
198        match self {
199            Self::Stale(id) => write!(f, "stream {id} has ended"),
200            Self::AlreadyClaimed(ticket) => write!(f, "stream side {ticket} is already held"),
201            Self::Full { capacity } => write!(f, "stream lane is full: capacity={capacity}"),
202            Self::PayloadFull { requested_slots } => {
203                write!(f, "payload arena has no run of {requested_slots} free slots")
204            }
205            Self::PayloadTooLarge { len, capacity } => {
206                write!(f, "payload chunk is {len} bytes; arena lane capacity is {capacity}")
207            }
208            Self::Malformed(text) => write!(f, "not a stream address: {text:?}"),
209            Self::Closed => f.write_str("stream write half is shut down"),
210            Self::Reset => f.write_str("stream direction was reset by the peer"),
211            Self::PeerGone => f.write_str("stream peer reader is gone"),
212            Self::WouldBlock => f.write_str("stream operation would block"),
213            Self::Io(error) => write!(f, "Orbit stream io error: {error}")
214        }
215    }
216}
217
218impl std::error::Error for Error {
219    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
220        match self {
221            Self::Io(error) => Some(error),
222            _ => None
223        }
224    }
225}
226
227impl From<io::Error> for Error {
228    fn from(value: io::Error) -> Self {
229        Self::Io(value)
230    }
231}
232
233impl From<Error> for io::Error {
234    fn from(value: Error) -> Self {
235        let kind = match &value {
236            Error::Stale(_) | Error::AlreadyClaimed(_) => io::ErrorKind::NotConnected,
237            Error::Full { .. } | Error::PayloadFull { .. } | Error::PayloadTooLarge { .. } => {
238                io::ErrorKind::OutOfMemory
239            }
240            Error::Malformed(_) => io::ErrorKind::InvalidInput,
241            Error::Closed => io::ErrorKind::BrokenPipe,
242            Error::Reset => io::ErrorKind::ConnectionReset,
243            Error::PeerGone => io::ErrorKind::BrokenPipe,
244            Error::WouldBlock => io::ErrorKind::WouldBlock,
245            Error::Io(error) => error.kind()
246        };
247        io::Error::new(kind, value)
248    }
249}
250
251/// The address of one stream: a [`NetId64`] whose node is the creator's
252/// lane and whose counter is the slot and its generation.
253#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
254pub struct StreamId(NetId64);
255
256impl StreamId {
257    pub const fn from_net_id(id: NetId64) -> Self {
258        Self(id)
259    }
260
261    pub const fn net_id(self) -> NetId64 {
262        self.0
263    }
264
265    pub const fn kind(self) -> u8 {
266        self.0.kind()
267    }
268
269    /// The lane: the fleet node that created the stream.
270    pub const fn node(self) -> u16 {
271        self.0.node()
272    }
273
274    pub const fn slot(self) -> u32 {
275        (self.0.counter() & SLOT_MASK) as u32
276    }
277
278    pub const fn generation(self) -> u32 {
279        (self.0.counter() >> SLOT_BITS) as u32
280    }
281
282    fn make(
283        kind: u8,
284        node: u16,
285        slot: u32,
286        generation: u32
287    ) -> Self {
288        Self(NetId64::make(
289            kind,
290            node,
291            ((generation as u64) << SLOT_BITS) | (slot as u64 & SLOT_MASK)
292        ))
293    }
294}
295
296impl fmt::Display for StreamId {
297    fn fmt(
298        &self,
299        f: &mut fmt::Formatter<'_>
300    ) -> fmt::Result {
301        self.0.fmt(f)
302    }
303}
304
305impl FromStr for StreamId {
306    type Err = Error;
307
308    fn from_str(text: &str) -> Result<Self> {
309        text.parse::<NetId64>().map(Self).map_err(|_| Error::Malformed(text.to_owned()))
310    }
311}
312
313/// Which end of a stream a handle holds. The creator holds `A`.
314#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
315pub enum Side {
316    A,
317    B
318}
319
320impl Side {
321    const fn index(self) -> usize {
322        match self {
323            Self::A => 0,
324            Self::B => 1
325        }
326    }
327
328    /// The direction this side writes into.
329    const fn write_direction(self) -> usize {
330        self.index()
331    }
332
333    /// The direction this side reads from.
334    const fn read_direction(self) -> usize {
335        1 - self.index()
336    }
337
338    const fn letter(self) -> char {
339        match self {
340            Self::A => 'a',
341            Self::B => 'b'
342        }
343    }
344}
345
346/// One end of a stream, in the form that crosses a setup channel: the
347/// address, the side, and the epoch of the table the address was minted
348/// in. Prints as `<address>/b/<epoch>`.
349///
350/// Two things keep a ticket from reaching the wrong memory: the slot
351/// generation inside the address separates reuses of a slot within one
352/// epoch, and the epoch separates before and after a quiescent reset or a
353/// recreated segment, which reinstalls slots from generation one.
354#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
355pub struct Ticket {
356    pub id: StreamId,
357    pub side: Side,
358    pub epoch: u64
359}
360
361impl fmt::Display for Ticket {
362    fn fmt(
363        &self,
364        f: &mut fmt::Formatter<'_>
365    ) -> fmt::Result {
366        write!(f, "{}/{}/{}", self.id, self.side.letter(), self.epoch)
367    }
368}
369
370impl FromStr for Ticket {
371    type Err = Error;
372
373    fn from_str(text: &str) -> Result<Self> {
374        let malformed = || Error::Malformed(text.to_owned());
375        let (rest, epoch) = text.rsplit_once('/').ok_or_else(malformed)?;
376        let (id, side) = rest.rsplit_once('/').ok_or_else(malformed)?;
377        let side = match side {
378            "a" => Side::A,
379            "b" => Side::B,
380            _ => return Err(malformed())
381        };
382        Ok(Self {
383            id: id.parse().map_err(|_| malformed())?,
384            side,
385            epoch: epoch.parse().map_err(|_| malformed())?
386        })
387    }
388}
389
390/// Which life of a process holds a side. A node id is a role that a
391/// replacement process inherits; the incarnation tells the two apart, so a
392/// late death report for the old process never touches the new one's
393/// streams. Orbit does not mint it: the embedder knows what identifies one
394/// live process (a start stamp, a supervisor generation) and supplies it.
395#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
396pub struct Incarnation(u64);
397
398impl Incarnation {
399    pub const fn new(value: u64) -> Self {
400        Self(value)
401    }
402
403    pub const fn get(self) -> u64 {
404        self.0
405    }
406}
407
408/// The fleet's stream table. Cheap to clone; every clone in a process is
409/// the same table, the same driver and the same wakers.
410#[derive(Clone)]
411pub struct Streams {
412    table: Arc<Table>
413}
414
415impl Streams {
416    /// Open the fleet's table as this process's incarnation. Every side
417    /// this process claims is stamped with it, and [`Streams::node_dead`]
418    /// for the same node and incarnation is what ends those sides.
419    pub fn new(
420        fleet: Arc<Fleet>,
421        incarnation: Incarnation
422    ) -> Result<Self> {
423        Self::with_spec(fleet, incarnation, StreamSpec::DEFAULT)
424    }
425
426    /// Open the table `spec` names. Independent specs are independent
427    /// tables: separate segments, separate lanes and rings, separate
428    /// epochs, and a `reset_all` on one leaves the others alone. A process
429    /// may hold as many as it has specs, but each under one incarnation.
430    pub fn with_spec(
431        fleet: Arc<Fleet>,
432        incarnation: Incarnation,
433        spec: StreamSpec
434    ) -> Result<Self> {
435        spec.validate()?;
436        Ok(Self { table: table::open(&fleet, incarnation, spec)? })
437    }
438
439    /// The kind this table's segment lives under.
440    pub fn kind(&self) -> u8 {
441        self.table.kind()
442    }
443
444    /// A descriptor that becomes readable when something among this
445    /// process's streams may have changed, for a runtime that parks on
446    /// descriptors rather than on wakers.
447    ///
448    /// Edge-triggered and coalescing: drain it, then re-try the
449    /// non-blocking calls on the streams you hold. It is what makes this
450    /// table usable from a foreign event loop — an embedded runtime, a
451    /// reactor of its own — without a thread or a descriptor per stream.
452    ///
453    /// One per table: a second caller is refused rather than handed a
454    /// descriptor whose signals the first would drain.
455    #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
456    pub fn readiness(&self) -> Result<Readiness> {
457        self.table.take_readiness()
458    }
459
460    /// The epoch every ticket minted from this table carries right now.
461    pub fn epoch(&self) -> u64 {
462        self.table.epoch()
463    }
464
465    /// Take a slot in this process's lane. The endpoint is side A; the
466    /// ticket names side B and is what the other end opens.
467    pub fn create(&self) -> Result<(Endpoint, Ticket)> {
468        let (index, generation) = self.table.allocate()?;
469        let id = StreamId::make(
470            self.table.kind(),
471            self.table.node(),
472            (index % self.table.geometry().lane_capacity) as u32,
473            generation
474        );
475        let handle = Arc::new(Handle { table: Arc::clone(&self.table), index, id, side: Side::A });
476        Ok((Endpoint::new(handle), Ticket { id, side: Side::B, epoch: self.table.epoch() }))
477    }
478
479    /// Hold the side a ticket names. Each side can be held once per stream.
480    pub fn open(
481        &self,
482        ticket: Ticket
483    ) -> Result<Endpoint> {
484        let index = self.locate(ticket.id)?;
485        if ticket.epoch != self.table.epoch() {
486            return Err(Error::Stale(ticket.id));
487        }
488        let slot = &self.table.slots()[index];
489        if !slot.is(ticket.id.generation()) {
490            return Err(Error::Stale(ticket.id));
491        }
492        let claimed = &slot.claimed[ticket.side.index()];
493        if claimed
494            .compare_exchange(SIDE_FREE, SIDE_CLAIMED, Ordering::SeqCst, Ordering::SeqCst)
495            .is_err()
496        {
497            return Err(Error::AlreadyClaimed(ticket));
498        }
499        // The slot may have ended between the check and the claim; give the
500        // claim back rather than hold a side of whatever comes next.
501        if !slot.is(ticket.id.generation()) {
502            let _ = claimed.compare_exchange(
503                SIDE_CLAIMED,
504                SIDE_FREE,
505                Ordering::SeqCst,
506                Ordering::SeqCst
507            );
508            return Err(Error::Stale(ticket.id));
509        }
510        slot.incarnation[ticket.side.index()].store(self.table.incarnation(), Ordering::Release);
511        slot.node[ticket.side.index()].store(self.table.node(), Ordering::Release);
512        // Counted for blocking waiters, and no doorbell rung. A claim is not
513        // something a parked task can be waiting for: readiness is `head`,
514        // `tail` and the flags, and no predicate reads who holds a side. A
515        // ring here woke both processes' drivers on every open to find
516        // nothing ready — measured, it was most of what an exchange cost
517        // above a Unix socket.
518        for direction in &slot.directions {
519            self.table.notify(index, slot, direction, table::Ring::Nobody);
520        }
521        Ok(Endpoint::new(Arc::new(Handle {
522            table: Arc::clone(&self.table),
523            index,
524            id: ticket.id,
525            side: ticket.side
526        })))
527    }
528
529    /// Clear a stream for another exchange, and let its far side be opened
530    /// again — by any process.
531    ///
532    /// A slot created and released per exchange pays for both every time: two
533    /// compare-and-swaps, a reset, and a ring nobody has touched since it was
534    /// last used, so cold in cache and absent from the TLB. A creator that
535    /// keeps its slots between exchanges calls this instead. Its own claim
536    /// stands and the generation does not move, so the ticket stays valid;
537    /// the directions go back to nothing, and the far side changes hands.
538    ///
539    /// **The far side has to have let go, and the table is what says so.** Its
540    /// halves raise their flags as they drop and its claim is released after
541    /// them, so a claim that reads released belongs to a process that can no
542    /// longer touch the slot. That is the whole handshake — no message, no
543    /// flag to race with — and it is why the slot belongs to its creator
544    /// rather than to a pair: once reopened, whichever process the next
545    /// exchange is addressed to opens it.
546    ///
547    /// - `Ok`: re-armed; the next ticket written for it can be opened.
548    /// - [`Error::WouldBlock`]: the far side still holds it. Not yet — try
549    ///   another slot.
550    /// - [`Error::Stale`]: let it go. The address no longer names this stream,
551    ///   the far side died, or it never opened this exchange at all — and a
552    ///   side nobody opened may still be opened late by whoever the last
553    ///   ticket went to, so it is not safe to hand to anyone else. Dropping the
554    ///   endpoint empties the slot, and a late open is then refused as stale.
555    ///
556    /// The far side's owner goes back to what [`Streams::create`] writes for
557    /// nobody before its claim opens. A death report reads the claim first and
558    /// the owner after it, so without this a report about the process that
559    /// just let go could land on the one that opens next.
560    ///
561    /// **The caller owns the ordering.** Whoever re-arms must do it *before*
562    /// telling the other side that a new exchange has begun, so that the
563    /// message announcing the exchange is what publishes the cleared state.
564    pub fn rearm(
565        &self,
566        id: StreamId
567    ) -> Result<()> {
568        let index = self.locate(id)?;
569        let slot = &self.table.slots()[index];
570        if !slot.is(id.generation()) {
571            return Err(Error::Stale(id));
572        }
573        let far = Side::B.index();
574        match slot.claimed[far].load(Ordering::SeqCst) {
575            SIDE_RELEASED => {}
576            SIDE_CLAIMED => return Err(Error::WouldBlock),
577            _ => return Err(Error::Stale(id))
578        }
579        for direction in &slot.directions {
580            direction.clear();
581        }
582        slot.node[far].store(u16::MAX, Ordering::Relaxed);
583        slot.incarnation[far].store(0, Ordering::Relaxed);
584        // Released is terminal for its holder and a death report only touches
585        // a claimed side, so nothing else moves this word; the exchange is
586        // what publishes everything written above it.
587        slot.claimed[far]
588            .compare_exchange(SIDE_RELEASED, SIDE_FREE, Ordering::SeqCst, Ordering::SeqCst)
589            .map_err(|_| Error::Stale(id))?;
590        Ok(())
591    }
592
593    /// Whether the address names a live stream right now.
594    pub fn is_live(
595        &self,
596        id: StreamId
597    ) -> bool {
598        self.locate(id).map(|index| self.table.slots()[index].is(id.generation())).unwrap_or(false)
599    }
600
601    /// Tell `node`'s process that side B of this stream is its to take:
602    /// the slot is put in that node's offer bitmap and its doorbell rung.
603    /// Discovery only; the ticket may just as well travel by any other
604    /// channel. An offer is seen once, by that node, through
605    /// [`Streams::take_offer`] and its waiting forms.
606    pub fn offer(
607        &self,
608        ticket: Ticket,
609        to: NodeId
610    ) -> Result<()> {
611        let index = self.locate(ticket.id)?;
612        if ticket.epoch != self.table.epoch()
613            || !self.table.slots()[index].is(ticket.id.generation())
614        {
615            return Err(Error::Stale(ticket.id));
616        }
617        self.table.offer(index, to.get())
618    }
619
620    /// The next stream offered to this node, if any. A stream that ended
621    /// between the offer and now is skipped.
622    pub fn take_offer(&self) -> Option<Ticket> {
623        while let Some(index) = self.table.take_offer() {
624            if let Some(ticket) = self.ticket_for(index) {
625                return Some(ticket);
626            }
627        }
628        None
629    }
630
631    /// The same wait, bounded: `None` is the timeout and nothing else.
632    /// A worker whose shutdown must be observed waits this way rather
633    /// than parking on the next request forever.
634    #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
635    pub fn take_offer_timeout(
636        &self,
637        timeout: Duration
638    ) -> Result<Option<Ticket>> {
639        let deadline = Instant::now() + timeout;
640        loop {
641            if let Some(ticket) = self.take_offer() {
642                return Ok(Some(ticket));
643            }
644            let left = deadline.saturating_duration_since(Instant::now());
645            if left.is_zero() {
646                return Ok(self.take_offer());
647            }
648            if !self.table.wait_offer_timeout(left)? {
649                return Ok(self.take_offer());
650            }
651        }
652    }
653
654    /// Park the thread until a stream is offered to this node.
655    pub fn blocking_take_offer(&self) -> Result<Ticket> {
656        loop {
657            if let Some(ticket) = self.take_offer() {
658                return Ok(ticket);
659            }
660            self.table.wait_offer()?;
661        }
662    }
663
664    /// Readiness for a task: the next offer, or the waker is registered
665    /// and `Pending` comes back.
666    pub fn poll_take_offer(
667        &self,
668        cx: &mut std::task::Context<'_>
669    ) -> std::task::Poll<Result<Ticket>> {
670        if let Some(ticket) = self.take_offer() {
671            return std::task::Poll::Ready(Ok(ticket));
672        }
673        if let Err(error) = self.table.register_offer(cx.waker()) {
674            return std::task::Poll::Ready(Err(error));
675        }
676        match self.take_offer() {
677            Some(ticket) => std::task::Poll::Ready(Ok(ticket)),
678            None => std::task::Poll::Pending
679        }
680    }
681
682    /// Every process generation that still holds a side of a stream here.
683    ///
684    /// The mirror of `Pool::owners`, and for the same caller: a supervisor
685    /// that has to decide what to report dead after losing its own record.
686    /// It says who is *in* the table, never who is alive.
687    pub fn owners(&self) -> Vec<(NodeId, Incarnation)> {
688        let mut seen: Vec<(NodeId, Incarnation)> = Vec::new();
689        for slot in self.table.slots() {
690            if slot.state.load(Ordering::Acquire) != SLOT_LIVE {
691                continue;
692            }
693            for side in 0..2 {
694                if slot.claimed[side].load(Ordering::Acquire) != SIDE_CLAIMED {
695                    continue;
696                }
697                let holder = (
698                    NodeId::new(slot.node[side].load(Ordering::Acquire)),
699                    Incarnation::new(slot.incarnation[side].load(Ordering::Acquire))
700                );
701                if !seen.contains(&holder) {
702                    seen.push(holder);
703                }
704            }
705        }
706        seen.sort_by_key(|(node, incarnation)| (node.get(), incarnation.get()));
707        seen
708    }
709
710    /// A confirmed death, reported by whoever supervises processes: every
711    /// side that incarnation of `node` held, in any lane, is finished. Its
712    /// unfinished writes become resets and its reads are gone, so a holder
713    /// of the other side wakes with an error; that holder keeps the slot
714    /// until it drops its handles, and only then is the slot reused. A
715    /// report for another incarnation of the same node touches nothing.
716    pub fn node_dead(
717        &self,
718        node: NodeId,
719        incarnation: Incarnation
720    ) {
721        self.table.node_dead(node.get(), incarnation.get());
722    }
723
724    /// This process took over its node from an earlier life: every side that
725    /// any other incarnation of this node held, in any lane, is finished, as
726    /// [`Streams::node_dead`] finishes one. The node id is inherited and the
727    /// incarnation is not, so what the node left behind is told apart from
728    /// this process's own by the stamp alone — no report from a supervisor
729    /// is needed.
730    ///
731    /// Call it once, before this process creates or opens anything. It is
732    /// sound only where a node is handed on after its holder is gone; a
733    /// node given to a second process while the first still runs would
734    /// have that process's streams ended under it.
735    pub fn supersede(&self) {
736        self.table.supersede();
737    }
738
739    /// Clear the table during quiescent owner boot and start a new epoch.
740    /// Every handle and every ticket from before goes stale.
741    pub fn reset_all(&self) {
742        self.table.reset_all();
743    }
744
745    fn ticket_for(
746        &self,
747        index: usize
748    ) -> Option<Ticket> {
749        let slot = &self.table.slots()[index];
750        let generation = slot.generation.load(Ordering::Acquire);
751        if !slot.is(generation) {
752            return None;
753        }
754        let lane_capacity = self.table.geometry().lane_capacity;
755        let id = StreamId::make(
756            self.table.kind(),
757            (index / lane_capacity) as u16,
758            (index % lane_capacity) as u32,
759            generation
760        );
761        Some(Ticket { id, side: Side::B, epoch: self.table.epoch() })
762    }
763
764    /// Remove the SHM object. Existing mappings stay valid until their
765    /// processes release them.
766    #[cfg(unix)]
767    pub fn unlink(&self) -> Result<()> {
768        self.table.unlink()
769    }
770
771    fn locate(
772        &self,
773        id: StreamId
774    ) -> Result<usize> {
775        let geometry = self.table.geometry();
776        if id.kind() != self.table.kind()
777            || usize::from(id.node()) >= geometry.fleet_capacity
778            || id.slot() as usize >= geometry.lane_capacity
779            || id.generation() == 0
780            || id.generation() > GENERATION_MASK
781        {
782            return Err(Error::Malformed(id.to_string()));
783        }
784        Ok(usize::from(id.node()) * geometry.lane_capacity + id.slot() as usize)
785    }
786}
787
788/// One held side. Dropped when both halves are gone; that releases the side.
789struct Handle {
790    table: Arc<Table>,
791    index: usize,
792    id: StreamId,
793    side: Side
794}
795
796impl Handle {
797    /// The other side's doorbell when `ring`, nobody's otherwise. Everything a
798    /// handle changes is news to its peer and never to itself; see
799    /// [`table::Ring`].
800    fn peer_if(
801        &self,
802        ring: bool
803    ) -> table::Ring {
804        if ring { table::Ring::Side(1 - self.side.index()) } else { table::Ring::Nobody }
805    }
806
807    fn slot(&self) -> Result<&Slot> {
808        let slot = &self.table.slots()[self.index];
809        if slot.is(self.id.generation()) { Ok(slot) } else { Err(Error::Stale(self.id)) }
810    }
811
812    fn direction(
813        slot: &Slot,
814        direction: usize
815    ) -> &Direction {
816        &slot.directions[direction]
817    }
818
819    /// One attempt at the direction this side writes into.
820    fn try_write(
821        &self,
822        buf: &[u8]
823    ) -> Result<usize> {
824        let slot = self.slot()?;
825        let direction = Self::direction(slot, self.side.write_direction());
826        let flags = direction.flags();
827        if flags & FLAG_RESET != 0 {
828            return Err(Error::Reset);
829        }
830        if flags & FLAG_FIN != 0 {
831            return Err(Error::Closed);
832        }
833        if flags & FLAG_READER_GONE != 0 {
834            return Err(Error::PeerGone);
835        }
836        let buffer_bytes = self.table.geometry().buffer_bytes;
837        let head = direction.head.load(Ordering::Relaxed);
838        let tail = direction.tail.load(Ordering::Acquire);
839        let free = buffer_bytes - (head - tail) as usize;
840        if buf.is_empty() {
841            return Ok(0);
842        }
843        if free == 0 {
844            return Err(Error::WouldBlock);
845        }
846        let len = buf.len().min(free);
847        let base = self.table.buffer(self.index, self.side.write_direction());
848        let start = (head as usize) & (buffer_bytes - 1);
849        let first = len.min(buffer_bytes - start);
850        // SAFETY: only this side writes this direction, and `[head, head+len)`
851        // is free space the reader will not touch until `head` moves.
852        unsafe {
853            std::ptr::copy_nonoverlapping(buf.as_ptr(), base.add(start), first);
854            std::ptr::copy_nonoverlapping(buf.as_ptr().add(first), base, len - first);
855        }
856        direction.head.store(head + len as u64, Ordering::SeqCst);
857        // A reader parks only on an empty ring: it looks, registers, looks
858        // again. Whether one could be parked is the tail *after* this
859        // commit, never the one read before the copy — the reader may have
860        // drained the ring and parked while we were copying into it, and
861        // that older tail would say "nobody to wake" about a task asleep on
862        // these very bytes. Each side orders its own store before the
863        // other's load, so at least one of the two sees the other: the
864        // reader sees the new head and does not park, or this sees the
865        // drained tail and rings.
866        std::sync::atomic::fence(Ordering::SeqCst);
867        let drained = direction.tail.load(Ordering::SeqCst) >= head;
868        self.table.notify(self.index, slot, direction, self.peer_if(drained));
869        Ok(len)
870    }
871
872    /// Publish `buf` as one indivisible write, or publish nothing.
873    fn try_write_exact(
874        &self,
875        buf: &[u8]
876    ) -> Result<()> {
877        let slot = self.slot()?;
878        let direction = Self::direction(slot, self.side.write_direction());
879        let flags = direction.flags();
880        if flags & FLAG_RESET != 0 {
881            return Err(Error::Reset);
882        }
883        if flags & FLAG_FIN != 0 {
884            return Err(Error::Closed);
885        }
886        if flags & FLAG_READER_GONE != 0 {
887            return Err(Error::PeerGone);
888        }
889        let buffer_bytes = self.table.geometry().buffer_bytes;
890        if buf.len() > buffer_bytes {
891            return Err(Error::Malformed(format!(
892                "atomic stream write is {} bytes; ring capacity is {buffer_bytes}",
893                buf.len()
894            )));
895        }
896        if buf.is_empty() {
897            return Ok(());
898        }
899        let head = direction.head.load(Ordering::Relaxed);
900        let tail = direction.tail.load(Ordering::Acquire);
901        if buffer_bytes - ((head - tail) as usize) < buf.len() {
902            return Err(Error::WouldBlock);
903        }
904        let base = self.table.buffer(self.index, self.side.write_direction());
905        let start = (head as usize) & (buffer_bytes - 1);
906        let first = buf.len().min(buffer_bytes - start);
907        // SAFETY: the complete frame fits in free ring space owned by this writer.
908        unsafe {
909            std::ptr::copy_nonoverlapping(buf.as_ptr(), base.add(start), first);
910            std::ptr::copy_nonoverlapping(buf.as_ptr().add(first), base, buf.len() - first);
911        }
912        direction.head.store(head + buf.len() as u64, Ordering::SeqCst);
913        std::sync::atomic::fence(Ordering::SeqCst);
914        let drained = direction.tail.load(Ordering::SeqCst) >= head;
915        self.table.notify(self.index, slot, direction, self.peer_if(drained));
916        Ok(())
917    }
918
919    /// One attempt at the direction this side reads from. `Ok(0)` with a
920    /// non-empty buffer is clean end of stream.
921    fn try_read(
922        &self,
923        buf: &mut [u8]
924    ) -> Result<usize> {
925        let slot = self.slot()?;
926        let direction = Self::direction(slot, self.side.read_direction());
927        let flags = direction.flags();
928        if flags & FLAG_RESET != 0 {
929            return Err(Error::Reset);
930        }
931        let head = direction.head.load(Ordering::Acquire);
932        let tail = direction.tail.load(Ordering::Relaxed);
933        let available = (head - tail) as usize;
934        if buf.is_empty() {
935            return Ok(0);
936        }
937        if available == 0 {
938            if flags & FLAG_FIN != 0 {
939                return Ok(0);
940            }
941            return Err(Error::WouldBlock);
942        }
943        let len = buf.len().min(available);
944        let base = self.table.buffer(self.index, self.side.read_direction());
945        let buffer_bytes = self.table.geometry().buffer_bytes;
946        let start = (tail as usize) & (buffer_bytes - 1);
947        let first = len.min(buffer_bytes - start);
948        // SAFETY: `[tail, head)` was published by the writer's `Release`
949        // store of `head`, acquired above, and is not rewritten until `tail`
950        // moves past it.
951        unsafe {
952            std::ptr::copy_nonoverlapping(base.add(start), buf.as_mut_ptr(), first);
953            std::ptr::copy_nonoverlapping(base, buf.as_mut_ptr().add(first), len - first);
954        }
955        direction.tail.store(tail + len as u64, Ordering::SeqCst);
956        // The mirror image, and the other half of that agreement: a writer
957        // parks only on a full ring, so the head read after this consume is
958        // what says whether one is parked on the space just freed.
959        std::sync::atomic::fence(Ordering::SeqCst);
960        let head_now = direction.head.load(Ordering::SeqCst);
961        let was_full = head_now - tail >= buffer_bytes as u64;
962        self.table.notify(self.index, slot, direction, self.peer_if(was_full));
963        Ok(len)
964    }
965
966    /// Consume exactly one complete frame, or consume nothing.
967    fn try_read_exact(
968        &self,
969        buf: &mut [u8]
970    ) -> Result<()> {
971        let slot = self.slot()?;
972        let direction = Self::direction(slot, self.side.read_direction());
973        let flags = direction.flags();
974        if flags & FLAG_RESET != 0 {
975            return Err(Error::Reset);
976        }
977        let buffer_bytes = self.table.geometry().buffer_bytes;
978        if buf.len() > buffer_bytes {
979            return Err(Error::Malformed(format!(
980                "atomic stream read is {} bytes; ring capacity is {buffer_bytes}",
981                buf.len()
982            )));
983        }
984        if buf.is_empty() {
985            return Ok(());
986        }
987        let head = direction.head.load(Ordering::Acquire);
988        let tail = direction.tail.load(Ordering::Relaxed);
989        let available = (head - tail) as usize;
990        if available < buf.len() {
991            if flags & FLAG_FIN != 0 {
992                return Err(Error::Malformed(format!(
993                    "stream ended with {available} bytes of a {} byte frame",
994                    buf.len()
995                )));
996            }
997            return Err(Error::WouldBlock);
998        }
999        let base = self.table.buffer(self.index, self.side.read_direction());
1000        let start = (tail as usize) & (buffer_bytes - 1);
1001        let first = buf.len().min(buffer_bytes - start);
1002        // SAFETY: the complete frame is within the writer-published range.
1003        unsafe {
1004            std::ptr::copy_nonoverlapping(base.add(start), buf.as_mut_ptr(), first);
1005            std::ptr::copy_nonoverlapping(base, buf.as_mut_ptr().add(first), buf.len() - first);
1006        }
1007        direction.tail.store(tail + buf.len() as u64, Ordering::SeqCst);
1008        std::sync::atomic::fence(Ordering::SeqCst);
1009        let head_now = direction.head.load(Ordering::SeqCst);
1010        let was_full = head_now - tail >= buffer_bytes as u64;
1011        self.table.notify(self.index, slot, direction, self.peer_if(was_full));
1012        Ok(())
1013    }
1014
1015    fn set_flag(
1016        &self,
1017        direction_index: usize,
1018        flag: u8
1019    ) {
1020        if let Ok(slot) = self.slot() {
1021            let direction = Self::direction(slot, direction_index);
1022            let before = direction.flags.fetch_or(flag, Ordering::SeqCst);
1023            if before & flag == 0 {
1024                let ring = flag != FLAG_READER_GONE || writer_may_park(direction, before);
1025                self.table.notify(self.index, slot, direction, self.peer_if(ring));
1026            }
1027        }
1028    }
1029
1030    /// Park until `ready` holds for the direction, or the stream ends.
1031    /// The same wait, bounded: `false` is the timeout and nothing else.
1032    #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1033    fn wait_until_timeout(
1034        &self,
1035        direction_index: usize,
1036        ready: impl Fn(&Direction) -> bool,
1037        timeout: Duration
1038    ) -> Result<bool> {
1039        let deadline = Instant::now() + timeout;
1040        loop {
1041            let slot = self.slot()?;
1042            let direction = Self::direction(slot, direction_index);
1043            if ready(direction) {
1044                return Ok(true);
1045            }
1046            let left = deadline.saturating_duration_since(Instant::now());
1047            if left.is_zero() {
1048                return Ok(false);
1049            }
1050            direction.waiters.fetch_add(1, Ordering::SeqCst);
1051            let since = direction.changes.load(Ordering::SeqCst);
1052            let outcome = if ready(direction) {
1053                Ok(true)
1054            } else {
1055                wait_on_timeout(&direction.changes, since, left)
1056            };
1057            direction.waiters.fetch_sub(1, Ordering::SeqCst);
1058            if !outcome? {
1059                // The deadline passed; one last look, since readiness may
1060                // have landed between the wait giving up and this line.
1061                let slot = self.slot()?;
1062                return Ok(ready(Self::direction(slot, direction_index)));
1063            }
1064        }
1065    }
1066
1067    fn wait_until(
1068        &self,
1069        direction_index: usize,
1070        ready: impl Fn(&Direction) -> bool
1071    ) -> Result<()> {
1072        loop {
1073            let slot = self.slot()?;
1074            let direction = Self::direction(slot, direction_index);
1075            if ready(direction) {
1076                return Ok(());
1077            }
1078            direction.waiters.fetch_add(1, Ordering::SeqCst);
1079            let since = direction.changes.load(Ordering::SeqCst);
1080            let outcome =
1081                if ready(direction) { Ok(()) } else { wait_on(&direction.changes, since) };
1082            direction.waiters.fetch_sub(1, Ordering::SeqCst);
1083            outcome?;
1084        }
1085    }
1086
1087    /// Check, register, check again: a change between the first look and
1088    /// the registration is not missed.
1089    fn poll_ready(
1090        &self,
1091        interest: Interest,
1092        ready: impl Fn(&Direction) -> bool,
1093        cx: &mut std::task::Context<'_>
1094    ) -> std::task::Poll<Result<()>> {
1095        let slot = match self.slot() {
1096            Ok(slot) => slot,
1097            Err(error) => return std::task::Poll::Ready(Err(error))
1098        };
1099        let direction = Self::direction(slot, interest.direction);
1100        if ready(direction) {
1101            return std::task::Poll::Ready(Ok(()));
1102        }
1103        if let Err(error) = self.table.register(self.index, interest, cx.waker()) {
1104            return std::task::Poll::Ready(Err(error));
1105        }
1106        // Pairs with the fence in `writer_may_park`: a flag set without a
1107        // doorbell is one this second look is guaranteed to see.
1108        std::sync::atomic::fence(Ordering::SeqCst);
1109        if ready(direction) { std::task::Poll::Ready(Ok(())) } else { std::task::Poll::Pending }
1110    }
1111}
1112
1113/// Whether a task could be parked writing into `direction` as its reader
1114/// goes — the only case in which the reader going has to ring a doorbell.
1115///
1116/// Not a writer that has finished: it will never write or wait again. And not
1117/// while nothing is queued: with the ring empty every write fits, so nobody is
1118/// waiting for room. The ordinary end of an exchange is both — the reader has
1119/// taken everything, and often the writer has already said so — and ringing
1120/// there woke two drivers per exchange for no one.
1121///
1122/// The fence pairs with the one in [`Handle::poll_ready`]: either this sees
1123/// bytes a writer queued before parking, or that writer's second look sees
1124/// the flag. The reader is gone, so `tail` no longer moves, and a ring seen
1125/// empty here fills only through writes that will see the flag first.
1126fn writer_may_park(
1127    direction: &Direction,
1128    before: u8
1129) -> bool {
1130    if before & (FLAG_FIN | FLAG_RESET) != 0 {
1131        return false;
1132    }
1133    std::sync::atomic::fence(Ordering::SeqCst);
1134    direction.head.load(Ordering::SeqCst) != direction.tail.load(Ordering::SeqCst)
1135}
1136
1137fn readable(direction: &Direction) -> bool {
1138    direction.head.load(Ordering::Acquire) != direction.tail.load(Ordering::Relaxed)
1139        || direction.flags() & (FLAG_FIN | FLAG_RESET) != 0
1140}
1141
1142fn writable(
1143    direction: &Direction,
1144    buffer_bytes: usize
1145) -> bool {
1146    let queued =
1147        (direction.head.load(Ordering::Relaxed) - direction.tail.load(Ordering::Acquire)) as usize;
1148    queued < buffer_bytes || direction.flags() & (FLAG_FIN | FLAG_RESET | FLAG_READER_GONE) != 0
1149}
1150
1151fn writable_exact(
1152    direction: &Direction,
1153    buffer_bytes: usize,
1154    needed: usize
1155) -> bool {
1156    let queued =
1157        (direction.head.load(Ordering::Relaxed) - direction.tail.load(Ordering::Acquire)) as usize;
1158    buffer_bytes.saturating_sub(queued) >= needed
1159        || direction.flags() & (FLAG_FIN | FLAG_RESET | FLAG_READER_GONE) != 0
1160}
1161
1162impl Drop for Handle {
1163    /// Both halves are gone: give the side back. The slot empties once no
1164    /// side holds it any more, and every address to this generation goes
1165    /// stale.
1166    fn drop(&mut self) {
1167        let slot = &self.table.slots()[self.index];
1168        if !slot.is(self.id.generation()) {
1169            return;
1170        }
1171        // Only a side that is still ours is ours to release: a death
1172        // report may have finished it already.
1173        if slot.claimed[self.side.index()]
1174            .compare_exchange(SIDE_CLAIMED, SIDE_RELEASED, Ordering::SeqCst, Ordering::SeqCst)
1175            .is_err()
1176        {
1177            return;
1178        }
1179        // Side A is claimed at creation, so a free B was simply never
1180        // taken; a released or dead side is gone. Only a claimed side keeps
1181        // the slot alive.
1182        let other = slot.claimed[1 - self.side.index()].load(Ordering::SeqCst);
1183        if other != SIDE_CLAIMED {
1184            let _ = slot.state.compare_exchange(
1185                SLOT_LIVE,
1186                SLOT_EMPTY,
1187                Ordering::SeqCst,
1188                Ordering::SeqCst
1189            );
1190            for direction in &slot.directions {
1191                // A parked waiter with a handle to this generation wakes
1192                // and finds it stale.
1193                direction.changes.fetch_add(1, Ordering::SeqCst);
1194                if direction.waiters.load(Ordering::SeqCst) > 0 {
1195                    wake_on(&direction.changes);
1196                }
1197            }
1198            self.table.registry.wake(self.index);
1199        }
1200    }
1201}
1202
1203/// Both ends of one side: what [`Streams::create`] and [`Streams::open`]
1204/// return. Reads and writes go through it directly, or [`Endpoint::split`]
1205/// gives the halves to two tasks.
1206pub struct Endpoint {
1207    read: ReadHalf,
1208    write: WriteHalf
1209}
1210
1211impl Endpoint {
1212    fn new(handle: Arc<Handle>) -> Self {
1213        Self {
1214            read: ReadHalf { handle: Arc::clone(&handle) },
1215            write: WriteHalf { handle, shut: false }
1216        }
1217    }
1218
1219    pub fn id(&self) -> StreamId {
1220        self.read.handle.id
1221    }
1222
1223    pub fn side(&self) -> Side {
1224        self.read.handle.side
1225    }
1226
1227    pub fn split(self) -> (ReadHalf, WriteHalf) {
1228        (self.read, self.write)
1229    }
1230
1231    /// Put a split endpoint back together.
1232    ///
1233    /// For a caller that keeps its streams between exchanges: the halves go
1234    /// out to whoever pumps the bodies and come back here, so the slot is
1235    /// never given up and never claimed again. Neither half is dropped on the
1236    /// way through, so neither terminal flag is set.
1237    ///
1238    /// Refused if the two halves are not the same side of the same stream,
1239    /// which would otherwise build an endpoint that reads one slot and writes
1240    /// another.
1241    pub fn rejoin(
1242        read: ReadHalf,
1243        write: WriteHalf
1244    ) -> Result<Self> {
1245        if read.id() != write.id() {
1246            return Err(Error::Stale(read.id()));
1247        }
1248        Ok(Self { read, write })
1249    }
1250
1251    /// Clear this creator-held endpoint for another peer and restore its
1252    /// process-local write state.
1253    ///
1254    /// [`Streams::rearm`] clears the shared directions after side B has let
1255    /// go. An endpoint that sent FIN also remembers that fact locally in its
1256    /// write half, so reusing the slot through the endpoint needs to clear
1257    /// both layers. Keeping that pairing here prevents a caller from
1258    /// successfully rearming SHM and then finding its retained writer still
1259    /// closed.
1260    ///
1261    /// Only side A, the creator-held side, is reusable. The ticket remains
1262    /// valid because rearming does not change the stream generation.
1263    pub fn rearm(
1264        &mut self,
1265        streams: &Streams
1266    ) -> Result<()> {
1267        if self.side() != Side::A {
1268            return Err(Error::Malformed(
1269                "only the creator side of a stream can be rearmed".to_owned()
1270            ));
1271        }
1272        streams.rearm(self.id())?;
1273        self.write.shut = false;
1274        Ok(())
1275    }
1276
1277    pub fn try_read(
1278        &self,
1279        buf: &mut [u8]
1280    ) -> Result<usize> {
1281        self.read.try_read(buf)
1282    }
1283
1284    pub fn try_read_exact(
1285        &self,
1286        buf: &mut [u8]
1287    ) -> Result<()> {
1288        self.read.try_read_exact(buf)
1289    }
1290
1291    pub fn blocking_read(
1292        &self,
1293        buf: &mut [u8]
1294    ) -> Result<usize> {
1295        self.read.blocking_read(buf)
1296    }
1297
1298    pub fn blocking_read_chunk(
1299        &self,
1300        max: usize
1301    ) -> Result<Bytes> {
1302        self.read.blocking_read_chunk(max)
1303    }
1304
1305    pub fn try_write(
1306        &self,
1307        buf: &[u8]
1308    ) -> Result<usize> {
1309        self.write.try_write(buf)
1310    }
1311
1312    pub fn try_write_exact(
1313        &self,
1314        buf: &[u8]
1315    ) -> Result<()> {
1316        self.write.try_write_exact(buf)
1317    }
1318
1319    pub fn blocking_write(
1320        &self,
1321        buf: &[u8]
1322    ) -> Result<usize> {
1323        self.write.blocking_write(buf)
1324    }
1325
1326    pub fn blocking_write_all(
1327        &self,
1328        buf: &[u8]
1329    ) -> Result<()> {
1330        self.write.blocking_write_all(buf)
1331    }
1332
1333    pub fn finish(&mut self) -> Result<()> {
1334        self.write.finish()
1335    }
1336
1337    pub fn reset(&mut self) {
1338        self.write.reset()
1339    }
1340}
1341
1342/// The reading end of one side. Dropping it tells the peer's writer that
1343/// nothing more will be read.
1344pub struct ReadHalf {
1345    handle: Arc<Handle>
1346}
1347
1348impl ReadHalf {
1349    pub fn id(&self) -> StreamId {
1350        self.handle.id
1351    }
1352
1353    /// Read what is there now. `Ok(0)` is clean end of stream;
1354    /// [`Error::WouldBlock`] means nothing yet.
1355    pub fn try_read(
1356        &self,
1357        buf: &mut [u8]
1358    ) -> Result<usize> {
1359        self.handle.try_read(buf)
1360    }
1361
1362    /// Read the complete buffer atomically. When it is not all available,
1363    /// returns [`Error::WouldBlock`] without consuming a prefix.
1364    pub fn try_read_exact(
1365        &self,
1366        buf: &mut [u8]
1367    ) -> Result<()> {
1368        self.handle.try_read_exact(buf)
1369    }
1370
1371    /// Park until something can be read, then read it. `Ok(0)` is clean
1372    /// end of stream. Blocks the thread; an async runtime uses `AsyncRead`.
1373    pub fn blocking_read(
1374        &self,
1375        buf: &mut [u8]
1376    ) -> Result<usize> {
1377        loop {
1378            match self.handle.try_read(buf) {
1379                Err(Error::WouldBlock) => self.wait_readable()?,
1380                other => return other
1381            }
1382        }
1383    }
1384
1385    /// Tell the peer's writer that nothing more will be read, without letting
1386    /// go of the stream.
1387    ///
1388    /// What dropping this half says, for a holder that keeps its halves
1389    /// between exchanges. Without it a writer the reader has given up on —
1390    /// a client gone mid-response — waits on a full ring that nobody will
1391    /// empty. Re-arming the stream clears it again.
1392    pub fn stop_reading(&self) {
1393        self.handle.set_flag(self.handle.side.read_direction(), FLAG_READER_GONE);
1394    }
1395
1396    /// Up to `max` bytes as one owned chunk; empty at clean end of stream.
1397    pub fn blocking_read_chunk(
1398        &self,
1399        max: usize
1400    ) -> Result<Bytes> {
1401        let mut chunk = BytesMut::zeroed(max);
1402        let len = self.blocking_read(&mut chunk)?;
1403        chunk.truncate(len);
1404        Ok(chunk.freeze())
1405    }
1406
1407    /// The same wait, bounded: `false` is the timeout and nothing else.
1408    #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1409    pub fn wait_readable_timeout(
1410        &self,
1411        timeout: Duration
1412    ) -> Result<bool> {
1413        self.handle.wait_until_timeout(self.handle.side.read_direction(), readable, timeout)
1414    }
1415
1416    /// Park until a read would make progress or the direction ended.
1417    pub fn wait_readable(&self) -> Result<()> {
1418        self.handle.wait_until(self.handle.side.read_direction(), readable)
1419    }
1420
1421    /// Readiness for a task: `Ready` when a read would make progress or the
1422    /// direction ended, otherwise the waker is registered and `Pending`
1423    /// comes back. Runtime-neutral; the `tokio` feature builds `AsyncRead`
1424    /// on it.
1425    pub fn poll_readable(
1426        &self,
1427        cx: &mut std::task::Context<'_>
1428    ) -> std::task::Poll<Result<()>> {
1429        self.handle.poll_ready(
1430            Interest { direction: self.handle.side.read_direction(), writer: false },
1431            readable,
1432            cx
1433        )
1434    }
1435}
1436
1437impl Drop for ReadHalf {
1438    fn drop(&mut self) {
1439        self.handle.set_flag(self.handle.side.read_direction(), FLAG_READER_GONE);
1440    }
1441}
1442
1443/// The writing end of one side. Dropping it without [`WriteHalf::finish`]
1444/// resets the direction, as a dropped socket would.
1445pub struct WriteHalf {
1446    handle: Arc<Handle>,
1447    shut: bool
1448}
1449
1450impl WriteHalf {
1451    pub fn id(&self) -> StreamId {
1452        self.handle.id
1453    }
1454
1455    /// Write what fits now; [`Error::WouldBlock`] when the ring is full.
1456    pub fn try_write(
1457        &self,
1458        buf: &[u8]
1459    ) -> Result<usize> {
1460        self.handle.try_write(buf)
1461    }
1462
1463    /// Write the complete buffer atomically. When it does not all fit,
1464    /// returns [`Error::WouldBlock`] without publishing a prefix.
1465    pub fn try_write_exact(
1466        &self,
1467        buf: &[u8]
1468    ) -> Result<()> {
1469        self.handle.try_write_exact(buf)
1470    }
1471
1472    /// Park until something fits, then write it. Blocks the thread.
1473    pub fn blocking_write(
1474        &self,
1475        buf: &[u8]
1476    ) -> Result<usize> {
1477        loop {
1478            match self.handle.try_write(buf) {
1479                Err(Error::WouldBlock) => self.wait_writable()?,
1480                other => return other
1481            }
1482        }
1483    }
1484
1485    /// Write everything, waiting as needed. A failure part-way has already
1486    /// committed a prefix; nothing is replayed.
1487    pub fn blocking_write_all(
1488        &self,
1489        mut buf: &[u8]
1490    ) -> Result<()> {
1491        while !buf.is_empty() {
1492            let written = self.blocking_write(buf)?;
1493            buf = &buf[written..];
1494        }
1495        Ok(())
1496    }
1497
1498    /// No more bytes from this side (FIN); what is buffered still reaches
1499    /// the reader, then it sees a clean end. The other direction is
1500    /// unaffected. `AsyncWriteExt::shutdown` does the same.
1501    pub fn finish(&mut self) -> Result<()> {
1502        self.shut = true;
1503        self.handle.set_flag(self.handle.side.write_direction(), FLAG_FIN);
1504        Ok(())
1505    }
1506
1507    /// Abandon the direction: the reader gets an error and buffered bytes
1508    /// are discarded.
1509    pub fn reset(&mut self) {
1510        self.shut = true;
1511        self.handle.set_flag(self.handle.side.write_direction(), FLAG_RESET);
1512    }
1513
1514    /// The same wait, bounded: `false` is the timeout and nothing else,
1515    /// which is how a caller with a deadline of its own gives up without
1516    /// giving up its stream.
1517    #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1518    pub fn wait_writable_timeout(
1519        &self,
1520        timeout: Duration
1521    ) -> Result<bool> {
1522        let buffer_bytes = self.handle.table.geometry().buffer_bytes;
1523        self.handle.wait_until_timeout(
1524            self.handle.side.write_direction(),
1525            |direction| writable(direction, buffer_bytes),
1526            timeout
1527        )
1528    }
1529
1530    /// Park until a write would make progress or the direction ended.
1531    pub fn wait_writable(&self) -> Result<()> {
1532        let buffer_bytes = self.handle.table.geometry().buffer_bytes;
1533        self.handle.wait_until(self.handle.side.write_direction(), |direction| {
1534            writable(direction, buffer_bytes)
1535        })
1536    }
1537
1538    /// Park until an atomic write of `needed` bytes can be attempted.
1539    pub fn wait_writable_exact(
1540        &self,
1541        needed: usize
1542    ) -> Result<()> {
1543        let buffer_bytes = self.handle.table.geometry().buffer_bytes;
1544        if needed > buffer_bytes {
1545            return Err(Error::Malformed(format!(
1546                "atomic stream write is {needed} bytes; ring capacity is {buffer_bytes}"
1547            )));
1548        }
1549        self.handle.wait_until(self.handle.side.write_direction(), |direction| {
1550            writable_exact(direction, buffer_bytes, needed)
1551        })
1552    }
1553
1554    /// Readiness for a task: `Ready` when a write would make progress or
1555    /// the direction ended, otherwise the waker is registered and `Pending`
1556    /// comes back. Runtime-neutral; the `tokio` feature builds `AsyncWrite`
1557    /// on it.
1558    pub fn poll_writable(
1559        &self,
1560        cx: &mut std::task::Context<'_>
1561    ) -> std::task::Poll<Result<()>> {
1562        let buffer_bytes = self.handle.table.geometry().buffer_bytes;
1563        self.handle.poll_ready(
1564            Interest { direction: self.handle.side.write_direction(), writer: true },
1565            |direction| writable(direction, buffer_bytes),
1566            cx
1567        )
1568    }
1569
1570    /// Task readiness for an indivisible write of `needed` bytes.
1571    pub fn poll_writable_exact(
1572        &self,
1573        needed: usize,
1574        cx: &mut std::task::Context<'_>
1575    ) -> std::task::Poll<Result<()>> {
1576        let buffer_bytes = self.handle.table.geometry().buffer_bytes;
1577        if needed > buffer_bytes {
1578            return std::task::Poll::Ready(Err(Error::Malformed(format!(
1579                "atomic stream write is {needed} bytes; ring capacity is {buffer_bytes}"
1580            ))));
1581        }
1582        self.handle.poll_ready(
1583            Interest { direction: self.handle.side.write_direction(), writer: true },
1584            |direction| writable_exact(direction, buffer_bytes, needed),
1585            cx
1586        )
1587    }
1588}
1589
1590impl Drop for WriteHalf {
1591    fn drop(&mut self) {
1592        if !self.shut {
1593            self.handle.set_flag(self.handle.side.write_direction(), FLAG_RESET);
1594        }
1595    }
1596}
1597
1598/// Whether this build can park on a shared word. A table refuses to open
1599/// where it cannot: a sleep loop wearing the shape of a wait is worse
1600/// than a clear no, and nothing above here should have to ask again.
1601#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1602pub(crate) fn waits_supported() -> bool {
1603    orbit_core::sync::supported()
1604}
1605
1606#[cfg(not(any(target_os = "linux", target_os = "freebsd", target_os = "macos")))]
1607pub(crate) fn waits_supported() -> bool {
1608    false
1609}
1610
1611/// Park until `word` no longer holds `expected`, through the platform's
1612/// shared address wait. There is no polling fallback: a table refuses to
1613/// open where the platform cannot wait, so by here it can.
1614#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1615/// Park until the word moves or `timeout` passes. `false` is the
1616/// timeout and nothing else.
1617#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1618pub(crate) fn wait_on_timeout(
1619    word: &AtomicU32,
1620    expected: u32,
1621    timeout: Duration
1622) -> Result<bool> {
1623    orbit_core::sync::wait_word_timeout(word, expected, timeout).map_err(Error::Io)
1624}
1625
1626pub(crate) fn wait_on(
1627    word: &AtomicU32,
1628    expected: u32
1629) -> Result<()> {
1630    orbit_core::sync::wait_word(word, expected).map_err(Error::Io)
1631}
1632
1633#[cfg(not(any(target_os = "linux", target_os = "freebsd", target_os = "macos")))]
1634pub(crate) fn wait_on(
1635    _word: &AtomicU32,
1636    _expected: u32
1637) -> Result<()> {
1638    Err(Error::Io(std::io::Error::new(
1639        std::io::ErrorKind::Unsupported,
1640        "orbit-stream needs a platform that can wait on a shared word"
1641    )))
1642}
1643
1644/// Wake everyone parked on `word`; nothing to do where nobody can park.
1645pub(crate) fn wake_on(word: &AtomicU32) {
1646    #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1647    let _ = orbit_core::sync::wake_word(word);
1648    #[cfg(not(any(target_os = "linux", target_os = "freebsd", target_os = "macos")))]
1649    let _ = word;
1650}
1651
1652pub(crate) fn lock_unpoisoned<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
1653    mutex.lock().unwrap_or_else(|error| error.into_inner())
1654}
1655
1656#[cfg(test)]
1657mod tests {
1658    use std::sync::Arc;
1659    use std::time::Duration;
1660
1661    use orbit_core::{Fleet, NodeId};
1662
1663    use super::{
1664        Error, Incarnation, STREAM_BUFFER_BYTES, STREAM_LANE_CAPACITY, Side, Streams, Ticket
1665    };
1666
1667    fn streams(name: &'static str) -> Streams {
1668        Streams::new(Arc::new(Fleet::join(name, 2).unwrap()), Incarnation::new(1)).unwrap()
1669    }
1670
1671    #[test]
1672    fn bytes_cross_in_order_and_end_cleanly() {
1673        let streams = streams("stream-order");
1674        let (mut a, ticket) = streams.create().unwrap();
1675        assert_eq!(ticket.side, Side::B);
1676        assert_eq!(ticket.id.node(), 0);
1677        assert_eq!(ticket.epoch, streams.epoch());
1678        let b = streams.open(ticket).unwrap();
1679
1680        a.blocking_write_all(b"hello, ").unwrap();
1681        a.blocking_write_all(b"world").unwrap();
1682        a.finish().unwrap();
1683
1684        let mut out = Vec::new();
1685        let mut buf = [0_u8; 4];
1686        loop {
1687            let n = b.blocking_read(&mut buf).unwrap();
1688            if n == 0 {
1689                break;
1690            }
1691            out.extend_from_slice(&buf[..n]);
1692        }
1693        assert_eq!(out, b"hello, world");
1694        // The other direction is its own thing.
1695        b.blocking_write_all(b"back").unwrap();
1696        assert_eq!(a.blocking_read_chunk(16).unwrap().as_ref(), b"back");
1697    }
1698
1699    #[test]
1700    fn a_ticket_prints_and_parses() {
1701        let streams = streams("stream-ticket");
1702        let (_a, ticket) = streams.create().unwrap();
1703        let text = ticket.to_string();
1704        assert!(text.contains("/b/"), "{text}");
1705        assert_eq!(text.parse::<Ticket>().unwrap(), ticket);
1706        assert!(matches!("nope".parse::<Ticket>(), Err(Error::Malformed(_))));
1707        assert!(matches!("x/b".parse::<Ticket>(), Err(Error::Malformed(_))));
1708    }
1709
1710    #[test]
1711    fn a_side_is_held_once_and_a_released_stream_goes_stale() {
1712        let streams = streams("stream-claim");
1713        let (a, ticket) = streams.create().unwrap();
1714        let b = streams.open(ticket).unwrap();
1715        assert!(matches!(streams.open(ticket), Err(Error::AlreadyClaimed(_))));
1716        assert!(streams.is_live(ticket.id));
1717
1718        drop(a);
1719        // A's write half went without finishing: B reads a reset.
1720        let mut buf = [0_u8; 1];
1721        assert!(matches!(b.blocking_read(&mut buf), Err(Error::Reset)));
1722        drop(b);
1723        assert!(!streams.is_live(ticket.id));
1724        assert!(matches!(streams.open(ticket), Err(Error::Stale(_))));
1725
1726        // Once the lane wraps, the slot is reused under a new generation and
1727        // the old ticket stays stale.
1728        for _ in 0..STREAM_LANE_CAPACITY - 1 {
1729            drop(streams.create().unwrap());
1730        }
1731        let (_a2, ticket2) = streams.create().unwrap();
1732        assert_eq!(ticket2.id.slot(), ticket.id.slot());
1733        assert_ne!(ticket2.id.generation(), ticket.id.generation());
1734        assert!(matches!(streams.open(ticket), Err(Error::Stale(_))));
1735    }
1736
1737    #[test]
1738    fn a_reset_starts_a_new_epoch_and_refuses_old_tickets() {
1739        let streams = streams("stream-epoch");
1740        let (_a, ticket) = streams.create().unwrap();
1741        let before = streams.epoch();
1742        streams.reset_all();
1743        assert!(streams.epoch() > before);
1744        // The slot is free again and reinstalled from generation one, which
1745        // is exactly what the old ticket names; the epoch tells them apart.
1746        let (_a2, ticket2) = streams.create().unwrap();
1747        assert_eq!(ticket2.id, ticket.id);
1748        assert_ne!(ticket2.epoch, ticket.epoch);
1749        assert!(matches!(streams.open(ticket), Err(Error::Stale(_))));
1750        assert!(streams.open(ticket2).is_ok());
1751    }
1752
1753    #[test]
1754    fn a_slot_whose_generation_runs_out_is_not_reused() {
1755        // GENERATION_LIMIT is 4 under test: each slot can be installed four
1756        // times in one epoch, then it sits out until the next epoch.
1757        let streams = streams("stream-exhaust");
1758        let limit = super::layout::GENERATION_LIMIT as usize;
1759        for _ in 0..STREAM_LANE_CAPACITY * limit {
1760            drop(streams.create().unwrap());
1761        }
1762        assert!(matches!(streams.create(), Err(Error::Full { .. })));
1763        streams.reset_all();
1764        let (_a, ticket) = streams.create().unwrap();
1765        assert_eq!(ticket.id.generation(), 1);
1766    }
1767
1768    #[test]
1769    fn a_full_ring_blocks_the_writer_and_wraps_correctly() {
1770        let streams = streams("stream-wrap");
1771        let (a, ticket) = streams.create().unwrap();
1772        let b = streams.open(ticket).unwrap();
1773
1774        let filler = vec![7_u8; STREAM_BUFFER_BYTES];
1775        assert_eq!(a.try_write(&filler).unwrap(), STREAM_BUFFER_BYTES);
1776        assert!(matches!(a.try_write(b"x"), Err(Error::WouldBlock)));
1777
1778        let mut buf = vec![0_u8; STREAM_BUFFER_BYTES - 5];
1779        assert_eq!(b.blocking_read(&mut buf).unwrap(), STREAM_BUFFER_BYTES - 5);
1780        // Room again; the next write wraps around the end of the ring.
1781        let tail = (0..STREAM_BUFFER_BYTES).map(|i| i as u8).collect::<Vec<_>>();
1782        assert_eq!(a.try_write(&tail).unwrap(), STREAM_BUFFER_BYTES - 5);
1783
1784        let mut rest = vec![0_u8; 5];
1785        assert_eq!(b.blocking_read(&mut rest).unwrap(), 5);
1786        assert_eq!(rest, vec![7_u8; 5]);
1787        let mut wrapped = vec![0_u8; STREAM_BUFFER_BYTES];
1788        let mut got = 0;
1789        while got < STREAM_BUFFER_BYTES - 5 {
1790            got += b.blocking_read(&mut wrapped[got..]).unwrap();
1791        }
1792        assert_eq!(&wrapped[..got], &tail[..got]);
1793    }
1794
1795    #[test]
1796    fn exact_frames_never_publish_or_consume_a_prefix() {
1797        let streams = streams("stream-exact-frame");
1798        let (a, ticket) = streams.create().unwrap();
1799        let b = streams.open(ticket).unwrap();
1800
1801        let filler = vec![7_u8; STREAM_BUFFER_BYTES - 32];
1802        a.try_write_exact(&filler).unwrap();
1803        assert!(matches!(a.try_write_exact(&[9_u8; 64]), Err(Error::WouldBlock)));
1804        let mut consumed = vec![0_u8; filler.len()];
1805        b.try_read_exact(&mut consumed).unwrap();
1806        assert_eq!(consumed, filler);
1807        assert!(matches!(b.try_read(&mut [0_u8; 1]), Err(Error::WouldBlock)));
1808
1809        a.try_write_exact(&[3_u8; 32]).unwrap();
1810        assert!(matches!(b.try_read_exact(&mut [0_u8; 64]), Err(Error::WouldBlock)));
1811        let mut frame = [0_u8; 32];
1812        b.try_read_exact(&mut frame).unwrap();
1813        assert_eq!(frame, [3_u8; 32]);
1814    }
1815
1816    #[test]
1817    fn a_blocked_reader_is_woken_by_a_write_in_another_thread() {
1818        let streams = streams("stream-wake");
1819        let (a, ticket) = streams.create().unwrap();
1820        let b = streams.open(ticket).unwrap();
1821        let reader = std::thread::spawn(move || {
1822            let mut buf = [0_u8; 8];
1823            let n = b.blocking_read(&mut buf).unwrap();
1824            buf[..n].to_vec()
1825        });
1826        std::thread::sleep(Duration::from_millis(30));
1827        a.blocking_write_all(b"ping").unwrap();
1828        assert_eq!(reader.join().unwrap(), b"ping");
1829    }
1830
1831    #[test]
1832    fn a_reader_gone_fails_the_writer_and_a_lane_fills_up() {
1833        let streams = streams("stream-gone");
1834        let (a, ticket) = streams.create().unwrap();
1835        let b = streams.open(ticket).unwrap();
1836        let (b_read, _b_write) = b.split();
1837        drop(b_read);
1838        assert!(matches!(a.blocking_write(b"nobody"), Err(Error::PeerGone)));
1839
1840        let mut held = vec![a];
1841        for _ in 1..STREAM_LANE_CAPACITY {
1842            held.push(streams.create().unwrap().0);
1843        }
1844        assert!(matches!(streams.create(), Err(Error::Full { .. })));
1845    }
1846
1847    #[test]
1848    fn a_death_report_ends_that_incarnations_sides_only() {
1849        let streams = streams("stream-dead");
1850        let (a, ticket) = streams.create().unwrap();
1851        let b = streams.open(ticket).unwrap();
1852        b.blocking_write_all(b"partial").unwrap();
1853
1854        // Another incarnation of the same node: nothing happens.
1855        streams.node_dead(NodeId::ZERO, Incarnation::new(2));
1856        assert_eq!(a.blocking_read_chunk(16).unwrap().as_ref(), b"partial");
1857
1858        // Our incarnation died (as a supervisor would report about a peer):
1859        // both sides here are stamped with it, so both end and the slot
1860        // empties at once. A parked reader wakes with an error either way;
1861        // the two-incarnation case, where the survivor keeps the slot and
1862        // reads a reset, is the shared-memory test.
1863        let reader = std::thread::spawn(move || {
1864            let mut buf = [0_u8; 8];
1865            a.blocking_read(&mut buf).map(|_| ())
1866        });
1867        std::thread::sleep(Duration::from_millis(30));
1868        streams.node_dead(NodeId::ZERO, Incarnation::new(1));
1869        assert!(matches!(reader.join().unwrap(), Err(Error::Reset | Error::Stale(_))));
1870        assert!(matches!(b.try_write(b"x"), Err(Error::Reset | Error::Stale(_))));
1871        assert!(matches!(streams.open(ticket), Err(Error::AlreadyClaimed(_) | Error::Stale(_))));
1872        drop(b);
1873        assert!(!streams.is_live(ticket.id));
1874    }
1875
1876    #[test]
1877    fn an_offer_is_taken_once_and_a_stale_offer_is_skipped() {
1878        let streams = streams("stream-offer");
1879        assert!(streams.take_offer().is_none());
1880        let (a, ticket) = streams.create().unwrap();
1881        streams.offer(ticket, NodeId::ZERO).unwrap();
1882        assert_eq!(streams.take_offer(), Some(ticket));
1883        assert!(streams.take_offer().is_none());
1884
1885        let (gone, stale) = streams.create().unwrap();
1886        streams.offer(stale, NodeId::ZERO).unwrap();
1887        drop(gone);
1888        assert!(streams.take_offer().is_none());
1889
1890        let taker = streams.clone();
1891        let waiter = std::thread::spawn(move || taker.blocking_take_offer());
1892        std::thread::sleep(Duration::from_millis(30));
1893        streams.offer(ticket, NodeId::ZERO).unwrap();
1894        assert_eq!(waiter.join().unwrap().unwrap(), ticket);
1895        drop(a);
1896    }
1897
1898    #[test]
1899    fn a_contested_claim_goes_to_exactly_one_opener() {
1900        let streams = streams("stream-race");
1901        for _ in 0..50 {
1902            let (_a, ticket) = streams.create().unwrap();
1903            let openers = (0..4)
1904                .map(|_| {
1905                    let streams = streams.clone();
1906                    std::thread::spawn(move || streams.open(ticket).is_ok())
1907                })
1908                .collect::<Vec<_>>();
1909            let won =
1910                openers.into_iter().map(|opener| opener.join().unwrap()).filter(|won| *won).count();
1911            assert_eq!(won, 1);
1912        }
1913    }
1914
1915    #[test]
1916    fn random_sizes_concatenate_exactly() {
1917        let streams = streams("stream-random");
1918        let (mut a, ticket) = streams.create().unwrap();
1919        let b = streams.open(ticket).unwrap();
1920        let payload =
1921            (0..300_000_u32).map(|i| (i.wrapping_mul(2654435761) >> 13) as u8).collect::<Vec<_>>();
1922        let expected = payload.clone();
1923        let writer = std::thread::spawn(move || {
1924            let mut rest = &payload[..];
1925            let mut step = 1_usize;
1926            while !rest.is_empty() {
1927                let len = step.min(rest.len());
1928                a.blocking_write_all(&rest[..len]).unwrap();
1929                rest = &rest[len..];
1930                step = (step * 7 + 13) % 9_001 + 1;
1931            }
1932            a.finish().unwrap();
1933        });
1934        let mut out = Vec::new();
1935        let mut step = 3_usize;
1936        loop {
1937            let chunk = b.blocking_read_chunk(step).unwrap();
1938            if chunk.is_empty() {
1939                break;
1940            }
1941            out.extend_from_slice(&chunk);
1942            step = (step * 5 + 11) % 4_099 + 1;
1943        }
1944        writer.join().unwrap();
1945        assert_eq!(out, expected);
1946    }
1947}