Skip to main content

darkbio_wire/transport/
stream.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//! The byte stream and its shutdown operation, owned together by the transport.
8
9use super::io::check_deadline;
10use super::{DEFAULT_WRITE_TIMEOUT, Read, Write};
11use darkbio_clock::Clock;
12use darkbio_clock::sync::{Condvar, Mutex};
13use std::fmt;
14use std::io;
15use std::sync::Arc;
16use std::time::{Duration, Instant};
17use tracing::debug;
18
19/// A duplex byte stream with a shutdown operation for both directions.
20///
21/// Shutdown must release blocked reads, writes and flushes, including reads
22/// with no deadline. Socket adapters can shut down the underlying socket.
23/// The shutdown operation must return promptly, must not panic, and must not
24/// acquire a lock held by a blocked I/O operation. It runs at most once.
25/// Neither shutdown nor an I/O operation may call this stream's closer. Closing
26/// would wait for the calling operation itself to return.
27///
28/// Closing refuses further adapter I/O. Admitted operations return their normal
29/// results and may succeed while shutdown is in progress. A failed frame send
30/// may have moved any prefix of its bytes. A successful write and flush means
31/// the adapter took the bytes, not that the peer received or processed them.
32/// Data already buffered by the transport may still be received after closing.
33///
34/// Dropping the stream closes it. Passing it to a client or server transfers
35/// that responsibility to the transport owner. [`Closer`] handles do not keep
36/// the reader or writer alive, and dropping a handle does not close the stream.
37pub struct Stream<R: Read, W: Write> {
38    /// Clock shared by both adapters and the transport on them.
39    clock: Clock,
40    /// Reader and writer, taken when ownership passes to the transport.
41    io: Option<(R, W)>,
42    /// Handle closing this stream, cloned for every caller that asks.
43    closer: Closer,
44    /// One budget for each frame's writes and flush.
45    timeout: Duration,
46}
47
48impl<R: Read, W: Write> Stream<R, W> {
49    /// Bundles the two I/O directions with their shutdown operation.
50    ///
51    /// Each frame gets [`DEFAULT_WRITE_TIMEOUT`] to write and flush until
52    /// [`Self::set_write_timeout`] changes it.
53    ///
54    /// # Panics
55    ///
56    /// Panics if the reader and writer report different clocks.
57    pub fn new(reader: R, writer: W, shutdown: impl FnOnce() + Send + 'static) -> Self {
58        // Reject mixed clocks before creating the stream's shutdown state
59        let clock = reader.clock();
60        assert_eq!(
61            clock,
62            writer.clock(),
63            "stream halves must use the same clock"
64        );
65
66        // Retain the shared clock alongside the adapters
67        Self {
68            closer: Closer::new(&clock, shutdown),
69            clock,
70            io: Some((reader, writer)),
71            timeout: DEFAULT_WRITE_TIMEOUT,
72        }
73    }
74
75    /// Returns the clock shared by this stream's adapters.
76    pub fn clock(&self) -> Clock {
77        self.clock.clone()
78    }
79
80    /// Sets the budget for writing and flushing one complete frame, including
81    /// any delimiter needed after failed output.
82    ///
83    /// Progress does not restart it. The budget begins after acquiring the
84    /// writer and includes frame encoding. Waiting for locks, encryption and
85    /// peer replies is outside this budget. Handshake frames also share the
86    /// overall handshake deadline, which can shorten this write budget.
87    ///
88    /// Zero, or a duration too large to add to an [`Instant`], refuses output
89    /// immediately.
90    pub fn set_write_timeout(mut self, timeout: Duration) -> Self {
91        self.timeout = timeout;
92        self
93    }
94
95    /// Returns a handle that can close the stream from another thread.
96    pub fn closer(&self) -> Closer {
97        self.closer.clone()
98    }
99
100    /// Permanently closes the stream and waits for shutdown and admitted adapter
101    /// operations to finish.
102    ///
103    /// Concurrent close calls wait for the same completion. See
104    /// [`Closer::close`].
105    pub fn close(&self) {
106        self.closer.close();
107    }
108
109    /// Hands the adapters, a closer and the write budget to a transport
110    /// without closing the stream.
111    pub(crate) fn into_parts(mut self) -> (R, W, Closer, Duration) {
112        let (reader, writer) = self.io.take().expect("stream consumed once");
113        (reader, writer, self.closer.clone(), self.timeout)
114    }
115}
116
117impl<R: Read, W: Write> Drop for Stream<R, W> {
118    fn drop(&mut self) {
119        if self.io.is_some() {
120            self.close();
121        }
122    }
123}
124
125impl<R: Read, W: Write> fmt::Debug for Stream<R, W> {
126    /// Shows the write budget and the shutdown state, never the adapters.
127    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128        f.debug_struct("Stream")
129            .field("timeout", &self.timeout)
130            .field("closer", &self.closer)
131            .finish_non_exhaustive()
132    }
133}
134
135/// A cloneable handle that permanently closes a byte stream.
136#[derive(Clone)]
137pub struct Closer(
138    /// Shutdown coordination shared by every clone.
139    Arc<Shutdown>,
140);
141
142/// Shutdown coordination shared by every handle of one stream.
143///
144/// The state lock orders I/O admission and closure. Adapter operations and the
145/// shutdown callback run without this lock.
146struct Shutdown {
147    /// Lifecycle, active operations and the pending shutdown callback.
148    state: Mutex<State>,
149    /// Signal waking closers as admitted operations and shutdown finish.
150    changed: Condvar,
151}
152
153/// Lifecycle phase of a byte stream.
154///
155/// Closing refuses new adapter operations. Closed also guarantees that
156/// shutdown and all admitted operations have finished.
157#[derive(Clone, Copy, Debug, PartialEq, Eq)]
158enum Phase {
159    /// Stream admitting adapter I/O, with no shutdown requested.
160    Open,
161    /// Shutdown run by one closer while other closers wait and new I/O is
162    /// refused.
163    Closing,
164    /// End state, reached once the callback and every admitted adapter call
165    /// have returned.
166    Closed,
167}
168
169/// Stream lifecycle and active adapter operations, guarded by one lock.
170///
171/// Each admitted operation increments the active count and decrements it on
172/// completion. Closed requires a zero count.
173struct State {
174    /// Current lifecycle phase.
175    phase: Phase,
176    /// Admitted adapter operations that shutdown must wait for.
177    active: usize,
178    /// Shutdown callback, taken by the first closer.
179    action: Option<Box<dyn FnOnce() + Send>>,
180}
181
182impl Closer {
183    /// Creates the coordinator of an open stream, whose closers wait on `clock`
184    /// and whose first closer runs `shutdown`.
185    pub(super) fn new(clock: &Clock, shutdown: impl FnOnce() + Send + 'static) -> Self {
186        Self(Arc::new(Shutdown {
187            state: Mutex::new(State {
188                phase: Phase::Open,
189                active: 0,
190                action: Some(Box::new(shutdown)),
191            }),
192            changed: Condvar::new(clock),
193        }))
194    }
195
196    /// Permanently closes the stream.
197    ///
198    /// Every caller waits until the shutdown callback and all admitted adapter
199    /// calls have returned. This includes deadline setters, reads, writes and
200    /// flushes. New I/O is refused as soon as closing begins. This does not join
201    /// the threads using the transport or wait for application handlers.
202    ///
203    /// The callback runs once, without holding a state or I/O lock. It must
204    /// return promptly and release blocked I/O, including reads with no
205    /// deadline. Calling close from adapter I/O or the callback would wait on
206    /// itself.
207    pub fn close(&self) {
208        // Wait for another closer to finish or take responsibility for shutdown
209        let action = {
210            let mut state = self.0.state.lock().expect("stream state not poisoned");
211            loop {
212                match state.phase {
213                    // Stream already closed, return early
214                    Phase::Closed => return,
215
216                    // Another closer is running shutdown. Wait for it to finish.
217                    Phase::Closing => {
218                        state = self
219                            .0
220                            .changed
221                            .wait(state)
222                            .expect("stream state not poisoned");
223                    }
224
225                    // Stream open, mark it closing and begin teardown
226                    Phase::Open => {
227                        debug!("closing wire stream");
228                        state.phase = Phase::Closing;
229                        break state.action.take().expect("shutdown called once");
230                    }
231                }
232            }
233        };
234
235        // The first closer runs shutdown without holding the state lock
236        action();
237
238        // Wait until all admitted adapter operations return
239        let mut state = self.0.state.lock().expect("stream state not poisoned");
240        while state.active != 0 {
241            state = self
242                .0
243                .changed
244                .wait(state)
245                .expect("stream state not poisoned");
246        }
247
248        // Mark the stream closed and wake any threads blocked on close
249        state.phase = Phase::Closed;
250        self.0.changed.notify_all();
251    }
252
253    /// Admits one adapter call while the stream is open, or returns `None` once
254    /// closing has begun.
255    ///
256    /// Admission and the decision to start closing are ordered under the state
257    /// lock.
258    fn enter(&self) -> Option<Activity<'_>> {
259        let mut state = self.0.state.lock().expect("stream state not poisoned");
260        if state.phase != Phase::Open {
261            return None;
262        }
263        state.active += 1;
264        Some(Activity(self))
265    }
266}
267
268impl fmt::Debug for Closer {
269    /// Shows the lifecycle phase and the admitted adapter operations.
270    ///
271    /// A state lock held elsewhere is reported instead of waited for.
272    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
273        let mut closer = f.debug_struct("Closer");
274        match self.0.state.try_lock() {
275            Ok(state) => closer
276                .field("phase", &state.phase)
277                .field("active", &state.active),
278            Err(_) => closer.field("state", &format_args!("<locked>")),
279        };
280        closer.finish()
281    }
282}
283
284/// Guard for one admitted adapter operation, held until it returns or unwinds.
285///
286/// Dropping it decrements the active count without acquiring an I/O lock.
287struct Activity<'a>(
288    /// Coordinator that admitted the operation.
289    &'a Closer,
290);
291
292impl Drop for Activity<'_> {
293    fn drop(&mut self) {
294        let mut state = self.0.0.state.lock().expect("stream state not poisoned");
295        state.active -= 1;
296        if state.active == 0 {
297            self.0.0.changed.notify_all();
298        }
299    }
300}
301
302/// Reader admitting each adapter call through the shutdown coordinator.
303pub(super) struct ReadHalf<R> {
304    /// Reading adapter of the stream.
305    pub(super) inner: R,
306    /// Coordinator admitting each deadline setter and read call.
307    pub(super) closer: Closer,
308}
309
310impl<R: Read> ReadHalf<R> {
311    /// Reads with the optional handshake deadline.
312    ///
313    /// Ordinary reads wait for data or adapter shutdown. Timeouts and
314    /// interruptions retry within the deadline. A successful read reports its
315    /// bytes even if it finishes late, so the framer can retain them before
316    /// surfacing expiry. Closure refuses new I/O with EOF.
317    pub(super) fn read(&mut self, buf: &mut [u8], deadline: Option<Instant>) -> io::Result<usize> {
318        loop {
319            // Refuse an operation whose deadline has expired
320            if let Some(deadline) = deadline {
321                check_deadline(&self.inner.clock(), deadline)?;
322            }
323
324            // Keep the setter and read accounted for until both have returned
325            let result = {
326                let Some(_active) = self.closer.enter() else {
327                    return Ok(0);
328                };
329                self.inner.set_read_deadline(deadline)?;
330                self.inner.read(buf)
331            };
332
333            // Retry an idle timeout or interrupted read. Other errors return.
334            match result {
335                Err(err)
336                    if matches!(
337                        err.kind(),
338                        io::ErrorKind::TimedOut | io::ErrorKind::Interrupted
339                    ) =>
340                {
341                    continue;
342                }
343                result => return result,
344            }
345        }
346    }
347}
348
349/// Writer admitting each deadline setter, partial write and flush through the
350/// shutdown coordinator.
351pub(super) struct WriteHalf<W> {
352    /// Writing adapter of the stream.
353    pub(super) inner: W,
354    /// Coordinator admitting each deadline setter, write and flush call.
355    pub(super) closer: Closer,
356}
357
358impl<W: Write> WriteHalf<W> {
359    /// Writes all bytes and flushes them under one absolute deadline.
360    ///
361    /// Installs the deadline once before I/O. Each partial write and flush
362    /// checks expiration and closure before calling the adapter. Interrupted
363    /// writes are retried. Zero progress fails with
364    /// [`WriteZero`](io::ErrorKind::WriteZero). Setter and flush errors are not
365    /// retried.
366    ///
367    /// An admitted call may finish after closure begins. Failure can leave a
368    /// written prefix. The framer checks the deadline again after this
369    /// operation returns, so a late flush fails the complete frame.
370    pub(super) fn write(&mut self, mut bytes: &[u8], deadline: Instant) -> io::Result<()> {
371        // Refuse an operation whose deadline has expired
372        check_deadline(&self.inner.clock(), deadline)?;
373
374        // Account for the deadline setter so shutdown waits for it too
375        {
376            let _active = self
377                .closer
378                .enter()
379                .ok_or_else(|| io::Error::new(io::ErrorKind::NotConnected, "stream closed"))?;
380            self.inner.set_write_deadline(deadline)?;
381        }
382
383        // Keep writing while bytes remain; flush only after the complete write
384        while !bytes.is_empty() {
385            // Recheck the deadline before each partial write
386            check_deadline(&self.inner.clock(), deadline)?;
387
388            // Attempt to write as much data as possible
389            let result = {
390                let _active = self
391                    .closer
392                    .enter()
393                    .ok_or_else(|| io::Error::new(io::ErrorKind::NotConnected, "stream closed"))?;
394                self.inner.write(bytes)
395            };
396            match result {
397                Ok(0) => return Err(io::ErrorKind::WriteZero.into()),
398                Ok(n) => bytes = &bytes[n..],
399                Err(err) if err.kind() == io::ErrorKind::Interrupted => continue,
400                Err(err) => return Err(err),
401            }
402        }
403
404        // Flush is part of the same operation and gets its own admission
405        check_deadline(&self.inner.clock(), deadline)?;
406        let _active = self
407            .closer
408            .enter()
409            .ok_or_else(|| io::Error::new(io::ErrorKind::NotConnected, "stream closed"))?;
410        self.inner.flush()
411    }
412}
413
414/// Checks stream ownership, shutdown, I/O admission and output deadlines.
415#[cfg(test)]
416#[cfg_attr(coverage_nightly, coverage(off))]
417mod tests {
418    use super::*;
419    use crate::testing;
420    use crate::transport::Client;
421    use crate::transport::testing::{Memory, test_clock};
422    use std::sync::atomic::{AtomicUsize, Ordering};
423    use std::sync::mpsc;
424    use std::thread;
425    use std::time::Duration;
426
427    /// Checks that concurrent closers wait on the stream clock until admitted
428    /// I/O is released.
429    #[test]
430    fn test_concurrent_closers_wait_on_stream_clock() {
431        // Keep an admitted operation alive while two threads close the stream
432        let tester = darkbio_clock::TestClock::new();
433        let (stream, _peer) = crate::memory::duplex(1, &tester.clock());
434        let closer = stream.closer();
435        let activity = closer.enter().unwrap();
436        thread::scope(|scope| {
437            let first = scope.spawn(|| closer.close());
438            let second = scope.spawn(|| closer.close());
439
440            // Require both shutdown waits to be registered on this clock
441            tester.wait_registered(2);
442            assert!(closer.enter().is_none());
443
444            // Complete the admitted operation and let both closers finish
445            drop(activity);
446            first.join().unwrap();
447            second.join().unwrap();
448            assert!(closer.enter().is_none());
449        });
450    }
451
452    /// Checks that the transport's owners and handles print without printable
453    /// adapters, which hosts may box as trait objects.
454    #[test]
455    fn test_debug_capabilities() {
456        use crate::transport::{Attestation, Event, Roots, Sender, Server};
457
458        /// Requires a type to be printable.
459        fn printable<T: fmt::Debug>() {}
460        printable::<Stream<Box<dyn Read>, Box<dyn Write>>>();
461        printable::<Closer>();
462        printable::<Client<Box<dyn Read>, Box<dyn Write>>>();
463        printable::<Server<Box<dyn Read>, Box<dyn Write>, Attestation>>();
464        printable::<Sender<Box<dyn Write>>>();
465        printable::<Event<Box<dyn Write>>>();
466        printable::<Attestation>();
467        printable::<Roots<'static>>();
468    }
469
470    /// Checks that the transport refuses an expired read before consuming bytes
471    /// a memory reader would still deliver after its own deadline.
472    #[test]
473    fn test_memory_reader_keeps_transport_deadline() {
474        // Queue bytes on a clock a day ahead of real time
475        let tester = test_clock();
476        let clock = tester.clock();
477        let (host, ark) = crate::memory::duplex(4, &clock);
478        let (_ark_read, mut ark_write) = ark.into_halves();
479        std::io::Write::write_all(&mut ark_write, b"abc").unwrap();
480        let (reader, _writer, closer, _) = host.into_parts();
481        let mut reader = ReadHalf {
482            inner: reader,
483            closer,
484        };
485
486        // Refuse an expired read without consuming the available bytes
487        let mut bytes = [0; 3];
488        assert_eq!(
489            reader
490                .read(&mut bytes, Some(clock.now()))
491                .unwrap_err()
492                .kind(),
493            io::ErrorKind::TimedOut
494        );
495        assert_eq!(bytes, [0; 3]);
496        assert_eq!(reader.read(&mut bytes, None).unwrap(), 3);
497        assert_eq!(&bytes, b"abc");
498    }
499
500    /// Adapter holding an admitted call until shutdown has been requested,
501    /// then returning the result selected by the test.
502    struct Adapter {
503        /// Clock shared by the adapter and shutdown gate.
504        clock: Clock,
505        /// Channel notifying the test that an adapter call has started.
506        entered: mpsc::Sender<()>,
507        /// Gate the shutdown callback opens to release the held call.
508        released: testing::Gate,
509        /// Whether the held call returns an adapter failure.
510        fails: bool,
511        /// Latest deadline installed by either direction's setter.
512        deadline: Option<Instant>,
513    }
514
515    impl Adapter {
516        /// Reports entry and waits for shutdown to release the call within its
517        /// deadline, then returns the selected result.
518        fn wait(&self, deadline: Option<Instant>) -> io::Result<()> {
519            self.entered.send(()).unwrap();
520            self.released.wait(deadline)?;
521            if self.fails {
522                Err(io::Error::other("adapter failure"))
523            } else {
524                Ok(())
525            }
526        }
527    }
528
529    impl Read for Adapter {
530        fn clock(&self) -> Clock {
531            self.clock.clone()
532        }
533
534        fn set_read_deadline(&mut self, deadline: Option<Instant>) -> io::Result<()> {
535            self.deadline = deadline;
536            Ok(())
537        }
538    }
539
540    impl io::Read for Adapter {
541        fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
542            self.wait(self.deadline)?;
543            buf[0] = 0x5a;
544            Ok(1)
545        }
546    }
547
548    impl Write for Adapter {
549        fn clock(&self) -> Clock {
550            self.clock.clone()
551        }
552
553        fn set_write_deadline(&mut self, deadline: Instant) -> io::Result<()> {
554            self.deadline = Some(deadline);
555            Ok(())
556        }
557    }
558
559    impl io::Write for Adapter {
560        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
561            self.wait(Some(self.deadline.expect("write deadline installed")))?;
562            Ok(buf.len())
563        }
564
565        fn flush(&mut self) -> io::Result<()> {
566            self.wait(Some(self.deadline.expect("write deadline installed")))
567        }
568    }
569
570    /// Runs an adapter call across shutdown and checks that its result survives.
571    fn during_shutdown(
572        fails: bool,
573        operation: impl FnOnce(Adapter, Closer) -> io::Result<()> + Send + 'static,
574    ) {
575        // Start an admitted adapter call on a paused clock
576        let tester = test_clock();
577        let clock = tester.clock();
578        let (entered, entries) = mpsc::channel();
579        let released = testing::Gate::new(&clock);
580        let release = released.clone();
581        let closer = Closer::new(&clock, move || release.open());
582        let io = thread::spawn({
583            let closer = closer.clone();
584            move || {
585                operation(
586                    Adapter {
587                        clock,
588                        entered,
589                        released,
590                        fails,
591                        deadline: None,
592                    },
593                    closer,
594                )
595            }
596        });
597
598        // Close only after the adapter has registered its wait and preserve its result
599        entries.recv().unwrap();
600        tester.wait_registered(1);
601        closer.close();
602        let result = io.join().unwrap();
603        if fails {
604            let err = result.unwrap_err();
605            assert_eq!(err.kind(), io::ErrorKind::Other);
606            assert_eq!(err.to_string(), "adapter failure");
607        } else {
608            result.unwrap();
609        }
610    }
611
612    /// Checks that shutdown preserves the results of an admitted read and final
613    /// flush, including their errors.
614    #[test]
615    fn test_admitted_io_preserves_results_during_shutdown() {
616        for fails in [false, true] {
617            // Preserve the admitted read's result across shutdown
618            during_shutdown(fails, |inner, closer| {
619                let mut buf = [0];
620                assert_eq!(ReadHalf { inner, closer }.read(&mut buf, None)?, 1);
621                assert_eq!(buf, [0x5a]);
622                Ok(())
623            });
624
625            // Let an admitted write finish, then refuse the flush after it
626            during_shutdown(fails, move |inner, closer| {
627                let deadline = inner.clock.now() + Duration::from_secs(5);
628                let result = WriteHalf { inner, closer }.write(&[1, 2, 3], deadline);
629                if fails {
630                    result
631                } else {
632                    assert_eq!(result.unwrap_err().kind(), io::ErrorKind::NotConnected);
633                    Ok(())
634                }
635            });
636
637            // Preserve the admitted final flush's result across shutdown
638            during_shutdown(fails, |inner, closer| {
639                let deadline = inner.clock.now() + Duration::from_secs(5);
640                WriteHalf { inner, closer }.write(&[], deadline)
641            });
642        }
643    }
644
645    /// Checks that handing a stream to a client keeps it open, and dropping
646    /// either owner runs shutdown exactly once despite repeated closes.
647    #[test]
648    fn test_ownership_and_repeated_close() {
649        // Transfer a paused stream to its client without closing it
650        let tester = test_clock();
651        let clock = tester.clock();
652        let calls = Arc::new(AtomicUsize::new(0));
653        let stream = Stream::new(
654            Memory::new(io::empty(), &clock),
655            Memory::new(io::sink(), &clock),
656            {
657                let calls = calls.clone();
658                move || {
659                    calls.fetch_add(1, Ordering::SeqCst);
660                }
661            },
662        );
663        let closer = stream.closer();
664        let client = Client::new(stream);
665        assert_eq!(
666            calls.load(Ordering::SeqCst),
667            0,
668            "handoff must keep the stream open"
669        );
670
671        // Close once when its owner drops and ignore repeated closes
672        drop(client);
673        assert_eq!(calls.load(Ordering::SeqCst), 1);
674        closer.close();
675        drop(closer.clone());
676        assert_eq!(calls.load(Ordering::SeqCst), 1);
677
678        // Close an unclaimed stream when it drops too
679        let stream = Stream::new(
680            Memory::new(io::empty(), &clock),
681            Memory::new(io::sink(), &clock),
682            {
683                let calls = calls.clone();
684                move || {
685                    calls.fetch_add(1, Ordering::SeqCst);
686                }
687            },
688        );
689        drop(stream);
690        assert_eq!(
691            calls.load(Ordering::SeqCst),
692            2,
693            "unclaimed streams also close on drop"
694        );
695    }
696
697    /// Checks that concurrent close calls and the owner's drop all wait for the
698    /// shutdown callback to return.
699    #[test]
700    fn test_every_closer_waits_for_the_shutdown_callback() {
701        // Park the shutdown callback on the stream's paused clock
702        let tester = test_clock();
703        let clock = tester.clock();
704        let (entered, callback) = mpsc::channel();
705        let release = testing::Gate::new(&clock);
706        let released = release.clone();
707        let stream = Stream::new(
708            Memory::new(io::empty(), &clock),
709            Memory::new(io::sink(), &clock),
710            move || {
711                entered.send(()).unwrap();
712                released.wait(None).unwrap();
713            },
714        );
715        let closer = stream.closer();
716        let (finished, finishes) = mpsc::channel();
717        let first = thread::spawn({
718            let closer = closer.clone();
719            let finished = finished.clone();
720            move || {
721                closer.close();
722                finished.send(()).unwrap();
723            }
724        });
725        callback.recv().unwrap();
726
727        // Start another close and owner drop while the first callback is waiting
728        let (started, starts) = mpsc::channel();
729        let second = thread::spawn({
730            let closer = closer.clone();
731            let started = started.clone();
732            let finished = finished.clone();
733            move || {
734                started.send(()).unwrap();
735                closer.close();
736                finished.send(()).unwrap();
737            }
738        });
739        let owner = thread::spawn(move || {
740            started.send(()).unwrap();
741            drop(stream);
742            finished.send(()).unwrap();
743        });
744        starts.recv().unwrap();
745        starts.recv().unwrap();
746        tester.wait_registered(3);
747        assert!(finishes.try_recv().is_err());
748
749        // Release the callback and await all three closers
750        release.open();
751        finishes.recv().unwrap();
752        finishes.recv().unwrap();
753        finishes.recv().unwrap();
754        first.join().unwrap();
755        second.join().unwrap();
756        owner.join().unwrap();
757    }
758
759    /// Adapter operation held in flight while the test requests shutdown.
760    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
761    enum BlockAt {
762        /// Raw read held until the test releases it.
763        Read,
764        /// Raw write held until the test releases it.
765        Write,
766        /// Flush held until the test releases it.
767        Flush,
768    }
769
770    /// Barrier holding one adapter operation until the test releases it.
771    ///
772    /// This keeps I/O in progress long enough to observe shutdown.
773    struct Gate {
774        /// Clock shared with both gated adapters.
775        clock: Clock,
776        /// Operation this barrier holds.
777        at: BlockAt,
778        /// Channel notifying the test that the held operation started.
779        entered: mpsc::Sender<()>,
780        /// Gate the test opens to let the held operation finish.
781        released: testing::Gate,
782        /// Read, write and flush calls made through either half.
783        calls: AtomicUsize,
784    }
785
786    impl Gate {
787        /// Counts an adapter call and holds the selected operation until
788        /// released or its supplied deadline expires.
789        fn call(&self, at: BlockAt, deadline: Option<Instant>) -> io::Result<()> {
790            self.calls.fetch_add(1, Ordering::SeqCst);
791            if at == self.at {
792                self.entered.send(()).unwrap();
793                self.released.wait(deadline)?;
794            }
795            Ok(())
796        }
797
798        /// Allows the admitted operation to finish.
799        fn release(&self) {
800            self.released.open();
801        }
802    }
803
804    /// Adapter half sharing a gate with the test driver.
805    struct GatedAdapter {
806        /// Barrier shared with the other half and the test.
807        gate: Arc<Gate>,
808        /// Latest deadline installed through either setter.
809        deadline: Option<Instant>,
810    }
811
812    impl Read for GatedAdapter {
813        fn clock(&self) -> Clock {
814            self.gate.clock.clone()
815        }
816
817        fn set_read_deadline(&mut self, deadline: Option<Instant>) -> io::Result<()> {
818            self.deadline = deadline;
819            Ok(())
820        }
821    }
822
823    impl io::Read for GatedAdapter {
824        fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
825            self.gate.call(BlockAt::Read, self.deadline)?;
826            Ok(0)
827        }
828    }
829
830    impl Write for GatedAdapter {
831        fn clock(&self) -> Clock {
832            self.gate.clock.clone()
833        }
834
835        fn set_write_deadline(&mut self, deadline: Instant) -> io::Result<()> {
836            self.deadline = Some(deadline);
837            Ok(())
838        }
839    }
840
841    impl io::Write for GatedAdapter {
842        fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
843            self.gate.call(
844                BlockAt::Write,
845                Some(self.deadline.expect("write deadline installed")),
846            )?;
847            Ok(bytes.len())
848        }
849
850        fn flush(&mut self) -> io::Result<()> {
851            self.gate.call(
852                BlockAt::Flush,
853                Some(self.deadline.expect("write deadline installed")),
854            )
855        }
856    }
857
858    /// Checks that close waits for an admitted read, write or flush after the
859    /// shutdown callback returns, and later I/O never reaches the adapter.
860    #[test]
861    fn test_every_closer_waits_for_active_io_and_refuses_new_io() {
862        for at in [BlockAt::Read, BlockAt::Write, BlockAt::Flush] {
863            // Hold one admitted adapter operation on the stream's clock
864            let tester = test_clock();
865            let clock = tester.clock();
866            let (entered, entries) = mpsc::channel();
867            let gate = Arc::new(Gate {
868                clock: clock.clone(),
869                at,
870                entered,
871                released: testing::Gate::new(&clock),
872                calls: AtomicUsize::new(0),
873            });
874            let (requested, requests) = mpsc::channel();
875            let stream = Stream::new(
876                GatedAdapter {
877                    gate: gate.clone(),
878                    deadline: None,
879                },
880                GatedAdapter {
881                    gate: gate.clone(),
882                    deadline: None,
883                },
884                move || {
885                    requested.send(()).unwrap();
886                },
887            );
888            let (reader, writer, closer, _) = stream.into_parts();
889            let io_closer = closer.clone();
890            let deadline = clock.now() + Duration::from_secs(5);
891            let io = thread::spawn(move || {
892                let mut reader = ReadHalf {
893                    inner: reader,
894                    closer: io_closer.clone(),
895                };
896                let mut writer = WriteHalf {
897                    inner: writer,
898                    closer: io_closer,
899                };
900                match at {
901                    BlockAt::Read => assert_eq!(reader.read(&mut [0], None).unwrap(), 0),
902                    BlockAt::Write => assert_eq!(
903                        writer.write(&[1], deadline).unwrap_err().kind(),
904                        io::ErrorKind::NotConnected
905                    ),
906                    BlockAt::Flush => writer.write(&[], deadline).unwrap(),
907                }
908                (reader, writer)
909            });
910            entries.recv().unwrap();
911
912            // Require both I/O and shutdown to register waits before checking completion
913            let (finished, finishes) = mpsc::channel();
914            let closing = thread::spawn({
915                let closer = closer.clone();
916                move || {
917                    closer.close();
918                    finished.send(()).unwrap();
919                }
920            });
921            requests.recv().unwrap();
922            tester.wait_registered(2);
923            assert!(finishes.try_recv().is_err());
924
925            // Release the admitted operation and refuse every new adapter call
926            gate.release();
927            finishes.recv().unwrap();
928            closing.join().unwrap();
929            let (mut reader, mut writer) = io.join().unwrap();
930            let calls = gate.calls.load(Ordering::SeqCst);
931            assert_eq!(reader.read(&mut [0], None).unwrap(), 0);
932            assert!(matches!(
933                writer.write(&[1], deadline),
934                Err(err) if err.kind() == io::ErrorKind::NotConnected
935            ));
936            assert_eq!(gate.calls.load(Ordering::SeqCst), calls);
937            closer.close();
938            assert!(requests.try_recv().is_err(), "shutdown called twice");
939        }
940    }
941
942    /// Writer accepting one byte per write, able to hold its flush until the
943    /// supplied deadline.
944    ///
945    /// Recorded deadlines reveal whether partial progress restarts the budget.
946    struct BudgetWriter {
947        /// Clock governing writes and the deliberately stalled flush.
948        clock: Clock,
949        /// Bytes accepted so far.
950        bytes: Vec<u8>,
951        /// Deadline each accepted write and each flush saw.
952        deadlines: Vec<Instant>,
953        /// Whether flush sleeps until its deadline and then times out.
954        stall_flush: bool,
955        /// Latest installed write deadline.
956        deadline: Option<Instant>,
957        /// Number of deadline setter calls.
958        settings: usize,
959    }
960
961    impl Write for BudgetWriter {
962        fn clock(&self) -> Clock {
963            self.clock.clone()
964        }
965
966        fn set_write_deadline(&mut self, deadline: Instant) -> io::Result<()> {
967            self.deadline = Some(deadline);
968            self.settings += 1;
969            Ok(())
970        }
971    }
972
973    impl io::Write for BudgetWriter {
974        fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
975            let deadline = self.deadline.expect("write deadline installed");
976            check_deadline(&self.clock(), deadline)?;
977            self.deadlines.push(deadline);
978            self.bytes.push(bytes[0]);
979            Ok(1)
980        }
981
982        fn flush(&mut self) -> io::Result<()> {
983            let deadline = self.deadline.expect("write deadline installed");
984            self.deadlines.push(deadline);
985            if self.stall_flush {
986                self.clock.sleep_until(deadline);
987                return Err(io::ErrorKind::TimedOut.into());
988            }
989            check_deadline(&self.clock(), deadline)
990        }
991    }
992
993    /// Checks that partial writes and a stalled flush share one absolute
994    /// deadline, a timeout leaves the stream reusable and a zero budget skips I/O.
995    #[test]
996    fn test_output_deadline_and_reuse() {
997        // Start a partial write whose flush waits until its clock deadline
998        let mut tester = test_clock();
999        let clock = tester.clock();
1000        let timeout = Duration::from_millis(40);
1001        let stream = Stream::new(
1002            Memory::new(io::empty(), &clock),
1003            BudgetWriter {
1004                clock: clock.clone(),
1005                bytes: Vec::new(),
1006                deadlines: Vec::new(),
1007                stall_flush: true,
1008                deadline: None,
1009                settings: 0,
1010            },
1011            || {},
1012        )
1013        .set_write_timeout(timeout);
1014        let (_, inner, closer, configured) = stream.into_parts();
1015        assert_eq!(configured, timeout);
1016        let mut writer = WriteHalf { inner, closer };
1017        let deadline = clock.now() + configured;
1018        let result = thread::scope(|scope| {
1019            let writing = scope.spawn(|| writer.write(b"abc", deadline));
1020            tester.wait_registered(1);
1021            assert_eq!(tester.next_deadline(), Some(deadline));
1022            tester.advance_to(deadline);
1023            writing.join().unwrap()
1024        });
1025        assert_eq!(result.unwrap_err().kind(), io::ErrorKind::TimedOut);
1026        assert_eq!(writer.inner.deadlines, vec![deadline; 4]);
1027        assert_eq!(writer.inner.settings, 1);
1028
1029        // Resume writes with a new deadline on the same stream
1030        writer.inner.stall_flush = false;
1031        let deadline = clock.now() + Duration::from_secs(5);
1032        writer.write(b"d", deadline).unwrap();
1033        assert_eq!(writer.inner.bytes, b"abcd");
1034        assert_eq!(writer.inner.settings, 2);
1035
1036        // Reject an already expired budget before configuring adapter I/O
1037        let calls = writer.inner.deadlines.len();
1038        let deadline = clock.now();
1039        assert!(matches!(
1040            writer.write(b"e", deadline),
1041            Err(err) if err.kind() == io::ErrorKind::TimedOut
1042        ));
1043        assert_eq!(writer.inner.deadlines.len(), calls);
1044        assert_eq!(writer.inner.settings, 2);
1045        writer.closer.close();
1046    }
1047
1048    /// Checks that write retries keep the unsent suffix, zero progress fails
1049    /// without flushing and an interrupted flush is returned without a retry.
1050    #[test]
1051    fn test_partial_write_retries_and_failures() {
1052        /// Writer replaying scripted results while recording offered and
1053        /// accepted bytes.
1054        struct Script {
1055            /// Clock governing every scripted partial write.
1056            clock: Clock,
1057            /// Results the next writes return, in order.
1058            results: std::collections::VecDeque<io::Result<usize>>,
1059            /// Bytes offered to each write call.
1060            offered: Vec<Vec<u8>>,
1061            /// Bytes the writes accepted, in order.
1062            accepted: Vec<u8>,
1063            /// Whether the flush fails as interrupted.
1064            interrupted_flush: bool,
1065            /// Number of flush calls.
1066            flushes: usize,
1067            /// Number of deadline setter calls.
1068            settings: usize,
1069            /// Latest installed write deadline.
1070            deadline: Option<Instant>,
1071        }
1072
1073        impl Write for Script {
1074            fn clock(&self) -> Clock {
1075                self.clock.clone()
1076            }
1077
1078            fn set_write_deadline(&mut self, deadline: Instant) -> io::Result<()> {
1079                self.settings += 1;
1080                self.deadline = Some(deadline);
1081                Ok(())
1082            }
1083        }
1084
1085        impl io::Write for Script {
1086            fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
1087                check_deadline(
1088                    &self.clock(),
1089                    self.deadline.expect("write deadline installed"),
1090                )?;
1091                self.offered.push(bytes.to_vec());
1092                let result = self.results.pop_front().expect("unexpected write");
1093                if let Ok(size) = &result {
1094                    self.accepted.extend_from_slice(&bytes[..*size]);
1095                }
1096                result
1097            }
1098
1099            fn flush(&mut self) -> io::Result<()> {
1100                check_deadline(
1101                    &self.clock(),
1102                    self.deadline.expect("write deadline installed"),
1103                )?;
1104                self.flushes += 1;
1105                assert_eq!(self.flushes, 1, "flush retried");
1106                if self.interrupted_flush {
1107                    Err(io::ErrorKind::Interrupted.into())
1108                } else {
1109                    Ok(())
1110                }
1111            }
1112        }
1113
1114        for (stalls, interrupted_flush) in [(false, false), (true, false), (false, true)] {
1115            // Run an interrupted write followed by partial progress on a paused clock
1116            let tester = test_clock();
1117            let clock = tester.clock();
1118            let mut writer = WriteHalf {
1119                inner: Script {
1120                    clock: clock.clone(),
1121                    results: [
1122                        Err(io::ErrorKind::Interrupted.into()),
1123                        Ok(1),
1124                        Ok(usize::from(!stalls)),
1125                    ]
1126                    .into(),
1127                    offered: Vec::new(),
1128                    accepted: Vec::new(),
1129                    interrupted_flush,
1130                    flushes: 0,
1131                    settings: 0,
1132                    deadline: None,
1133                },
1134                closer: Closer::new(&clock, || {}),
1135            };
1136            let result = writer.write(b"ab", clock.now() + Duration::from_secs(5));
1137            if stalls {
1138                assert_eq!(result.unwrap_err().kind(), io::ErrorKind::WriteZero);
1139            } else if interrupted_flush {
1140                assert_eq!(result.unwrap_err().kind(), io::ErrorKind::Interrupted);
1141            } else {
1142                result.unwrap();
1143            }
1144
1145            // Retain the unsent suffix across retries and configure the deadline once
1146            assert_eq!(
1147                writer.inner.offered,
1148                [b"ab".to_vec(), b"ab".to_vec(), b"b".to_vec()]
1149            );
1150            assert_eq!(
1151                writer.inner.accepted,
1152                if stalls { &b"a"[..] } else { &b"ab"[..] }
1153            );
1154            assert_eq!(writer.inner.flushes, usize::from(!stalls));
1155            assert_eq!(writer.inner.settings, 1);
1156            writer.closer.close();
1157        }
1158    }
1159
1160    /// Checks that a failed deadline setter prevents byte I/O and keeps its
1161    /// error, even an `Interrupted` or `TimedOut` one.
1162    ///
1163    /// Only retryable errors from an actual read may start another attempt.
1164    #[test]
1165    fn test_deadline_setter_failure_prevents_io() {
1166        /// Adapter refusing deadline installation, panicking if byte I/O is
1167        /// attempted.
1168        struct Refused {
1169            /// Clock shared by the test's adapters and closer.
1170            clock: Clock,
1171            /// Number of deadline setter calls.
1172            settings: usize,
1173            /// Error kind the setter fails with.
1174            kind: io::ErrorKind,
1175        }
1176
1177        impl Refused {
1178            /// Fails the setter call, panicking on a second one so an incorrect
1179            /// retry fails the test promptly.
1180            fn reject(&mut self) -> io::Result<()> {
1181                self.settings += 1;
1182                assert_eq!(self.settings, 1, "deadline setter failure retried");
1183                Err(io::Error::new(self.kind, "deadline refused"))
1184            }
1185        }
1186
1187        impl Read for Refused {
1188            fn clock(&self) -> Clock {
1189                self.clock.clone()
1190            }
1191
1192            fn set_read_deadline(&mut self, _: Option<Instant>) -> io::Result<()> {
1193                self.reject()
1194            }
1195        }
1196
1197        impl io::Read for Refused {
1198            fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
1199                panic!("read after deadline setter failed")
1200            }
1201        }
1202
1203        impl Write for Refused {
1204            fn clock(&self) -> Clock {
1205                self.clock.clone()
1206            }
1207
1208            fn set_write_deadline(&mut self, _: Instant) -> io::Result<()> {
1209                self.reject()
1210            }
1211        }
1212
1213        impl io::Write for Refused {
1214            fn write(&mut self, _: &[u8]) -> io::Result<usize> {
1215                panic!("write after deadline setter failed")
1216            }
1217
1218            fn flush(&mut self) -> io::Result<()> {
1219                panic!("flush after deadline setter failed")
1220            }
1221        }
1222
1223        for kind in [io::ErrorKind::Interrupted, io::ErrorKind::TimedOut] {
1224            // Fail each direction's deadline installation before byte I/O
1225            let tester = test_clock();
1226            let clock = tester.clock();
1227            let closer = Closer::new(&clock, || {});
1228            let mut reader = ReadHalf {
1229                inner: Refused {
1230                    clock: clock.clone(),
1231                    settings: 0,
1232                    kind,
1233                },
1234                closer: closer.clone(),
1235            };
1236            let err = reader.read(&mut [0], None).unwrap_err();
1237            assert_eq!(err.kind(), kind);
1238            assert_eq!(err.to_string(), "deadline refused");
1239            let mut writer = WriteHalf {
1240                inner: Refused {
1241                    clock: clock.clone(),
1242                    settings: 0,
1243                    kind,
1244                },
1245                closer: closer.clone(),
1246            };
1247            assert!(matches!(
1248                writer.write(&[1], clock.now() + Duration::from_secs(5)),
1249                Err(err) if err.kind() == kind && err.to_string() == "deadline refused"
1250            ));
1251
1252            // Refuse all new I/O after shutdown without another deadline setter call
1253            closer.close();
1254            assert_eq!(reader.read(&mut [0], None).unwrap(), 0);
1255            assert!(matches!(
1256                writer.write(&[1], clock.now() + Duration::from_secs(5)),
1257                Err(err) if err.kind() == io::ErrorKind::NotConnected
1258            ));
1259            assert_eq!(reader.inner.settings, 1);
1260            assert_eq!(writer.inner.settings, 1);
1261        }
1262    }
1263
1264    /// Checks that a partial write returning after its deadline keeps its
1265    /// accepted byte but fails the complete operation.
1266    ///
1267    /// Depending on the input length, either the remaining write or the flush
1268    /// is refused before I/O.
1269    #[test]
1270    fn test_late_write_preserves_progress() {
1271        /// Writer accepting one byte per write, returning only at its installed
1272        /// deadline.
1273        struct LateWriter {
1274            /// Clock governing the late partial write.
1275            clock: Clock,
1276            /// Latest installed write deadline.
1277            deadline: Option<Instant>,
1278            /// Bytes accepted so far.
1279            bytes: Vec<u8>,
1280        }
1281
1282        impl Write for LateWriter {
1283            fn clock(&self) -> Clock {
1284                self.clock.clone()
1285            }
1286
1287            fn set_write_deadline(&mut self, deadline: Instant) -> io::Result<()> {
1288                self.deadline = Some(deadline);
1289                Ok(())
1290            }
1291        }
1292
1293        impl io::Write for LateWriter {
1294            fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
1295                self.bytes.push(bytes[0]);
1296                self.clock
1297                    .sleep_until(self.deadline.expect("write deadline installed"));
1298                Ok(1)
1299            }
1300
1301            fn flush(&mut self) -> io::Result<()> {
1302                panic!("flush admitted after deadline expired")
1303            }
1304        }
1305
1306        for bytes in [&b"a"[..], &b"ab"[..]] {
1307            // Park a successful partial write until its deadline
1308            let mut tester = test_clock();
1309            let clock = tester.clock();
1310            let mut writer = WriteHalf {
1311                inner: LateWriter {
1312                    clock: clock.clone(),
1313                    deadline: None,
1314                    bytes: Vec::new(),
1315                },
1316                closer: Closer::new(&clock, || {}),
1317            };
1318            let deadline = clock.now() + Duration::from_millis(40);
1319            let result = thread::scope(|scope| {
1320                let writing = scope.spawn(|| writer.write(bytes, deadline));
1321                tester.wait_registered(1);
1322                tester.advance_to(deadline);
1323                writing.join().unwrap()
1324            });
1325
1326            // Retain only the accepted byte and reject all subsequent adapter I/O
1327            assert_eq!(result.unwrap_err().kind(), io::ErrorKind::TimedOut);
1328            assert_eq!(writer.inner.bytes, b"a");
1329            writer.closer.close();
1330        }
1331    }
1332}