Skip to main content

lace_rt/
transport.rs

1//! Connected ILTP transport over host-established byte IO.
2
3use std::io::{Read, Write};
4
5use lace_sans_io::core::PeerInput;
6use mio::event::Source;
7
8mod stream;
9
10pub use lace_sans_io::core::CarrierRole;
11pub(crate) use stream::StreamTransport;
12
13pub(crate) const TRANSPORT: mio::Token = mio::Token(0);
14
15/// Settles adopted local-to-peer output at clean completion.
16///
17/// After final output is written and flushed, bounded completion and clean peer
18/// closure call `shutdown_write` once; a split writer is then dropped. Bounded
19/// completion reads the peer direction to its end before releasing the IO.
20/// Shutdown must settle output the IO still owns and report its failure, which
21/// fails the operation unless the peer direction ended cleanly (Lace-050).
22/// Cancellation and failure release the IO without shutdown.
23pub trait WriteShutdown {
24    fn shutdown_write(&mut self) -> std::io::Result<()>;
25}
26
27impl WriteShutdown for mio::net::TcpStream {
28    fn shutdown_write(&mut self) -> std::io::Result<()> {
29        self.shutdown(std::net::Shutdown::Write)
30    }
31}
32
33#[cfg(unix)]
34impl WriteShutdown for mio::net::UnixStream {
35    fn shutdown_write(&mut self) -> std::io::Result<()> {
36        self.shutdown(std::net::Shutdown::Write)
37    }
38}
39
40/// A pipe has no half-close: written bytes are kernel-owned, and dropping
41/// the split writer after shutdown closes the write end.
42#[cfg(unix)]
43impl WriteShutdown for mio::unix::pipe::Sender {
44    fn shutdown_write(&mut self) -> std::io::Result<()> {
45        Ok(())
46    }
47}
48
49pub(crate) enum TransportActivity {
50    Progress,
51    Input(PeerInput),
52    Eof,
53    Idle,
54}
55
56impl crate::ConnectedPeer {
57    /// Adopt one nonblocking Mio source as a move-only connected peer.
58    ///
59    /// `io` must already be configured for nonblocking operation.
60    pub fn from_duplex<IO>(
61        role: CarrierRole,
62        io: IO,
63        options: crate::ConnectedPeerOptions,
64    ) -> crate::Result<Self>
65    where
66        IO: Read + Write + WriteShutdown + Source + 'static,
67    {
68        let inputs = crate::runtime::connected_inputs(options)?;
69        Ok(Self {
70            transport: Box::new(StreamTransport::raw_duplex(io)),
71            role,
72            inputs,
73        })
74    }
75
76    /// Adopt independent nonblocking Mio input and output sources.
77    ///
78    /// `reader` is peer-to-local and `writer` is local-to-peer. Both sources
79    /// must already be configured for nonblocking operation.
80    pub fn from_split<R, W>(
81        role: CarrierRole,
82        reader: R,
83        writer: W,
84        options: crate::ConnectedPeerOptions,
85    ) -> crate::Result<Self>
86    where
87        R: Read + Source + 'static,
88        W: Write + WriteShutdown + Source + 'static,
89    {
90        let inputs = crate::runtime::connected_inputs(options)?;
91        Ok(Self {
92            transport: Box::new(StreamTransport::raw_split(reader, writer)),
93            role,
94            inputs,
95        })
96    }
97}