Skip to main content

darkbio_wire/memory/
mod.rs

1// wire-rs: encrypted protocol between Ark and host
2// Copyright 2026 Dark Bio AG. All rights reserved.
3//
4// Use of this source code is governed by a BSD-style
5// license that can be found in the LICENSE file.
6
7//! Portable in-memory streams for local connections, emulators and tests.
8//!
9//! These streams synchronize through the clock's locks, with no sockets or
10//! worker threads. Split an endpoint with [`Duplex::into_halves`] for plain I/O.
11
12use crate::transport::{Read, Stream, Write};
13use darkbio_clock::Clock;
14use darkbio_clock::sync::{Condvar, Mutex, MutexGuard};
15use std::collections::VecDeque;
16use std::io;
17use std::sync::Arc;
18use std::time::Instant;
19
20/// One endpoint of an in-memory duplex connection, ready for a
21/// [`Client`](crate::transport::Client) or [`Server`](crate::transport::Server).
22pub type Duplex = Stream<Reader, Writer>;
23
24impl Duplex {
25    /// Takes the reader and writer out of the stream without closing either half.
26    ///
27    /// Each half closes its own direction on drop. Dropping the writer lets the
28    /// peer drain accepted output before EOF; dropping the reader refuses
29    /// further peer writes. Obtain a [`crate::transport::Closer`] with
30    /// [`Self::closer`] before splitting if you need to shut down both halves
31    /// from another thread.
32    ///
33    /// The stream's write timeout is discarded, and the halves have no deadline
34    /// until one is configured.
35    ///
36    /// ```
37    /// use darkbio_wire::{clock::Clock, memory};
38    /// use darkbio_wire::transport::Client;
39    ///
40    /// let (host, bus) = memory::duplex(64 * 1024, &Clock::real());
41    /// let (reader, writer) = bus.into_halves();
42    /// let client = Client::new(host);
43    /// // Move `reader` and `writer` to the bus's input and output pumps.
44    /// ```
45    pub fn into_halves(self) -> (Reader, Writer) {
46        let (reader, writer, _, _) = self.into_parts();
47        (reader, writer)
48    }
49}
50
51/// Creates two connected streams with `capacity` bytes of buffering per
52/// direction.
53///
54/// Both measure their deadlines on `clock`, and so does any transport on them.
55/// Reads wait for data and writes wait for buffer space, bounded by the
56/// deadlines the transport installs. Partial progress never refreshes a
57/// deadline. A timeout leaves the connection reusable and preserves any bytes
58/// already accepted. Flush checks its deadline but does not wait for the peer
59/// to consume output. Reads deliver available bytes, EOF and empty reads even
60/// after their deadline; the deadline only limits waiting for input. Writes and
61/// flushes refuse expired deadlines even if buffer space is available. The
62/// transport enforces its own deadlines before calling either half.
63///
64/// Closing or dropping an endpoint wakes blocked I/O on both sides. Its unread
65/// input is discarded, while the peer can still drain the endpoint's accepted
66/// output before receiving EOF. Further nonempty writes on either side fail
67/// with [`io::ErrorKind::BrokenPipe`]. So do flushes on the closed endpoint.
68/// The peer's flush fails the same way only if the endpoint closed with some of
69/// the peer's output unread. Output the endpoint had consumed before closing
70/// flushes fine afterwards.
71///
72/// Both peers must run concurrently when exchanging data. Allow enough capacity
73/// for the handshake's initial output; 64 KiB (`64 * 1024`) is a useful
74/// starting point. Small buffers can cause handshake backpressure, just as a
75/// real stream can.
76///
77/// # Panics
78///
79/// Panics if `capacity` is zero.
80pub fn duplex(capacity: usize, clock: &Clock) -> (Duplex, Duplex) {
81    // Validate capacity before allocating either direction
82    assert!(capacity > 0, "duplex capacity must be nonzero");
83
84    // Give both endpoints the same clock and independent buffers
85    let incoming = Arc::new(Pipe::new(capacity, clock));
86    let outgoing = Arc::new(Pipe::new(capacity, clock));
87    (
88        endpoint(incoming.clone(), outgoing.clone()),
89        endpoint(outgoing, incoming),
90    )
91}
92
93/// Bundles independent I/O halves with shutdown that wakes both directions.
94fn endpoint(incoming: Arc<Pipe>, outgoing: Arc<Pipe>) -> Duplex {
95    Stream::new(
96        Reader {
97            pipe: incoming.clone(),
98            deadline: None,
99        },
100        Writer {
101            pipe: outgoing.clone(),
102            deadline: None,
103        },
104        move || {
105            incoming.close_reader();
106            outgoing.close_writer();
107        },
108    )
109}
110
111/// Receiving half of a [`Duplex`], with an independently configured read deadline.
112#[derive(Debug)]
113pub struct Reader {
114    /// Buffer this half reads from, shared with the peer's writer.
115    pipe: Arc<Pipe>,
116    /// Latest read deadline, or `None` to wait without one.
117    deadline: Option<Instant>,
118}
119
120impl io::Read for Reader {
121    /// Reads the buffered bytes, waiting for more until the read deadline.
122    ///
123    /// Buffered bytes are delivered even past the deadline. An empty `buf`, a
124    /// closed reader or a closed peer with nothing buffered returns zero.
125    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
126        // Recheck the buffer and closure state after every wake
127        let mut state = self.pipe.lock();
128        loop {
129            // Empty reads and local closure need no buffered input
130            if buf.is_empty() || !state.reader_open {
131                return Ok(0);
132            }
133
134            // Deliver ready bytes even after the installed read deadline
135            if !state.bytes.is_empty() {
136                // Copy both ring buffer halves, then remove the copied bytes
137                let count = buf.len().min(state.bytes.len());
138                let (front, back) = state.bytes.as_slices();
139                let split = count.min(front.len());
140                buf[..split].copy_from_slice(&front[..split]);
141                buf[split..count].copy_from_slice(&back[..count - split]);
142                state.bytes.drain(..count);
143                drop(state);
144                self.pipe.changed.notify_all();
145                return Ok(count);
146            }
147
148            // End at peer closure or wait within the remaining clock budget
149            if !state.writer_open {
150                return Ok(0);
151            }
152            self.pipe.check_deadline(self.deadline)?;
153            state = self.pipe.wait(state, self.deadline);
154        }
155    }
156}
157
158impl Read for Reader {
159    fn clock(&self) -> Clock {
160        self.pipe.clock.clone()
161    }
162
163    fn set_read_deadline(&mut self, deadline: Option<Instant>) -> io::Result<()> {
164        self.deadline = deadline;
165        Ok(())
166    }
167}
168
169impl Drop for Reader {
170    /// Closes this reading direction and discards its unread bytes.
171    fn drop(&mut self) {
172        self.pipe.close_reader();
173    }
174}
175
176#[cfg(test)]
177impl Reader {
178    /// Makes any later wait for input panic instead of blocking, so a test
179    /// expecting an immediate return fails rather than hangs.
180    pub(crate) fn forbid_waits(&self) {
181        self.pipe.lock().waits_forbidden = true;
182    }
183}
184
185/// Sending half of a [`Duplex`], sharing one deadline across writes and flushes.
186#[derive(Debug)]
187pub struct Writer {
188    /// Buffer this half writes into, shared with the peer's reader.
189    pipe: Arc<Pipe>,
190    /// Latest write deadline, or `None` before the first is set.
191    deadline: Option<Instant>,
192}
193
194impl io::Write for Writer {
195    /// Appends as many bytes as fit, waiting for space until the write
196    /// deadline.
197    ///
198    /// An expired deadline fails before accepting any bytes, even with space
199    /// free. An empty `buf` then returns zero, and a closed end fails with
200    /// [`io::ErrorKind::BrokenPipe`].
201    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
202        // Recheck capacity and the same deadline after every wake
203        let mut state = self.pipe.lock();
204        loop {
205            // Refuse expiry and closure before accepting any bytes
206            self.pipe.check_deadline(self.deadline)?;
207            if buf.is_empty() {
208                return Ok(0);
209            }
210            if !state.reader_open || !state.writer_open {
211                return Err(io::ErrorKind::BrokenPipe.into());
212            }
213
214            // Fill available space and wake the reader after releasing the lock
215            let count = buf.len().min(self.pipe.capacity - state.bytes.len());
216            if count > 0 {
217                state.bytes.extend(&buf[..count]);
218                drop(state);
219                self.pipe.changed.notify_all();
220                return Ok(count);
221            }
222
223            // Wait for space without extending the deadline
224            state = self.pipe.wait(state, self.deadline);
225        }
226    }
227
228    /// Checks the write deadline and that no output was lost, without waiting
229    /// for the peer to read.
230    ///
231    /// It fails with [`io::ErrorKind::BrokenPipe`] after local closure, or when
232    /// the reader closed with output unread.
233    fn flush(&mut self) -> io::Result<()> {
234        // Check the installed deadline while observing a consistent pipe state
235        let state = self.pipe.lock();
236        self.pipe.check_deadline(self.deadline)?;
237
238        // Reject local closure or output lost when the peer closed
239        if !state.writer_open || state.lost {
240            return Err(io::ErrorKind::BrokenPipe.into());
241        }
242        Ok(())
243    }
244}
245
246impl Write for Writer {
247    fn clock(&self) -> Clock {
248        self.pipe.clock.clone()
249    }
250
251    fn set_write_deadline(&mut self, deadline: Instant) -> io::Result<()> {
252        self.deadline = Some(deadline);
253        Ok(())
254    }
255}
256
257impl Drop for Writer {
258    /// Closes this writing direction, so the reader gets EOF once the buffer
259    /// drains.
260    fn drop(&mut self) {
261        self.pipe.close_writer();
262    }
263}
264
265/// Shared bounded buffer for one direction.
266///
267/// Waiting always releases the mutex.
268#[derive(Debug)]
269struct Pipe {
270    /// Clock used to check the installed I/O deadlines.
271    clock: Clock,
272    /// Most bytes the buffer holds before writers wait.
273    capacity: usize,
274    /// Buffered bytes and the open state of both ends.
275    state: Mutex<State>,
276    /// Signal waking readers and writers when the buffer or either end changes.
277    changed: Condvar,
278}
279
280/// Contents of a pipe and the open state of its two ends.
281#[derive(Debug)]
282struct State {
283    /// Bytes written and not yet read.
284    bytes: VecDeque<u8>,
285    /// Whether the reading end is open.
286    reader_open: bool,
287    /// Whether the writing end is open.
288    writer_open: bool,
289    /// Whether the reader closed with accepted output still unconsumed.
290    lost: bool,
291    /// Registered operation waits, counted for tests to observe.
292    #[cfg(test)]
293    waiting: usize,
294    /// Whether a wait fails instead of blocking, so a test cannot hang.
295    #[cfg(test)]
296    waits_forbidden: bool,
297}
298
299impl Pipe {
300    /// Creates an open, empty pipe holding up to `capacity` bytes, waiting on
301    /// `clock`.
302    fn new(capacity: usize, clock: &Clock) -> Self {
303        Self {
304            clock: clock.clone(),
305            capacity,
306            state: Mutex::new(State {
307                bytes: VecDeque::with_capacity(capacity),
308                reader_open: true,
309                writer_open: true,
310                lost: false,
311                #[cfg(test)]
312                waiting: 0,
313                #[cfg(test)]
314                waits_forbidden: false,
315            }),
316            changed: Condvar::new(clock),
317        }
318    }
319
320    /// Locks the pipe state, recovering it from a poisoned mutex.
321    fn lock(&self) -> MutexGuard<'_, State> {
322        // Shutdown must remain usable even after a panic in an I/O operation
323        self.state.lock().unwrap_or_else(|err| err.into_inner())
324    }
325
326    /// Releases the buffer until notified or the installed deadline is reached.
327    fn wait<'a>(
328        &self,
329        state: MutexGuard<'a, State>,
330        deadline: Option<Instant>,
331    ) -> MutexGuard<'a, State> {
332        // Let tests observe the wait before it releases the mutex, or refuse it
333        #[cfg(test)]
334        let state = {
335            let mut state = state;
336            assert!(!state.waits_forbidden, "memory pipe waited");
337            state.waiting += 1;
338            self.changed.notify_all();
339            state
340        };
341
342        // Release the buffer lock until notified or the current budget runs out
343        let state = match deadline {
344            Some(deadline) => {
345                self.changed
346                    .wait_deadline(state, deadline)
347                    .unwrap_or_else(|err| err.into_inner())
348                    .0
349            }
350            None => self
351                .changed
352                .wait(state)
353                .unwrap_or_else(|err| err.into_inner()),
354        };
355
356        // Remove the test's waiter count after the buffer lock is retaken
357        #[cfg(test)]
358        let state = {
359            let mut state = state;
360            state.waiting -= 1;
361            state
362        };
363        state
364    }
365
366    /// Refuses an expired deadline using the pipe's clock on every attempt.
367    fn check_deadline(&self, deadline: Option<Instant>) -> io::Result<()> {
368        if deadline.is_some_and(|deadline| self.clock.now() >= deadline) {
369            return Err(io::ErrorKind::TimedOut.into());
370        }
371        Ok(())
372    }
373
374    /// Closes the reading end and discards unread bytes, marking them lost for
375    /// the writer's flush, then wakes both ends.
376    fn close_reader(&self) {
377        let mut state = self.lock();
378        state.reader_open = false;
379        state.lost |= !state.bytes.is_empty();
380        state.bytes.clear();
381        drop(state);
382        self.changed.notify_all();
383    }
384
385    /// Closes the writing end, so the reader gets EOF once the buffer drains,
386    /// then wakes both ends.
387    fn close_writer(&self) {
388        self.lock().writer_open = false;
389        self.changed.notify_all();
390    }
391}
392
393/// Checks the in-memory streams' byte order, deadlines, backpressure and
394/// shutdown.
395#[cfg(test)]
396#[cfg_attr(coverage_nightly, coverage(off))]
397mod tests {
398    use super::*;
399    use crate::transport::Closer;
400    use crate::transport::testing::test_clock;
401    use darkbio_clock::TestClock;
402    use std::io::{Read as _, Write as _};
403    use std::sync::mpsc;
404    use std::thread;
405    use std::time::Duration;
406
407    /// Budget of 50 ms for the timeout and reuse checks.
408    const TIMEOUT: Duration = Duration::from_millis(50);
409
410    /// Splits a stream into its halves and a closer, without closing it.
411    fn halves(stream: Duplex) -> (Reader, Writer, Closer) {
412        let closer = stream.closer();
413        let (reader, writer) = stream.into_halves();
414        (reader, writer, closer)
415    }
416
417    /// Waits until an operation has released its mutex in a condition-variable wait.
418    fn blocked(pipe: &Pipe) {
419        let mut state = pipe.lock();
420        while state.waiting == 0 {
421            state = pipe.changed.wait(state).unwrap();
422        }
423    }
424
425    /// Checks that a read blocked on an empty pipe expires when its clock
426    /// reaches the installed deadline.
427    #[test]
428    fn test_blocked_read_expires_on_clock_deadline() {
429        // Park an empty read with a deadline a day ahead of real time
430        let mut tester = TestClock::new();
431        tester.advance(Duration::from_secs(86400));
432        let clock = tester.clock();
433        let (host, _peer) = duplex(1, &clock);
434        let (mut reader, _writer) = host.into_halves();
435        let deadline = clock.now() + Duration::from_secs(5);
436        reader.set_read_deadline(Some(deadline)).unwrap();
437        let reading = thread::spawn(move || reader.read(&mut [0]));
438        tester.wait_registered(1);
439        assert_eq!(tester.next_deadline(), Some(deadline));
440
441        // Advance exactly to expiry and observe the blocked operation returning
442        tester.advance_to(deadline);
443        assert_eq!(
444            reading.join().unwrap().unwrap_err().kind(),
445            io::ErrorKind::TimedOut
446        );
447    }
448
449    /// Checks that a write blocked on a full pipe expires when its clock
450    /// reaches the installed deadline.
451    #[test]
452    fn test_blocked_write_expires_on_clock_deadline() {
453        // Fill the pipe and start another write with the same fixed deadline
454        let mut tester = TestClock::new();
455        tester.advance(Duration::from_secs(86400));
456        let clock = tester.clock();
457        let (host, _peer) = duplex(1, &clock);
458        let (_reader, mut writer) = host.into_halves();
459        writer.write_all(b"a").unwrap();
460        let deadline = clock.now() + Duration::from_secs(5);
461        writer.set_write_deadline(deadline).unwrap();
462        let writing = thread::spawn(move || writer.write(b"b"));
463        tester.wait_registered(1);
464        assert_eq!(tester.next_deadline(), Some(deadline));
465
466        // Advance exactly to expiry and observe the blocked operation returning
467        tester.advance_to(deadline);
468        assert_eq!(
469            writing.join().unwrap().unwrap_err().kind(),
470            io::ErrorKind::TimedOut
471        );
472    }
473
474    /// Checks that an expired read delivers available input before reporting
475    /// expiry on the empty pipe.
476    #[test]
477    fn test_read_delivers_buffered_bytes_after_deadline() {
478        // Buffer input before the installed read deadline
479        let mut tester = test_clock();
480        let clock = tester.clock();
481        let (host, peer) = duplex(1, &clock);
482        let (mut reader, _writer) = host.into_halves();
483        let (_peer_reader, mut writer) = peer.into_halves();
484        let deadline = clock.now() + Duration::from_secs(5);
485        reader.set_read_deadline(Some(deadline)).unwrap();
486        writer.write_all(b"a").unwrap();
487
488        // Read buffered bytes at expiry without entering a condvar wait
489        tester.advance_to(deadline);
490        reader.forbid_waits();
491        let mut byte = [0];
492        assert_eq!(reader.read(&mut byte).unwrap(), 1);
493        assert_eq!(&byte, b"a");
494
495        // Derive expiry from the clock once input is exhausted
496        assert_eq!(
497            reader.read(&mut byte).unwrap_err().kind(),
498            io::ErrorKind::TimedOut
499        );
500    }
501
502    /// Checks that a zero-capacity pipe is rejected at construction, before
503    /// any I/O can wait.
504    #[test]
505    #[should_panic(expected = "duplex capacity must be nonzero")]
506    fn test_zero_capacity() {
507        duplex(0, &test_clock().clock());
508    }
509
510    /// Checks that partial reads and writes keep byte order across queue
511    /// wraparound, with independent read and write deadlines.
512    ///
513    /// The deadlines stay independent for empty I/O and flush too.
514    #[test]
515    fn test_byte_stream_and_independent_deadlines() {
516        // Create both directions on one paused clock
517        let tester = test_clock();
518        let clock = tester.clock();
519        let (host, ark) = duplex(4, &clock);
520        let (mut host_read, mut host_write, _host_close) = halves(host);
521        let (mut ark_read, mut ark_write, _ark_close) = halves(ark);
522
523        // Preserve byte order across partial writes and queue wraparound
524        assert_eq!(host_read.read(&mut []).unwrap(), 0);
525        assert_eq!(host_write.write(b"abcdef").unwrap(), 4);
526        assert_eq!(host_write.write(&[]).unwrap(), 0);
527        // Flush succeeds even while the queue is full
528        host_write.flush().unwrap();
529        let mut first = [0; 2];
530        ark_read.read_exact(&mut first).unwrap();
531        assert_eq!(&first, b"ab");
532        host_write.write_all(b"ef").unwrap();
533        let mut rest = [0; 4];
534        ark_read.read_exact(&mut rest).unwrap();
535        assert_eq!(&rest, b"cdef");
536
537        // Deliver buffered input before reporting its expired deadline
538        ark_write.write_all(b"xy").unwrap();
539        host_read.set_read_deadline(Some(clock.now())).unwrap();
540        host_read.read_exact(&mut first).unwrap();
541        assert_eq!(&first, b"xy");
542        assert_eq!(
543            host_read.read(&mut first).unwrap_err().kind(),
544            io::ErrorKind::TimedOut
545        );
546        assert_eq!(host_read.read(&mut []).unwrap(), 0);
547        host_write.write_all(b"q").unwrap();
548
549        // Expire output while input remains usable
550        host_write.set_write_deadline(clock.now()).unwrap();
551        host_read.set_read_deadline(None).unwrap();
552        ark_write.write_all(b"uv").unwrap();
553        host_read.read_exact(&mut first).unwrap();
554        assert_eq!(&first, b"uv");
555        for bytes in [b"t".as_slice(), b""] {
556            assert_eq!(
557                host_write.write(bytes).unwrap_err().kind(),
558                io::ErrorKind::TimedOut
559            );
560        }
561        assert_eq!(
562            host_write.flush().unwrap_err().kind(),
563            io::ErrorKind::TimedOut
564        );
565
566        // Resume output with a fresh deadline
567        host_write
568            .set_write_deadline(clock.now() + Duration::from_secs(5))
569            .unwrap();
570        host_write.write_all(b"rs").unwrap();
571        host_write.flush().unwrap();
572        let mut accepted = [0; 3];
573        ark_read.read_exact(&mut accepted).unwrap();
574        assert_eq!(&accepted, b"qrs");
575    }
576
577    /// Checks that one read copies across the queue's wraparound, filling as
578    /// much of the buffer as the queue holds and leaving the rest queued.
579    #[test]
580    fn test_read_across_wraparound() {
581        let tests: [(usize, usize, &[u8; 6], &[u8]); 5] = [
582            (1, 1, b"c-----", b"def"), // stops inside the front half
583            (2, 2, b"cd----", b"ef"),  // stops at the wrap
584            (3, 3, b"cde---", b"f"),   // crosses into the back half
585            (4, 4, b"cdef--", b""),    // takes everything queued
586            (6, 4, b"cdef--", b""),    // leaves the rest of the buffer untouched
587        ];
588        for (i, (size, count, filled, rest)) in tests.into_iter().enumerate() {
589            // Wrap the queued bytes around the end of a 4 byte pipe
590            let tester = test_clock();
591            let (host, ark) = duplex(4, &tester.clock());
592            let (_host_read, mut writer) = host.into_halves();
593            let (mut reader, _ark_write) = ark.into_halves();
594            writer.write_all(b"abcd").unwrap();
595            reader.read_exact(&mut [0; 2]).unwrap();
596            writer.write_all(b"ef").unwrap();
597            assert_eq!(
598                reader.pipe.lock().bytes.as_slices(),
599                (b"cd".as_slice(), b"ef".as_slice()),
600                "test {i}"
601            );
602
603            // Read once, leaving the buffer past the copied bytes untouched
604            let mut buf = [b'-'; 6];
605            let copied = reader.read(&mut buf[..size]).unwrap();
606            assert_eq!(copied, count, "test {i}");
607            assert_eq!(&buf, filled, "test {i}");
608
609            // Drain the bytes left queued once the writer closes
610            drop(writer);
611            let mut left = Vec::new();
612            reader.read_to_end(&mut left).unwrap();
613            assert_eq!(left, rest, "test {i}");
614        }
615    }
616
617    /// Checks that an idle timed read expires without EOF or buffer changes,
618    /// and clearing its deadline restores an indefinite read.
619    #[test]
620    fn test_read_timeout_and_reuse() {
621        // Park an empty read on a deadline ahead of the paused clock
622        let mut tester = test_clock();
623        let clock = tester.clock();
624        let deadline = clock.now() + TIMEOUT;
625        let (host, ark) = duplex(1, &clock);
626        let (mut reader, _host_write, _host_close) = halves(host);
627        let (_ark_read, mut writer, _ark_close) = halves(ark);
628        let pipe = reader.pipe.clone();
629        let (done, result) = mpsc::channel();
630        let timed = thread::spawn(move || {
631            reader.set_read_deadline(Some(deadline)).unwrap();
632            let mut buf = [99];
633            let error = reader.read(&mut buf).unwrap_err();
634            assert!(clock.now() >= deadline);
635            assert_eq!(buf, [99]);
636            done.send((reader, error.kind())).unwrap();
637        });
638        tester.wait_registered(1);
639        tester.advance_to(deadline);
640        let (mut reader, kind) = result.recv().unwrap();
641        timed.join().unwrap();
642        assert_eq!(kind, io::ErrorKind::TimedOut);
643
644        // Clear expiry and wake a new indefinite read with input
645        reader.set_read_deadline(None).unwrap();
646        let (done, result) = mpsc::channel();
647        let reading = thread::spawn(move || {
648            let mut buf = [0];
649            reader.read_exact(&mut buf).unwrap();
650            done.send(buf).unwrap();
651        });
652        blocked(&pipe);
653        writer.write_all(b"x").unwrap();
654        assert_eq!(&result.recv().unwrap(), b"x");
655        reading.join().unwrap();
656    }
657
658    /// Checks that a later write follows the prefix a `write_all` accepted
659    /// before timing out, without the abandoned suffix appearing afterwards.
660    #[test]
661    fn test_write_timeout_and_reuse() {
662        // Fill the pipe and leave the remaining byte waiting until its deadline
663        let mut tester = test_clock();
664        let clock = tester.clock();
665        let deadline = clock.now() + TIMEOUT;
666        let (host, ark) = duplex(3, &clock);
667        let (_host_read, mut writer, _host_close) = halves(host);
668        let (mut reader, _ark_write, _ark_close) = halves(ark);
669        let (done, result) = mpsc::channel();
670        let writing = thread::spawn(move || {
671            writer.set_write_deadline(deadline).unwrap();
672            let error = writer.write_all(b"abcd").unwrap_err();
673            assert!(clock.now() >= deadline);
674            done.send((writer, error.kind())).unwrap();
675        });
676        tester.wait_registered(1);
677        tester.advance_to(deadline);
678        let (mut writer, kind) = result.recv().unwrap();
679        writing.join().unwrap();
680        assert_eq!(kind, io::ErrorKind::TimedOut);
681        let mut prefix = [0; 3];
682        reader.read_exact(&mut prefix).unwrap();
683        assert_eq!(&prefix, b"abc");
684
685        // Resume output after the accepted prefix with a fresh deadline
686        writer
687            .set_write_deadline(tester.clock().now() + Duration::from_secs(5))
688            .unwrap();
689        writer.write_all(b"ef").unwrap();
690        drop(writer);
691        let mut suffix = Vec::new();
692        reader.read_to_end(&mut suffix).unwrap();
693        assert_eq!(&suffix, b"ef");
694    }
695
696    /// Checks that draining a full queue wakes its writer through several
697    /// partial writes, while bounded reads rebuild the original byte stream.
698    #[test]
699    fn test_backpressure_wakes_writer() {
700        // Fill a paused pipe and start a writer behind its queued bytes
701        let tester = test_clock();
702        let deadline = tester.clock().now() + Duration::from_secs(5);
703        let (host, ark) = duplex(3, &tester.clock());
704        let (_host_read, mut writer, _host_close) = halves(host);
705        let (mut reader, _ark_write, _ark_close) = halves(ark);
706        writer.write_all(b"abc").unwrap();
707        let pipe = writer.pipe.clone();
708        let (done, result) = mpsc::channel();
709        let writing = thread::spawn(move || {
710            writer.set_write_deadline(deadline).unwrap();
711            done.send(writer.write_all(b"defgh")).unwrap();
712        });
713
714        // Drain the pipe after the writer has registered its wait
715        blocked(&pipe);
716        reader.set_read_deadline(Some(deadline)).unwrap();
717        let mut bytes = [0; 8];
718        reader.read_exact(&mut bytes).unwrap();
719        assert_eq!(&bytes, b"abcdefgh");
720        result.recv().unwrap().unwrap();
721        writing.join().unwrap();
722    }
723
724    /// Checks that a local shutdown and dropping the peer both release blocked
725    /// reads and writes, including ones without a deadline.
726    #[test]
727    fn test_shutdown_wakes_both_directions() {
728        for local in [false, true] {
729            // Start a blocked read and a blocked write on the same clock
730            let tester = test_clock();
731            let (host, ark) = duplex(1, &tester.clock());
732            let (mut reader, mut writer, closer) = halves(host);
733            writer.write_all(b"a").unwrap();
734            let incoming = reader.pipe.clone();
735            let outgoing = writer.pipe.clone();
736            let (read_done, read_result) = mpsc::channel();
737            let reading = thread::spawn(move || {
738                read_done.send(reader.read(&mut [0])).unwrap();
739            });
740            let (write_done, write_result) = mpsc::channel();
741            let writing = thread::spawn(move || {
742                write_done.send(writer.write(b"b")).unwrap();
743            });
744
745            // Close only after both adapter calls have registered their waits
746            blocked(&incoming);
747            blocked(&outgoing);
748            if local {
749                closer.close();
750            } else {
751                drop(ark);
752            }
753            assert_eq!(read_result.recv().unwrap().unwrap(), 0);
754            assert_eq!(
755                write_result.recv().unwrap().unwrap_err().kind(),
756                io::ErrorKind::BrokenPipe
757            );
758            reading.join().unwrap();
759            writing.join().unwrap();
760        }
761    }
762
763    /// Checks that closing discards local input but lets the peer drain accepted
764    /// output before EOF, even while the halves are still held.
765    #[test]
766    fn test_shutdown_drains_output_and_discards_input() {
767        // Queue bytes in both directions on a paused clock
768        let tester = test_clock();
769        let clock = tester.clock();
770        let (host, ark) = duplex(3, &clock);
771        let (mut host_read, mut host_write, closer) = halves(host);
772        let (mut ark_read, mut ark_write, _ark_close) = halves(ark);
773        host_write.write_all(b"abc").unwrap();
774        ark_write.write_all(b"xy").unwrap();
775        host_read.set_read_deadline(Some(clock.now())).unwrap();
776        ark_read.set_read_deadline(Some(clock.now())).unwrap();
777
778        // Close twice and retain only the peer's accepted input
779        closer.close();
780        closer.close();
781        assert_eq!(host_read.read(&mut [0]).unwrap(), 0);
782        let mut bytes = Vec::new();
783        ark_read.read_to_end(&mut bytes).unwrap();
784        assert_eq!(&bytes, b"abc");
785        for writer in [&mut host_write, &mut ark_write] {
786            assert_eq!(
787                writer.write(b"z").unwrap_err().kind(),
788                io::ErrorKind::BrokenPipe
789            );
790            assert_eq!(
791                writer.flush().unwrap_err().kind(),
792                io::ErrorKind::BrokenPipe
793            );
794        }
795    }
796
797    /// Checks that a flush succeeds after the peer consumed every accepted byte
798    /// and closed, while new output fails.
799    #[test]
800    fn test_flush_after_peer_drained_and_closed() {
801        // Drain the accepted output before closing the peer
802        let tester = test_clock();
803        let (host, ark) = duplex(4, &tester.clock());
804        let (_host_read, mut host_write) = host.into_halves();
805        let (mut ark_read, _ark_write) = ark.into_halves();
806        host_write.write_all(b"ab").unwrap();
807        let mut bytes = [0; 2];
808        ark_read.read_exact(&mut bytes).unwrap();
809        drop(ark_read);
810
811        // Allow a flush but reject any new output
812        host_write.flush().unwrap();
813        assert_eq!(
814            host_write.write(b"c").unwrap_err().kind(),
815            io::ErrorKind::BrokenPipe
816        );
817    }
818
819    /// Checks that splitting discards the write budget without closing the
820    /// halves, and dropping one half leaves the other direction usable.
821    #[test]
822    fn test_into_halves_and_independent_drop() {
823        // Split both streams on a paused clock
824        let tester = test_clock();
825        let (host, ark) = duplex(4, &tester.clock());
826        let (mut host_read, mut host_write) = host.set_write_timeout(Duration::ZERO).into_halves();
827        let (mut ark_read, mut ark_write) = ark.into_halves();
828
829        // Drop one writer and let its peer drain to EOF
830        host_write.write_all(b"abc").unwrap();
831        drop(host_write);
832        let mut bytes = Vec::new();
833        ark_read.read_to_end(&mut bytes).unwrap();
834        assert_eq!(&bytes, b"abc");
835
836        // Keep the opposite direction usable until its own reader drops
837        ark_write.write_all(b"xy").unwrap();
838        let mut reply = [0; 2];
839        host_read.read_exact(&mut reply).unwrap();
840        assert_eq!(&reply, b"xy");
841        drop(host_read);
842        assert_eq!(
843            ark_write.write(b"z").unwrap_err().kind(),
844            io::ErrorKind::BrokenPipe
845        );
846    }
847
848    /// Checks that protocol peers exchange a message larger than the pipe both
849    /// ways, then close while their transport readers wait for input.
850    #[test]
851    fn test_protocol_round_trip() {
852        use crate::protocol::{self, Message};
853        use crate::transport::mock::self_attestation;
854        use darkbio_crypto::xdsa;
855
856        // Connect protocol peers on a paused clock
857        let tester = test_clock();
858        let signer = xdsa::SecretKey::generate();
859        let identity = signer.public_key();
860        let attestation = self_attestation(&signer);
861        let (host, ark) = duplex(64 * 1024, &tester.clock());
862        let mut server = protocol::Server::new(ark, signer, attestation);
863        let (client, _) = protocol::connect(host, &identity).unwrap();
864        let mut session = server.accept().unwrap();
865
866        // Exchange a payload larger than the bounded transport pipe
867        let payload: Vec<u8> = (0..256 * 1024).map(|n| n as u8).collect();
868        let deadline = tester.clock().now() + Duration::from_secs(5);
869        let answer = client
870            .requester()
871            .request(payload.clone(), deadline)
872            .unwrap();
873        let (message, responder) = session.recv().unwrap();
874        assert_eq!(message, Message::Develop(payload.clone()));
875        let written = responder.reply(message, deadline).unwrap();
876        assert_eq!(answer.wait::<Vec<u8>>().unwrap(), payload);
877        written.wait().unwrap();
878
879        // Release both protocol owners after delivery
880        client.close();
881        server.close();
882    }
883}