Skip to main content

windows_overlapped_io_sys/
started.rs

1// Copyright (c) 2026 Mike Grier
2//! The outcome of a buffer-owning adapter's submission.
3//!
4//! An adapter (`fs::read`, `device::ioctl`, `socket::send`, ...) owns the buffers
5//! an operation reads into or writes from, so it can report one of exactly two
6//! things once the native call returns: the operation is in flight and its
7//! result must be claimed from a completion later, or it is already finished and
8//! the buffers are back in hand. [`Started`] is that pair.
9//!
10//! # Why the synchronous case is visible rather than hidden
11//!
12//! It exists because of `FILE_SKIP_COMPLETION_PORT_ON_SUCCESS` (see
13//! [`crate::Issued::Pending`] for what that mode changes). Without the mode,
14//! every operation on an IOCP-associated endpoint produces a completion packet
15//! -- even one that succeeded immediately -- so an adapter can always hand back
16//! a claim-later token. With the mode, a synchronously-successful operation
17//! produces no packet at all, and the token would name a completion that is
18//! never coming.
19//!
20//! An adapter therefore cannot paper over the difference, because the two cases
21//! do not merely differ in timing: they differ in *who owns the payload*. A
22//! caller that ignored the distinction would either wait forever for a packet
23//! that will not arrive, or drop a result that was already delivered. Making
24//! both arms explicit costs a `match` and removes that whole class of mistake.
25//!
26//! A caller that never enables the mode will only ever observe
27//! [`Started::Pending`], and can say so with [`Started::expect_pending`].
28
29/// What became of an adapter submission that did not fail immediately.
30///
31/// This is [`crate::Submitted`] as an adapter reports it: the `Failed` arm is
32/// folded into the enclosing `io::Result`'s `Err`, and the operation storage of
33/// a synchronous completion is already reduced to the payload the matching
34/// token's `claim` would have yielded, so the two arms report the same shape.
35#[derive(Debug)]
36pub enum Started<T, P> {
37    /// The operation is in flight and a completion will arrive for it. Claim its
38    /// result with the token, which also carries the operation's identity for
39    /// cancellation and matching.
40    Pending(T),
41    /// The operation finished synchronously and no completion will arrive, so
42    /// there is nothing to claim and its payload is returned directly.
43    ///
44    /// Only reachable on an endpoint in `FILE_SKIP_COMPLETION_PORT_ON_SUCCESS`
45    /// mode; an endpoint left in the default mode always reports
46    /// [`Started::Pending`], because the I/O Manager queues a packet even for an
47    /// immediate success.
48    Completed {
49        /// The buffers the operation owned -- the same payload the token's
50        /// `claim` yields on the pending path.
51        payload: P,
52        /// The byte count the native call reported.
53        bytes_transferred: usize,
54    },
55}
56
57impl<T, P> Started<T, P> {
58    /// Whether a completion will arrive for this operation.
59    #[must_use]
60    pub fn is_pending(&self) -> bool {
61        matches!(self, Started::Pending(_))
62    }
63
64    /// Whether the operation already finished with no completion to come.
65    #[must_use]
66    pub fn is_completed(&self) -> bool {
67        matches!(self, Started::Completed { .. })
68    }
69
70    /// The token, if a completion will arrive.
71    #[must_use]
72    pub fn pending(self) -> Option<T> {
73        match self {
74            Started::Pending(token) => Some(token),
75            Started::Completed { .. } => None,
76        }
77    }
78
79    /// The payload and byte count, if the operation already finished.
80    #[must_use]
81    pub fn completed(self) -> Option<(P, usize)> {
82        match self {
83            Started::Completed {
84                payload,
85                bytes_transferred,
86            } => Some((payload, bytes_transferred)),
87            Started::Pending(_) => None,
88        }
89    }
90
91    /// The token, panicking if the operation completed synchronously.
92    ///
93    /// For a caller that never puts its endpoints in
94    /// `FILE_SKIP_COMPLETION_PORT_ON_SUCCESS` mode, where the synchronous arm is
95    /// unreachable and matching on it is noise.
96    ///
97    /// # Panics
98    ///
99    /// Panics with `message` if the operation completed synchronously, which
100    /// means the endpoint was in skip-on-success mode after all.
101    #[must_use]
102    #[track_caller]
103    pub fn expect_pending(self, message: &str) -> T {
104        match self {
105            Started::Pending(token) => token,
106            Started::Completed { .. } => panic!("{message}"),
107        }
108    }
109}
110
111#[cfg(test)]
112mod tests;