Skip to main content

pipecrab_runtime/
outbound.rs

1use std::sync::atomic::{AtomicU64, Ordering};
2
3use futures::channel::mpsc::{SendError, Sender};
4use futures::sink::SinkExt;
5use pipecrab_core::{DataFrame, Direction, SystemFrame};
6
7use crate::inbound::Stamped;
8
9/// The send surface of a stage: typed sends for the data and system lanes.
10///
11/// `send_data` targets the downstream data lane; `send_system` targets the
12/// system lane with an explicit [`Direction`]. Every send stamps its frame from
13/// one per-link counter shared by both lanes, which is what lets the receiving
14/// side's flush distinguish frames queued before an `Interrupt` from frames
15/// queued after it.
16///
17/// Constructed only by [`link`](crate::link).
18pub struct Outbound {
19    /// Downstream data channel.
20    pub(crate) data: Sender<Stamped<DataFrame>>,
21    /// Bidirectional system channel.
22    pub(crate) sys: Sender<Stamped<(Direction, SystemFrame)>>,
23    /// Per-link monotonic stamp shared by both lanes. Atomic so sends work
24    /// through `&self`; sends on one `Outbound` are not otherwise synchronised,
25    /// so issue them sequentially.
26    pub(crate) seq: AtomicU64,
27}
28
29impl Outbound {
30    /// Send a data frame downstream.
31    ///
32    /// Takes `&self` (not `&mut self`) so a stage can send while it is borrowed
33    /// immutably by the run loop. `futures`' `Sink::send` needs `&mut`, so we
34    /// send on a cheap clone of the shared sender; clones feed the same channel.
35    pub async fn send_data(&self, frame: DataFrame) -> Result<(), SendError> {
36        let seq = self.seq.fetch_add(1, Ordering::Relaxed);
37        self.data.clone().send(Stamped { seq, frame }).await
38    }
39
40    /// Send a system frame in the given direction. Takes `&self` for the same
41    /// reason as [`send_data`](Self::send_data).
42    pub async fn send_system(&self, dir: Direction, frame: SystemFrame) -> Result<(), SendError> {
43        let seq = self.seq.fetch_add(1, Ordering::Relaxed);
44        self.sys
45            .clone()
46            .send(Stamped {
47                seq,
48                frame: (dir, frame),
49            })
50            .await
51    }
52}