Skip to main content

windows_overlapped_io_sys/
iocp.rs

1// Copyright (c) 2026 Mike Grier
2//! Raw I/O completion port backend: port ownership, association, and dequeue.
3//!
4//! A [`CompletionPort`] owns a completion-port handle and can service many
5//! endpoints, each associated with a caller-chosen completion key. Association
6//! is the consuming transition that binds an [`UnassociatedEndpoint`] to this
7//! backend. The port does not create worker threads; the owner decides where
8//! [`CompletionPort::get`] runs. Submission of real overlapped operations, and
9//! the reclamation that follows their completion, are built on top of this
10//! module.
11
12use std::cell::Cell;
13use std::collections::HashMap;
14use std::fmt;
15use std::io;
16use std::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, OwnedHandle};
17use std::panic::Location;
18use std::sync::atomic::{AtomicUsize, Ordering};
19use std::sync::{Arc, Mutex, MutexGuard};
20
21use windows_sys::Win32::Foundation::{HANDLE, INVALID_HANDLE_VALUE, WAIT_TIMEOUT};
22use windows_sys::Win32::System::IO::{
23    CancelIoEx, CreateIoCompletionPort, GetQueuedCompletionStatus, OVERLAPPED,
24    PostQueuedCompletionStatus,
25};
26
27use crate::identity::{OperationId, OperationRegistry};
28use crate::{Operation, OperationState, UnassociatedEndpoint};
29
30/// Bound on each `GetQueuedCompletionStatus` wait inside [`CompletionPort::run_down`].
31///
32/// `run_down` cannot wait without timeout: the port is shareable and
33/// [`CompletionPort::get`] takes `&self`, so a concurrent consumer can dequeue
34/// the last packet -- and clear the registry entry -- after `run_down` has
35/// observed a nonzero outstanding count but before its own wait begins. An
36/// unbounded wait would then block forever on a packet that is no longer coming.
37/// A bounded wait wakes periodically so the loop can recheck the live count and
38/// return once it reaches zero. The interval only bounds that recheck latency; a
39/// packet genuinely destined for `run_down` is still returned the instant it
40/// arrives, so this is not a busy-poll of a live operation.
41const RUN_DOWN_POLL_MS: u32 = 5;
42
43/// Optional per-operation source information, recorded only while source
44/// tracking is enabled.
45struct Track {
46    location: &'static Location<'static>,
47    #[cfg(feature = "operation-backtrace")]
48    backtrace: std::backtrace::Backtrace,
49}
50
51/// State shared between a port, its completions, and the drain path.
52///
53/// `live` is the registry of operations submitted through this port whose
54/// completion packet has not yet been dequeued. It governs rundown (its length
55/// is the outstanding count) and answers whether an [`OperationId`] still names
56/// an operation a packet is still coming for, which is what keeps a retained
57/// identity from cancelling an operation that merely recycled its address.
58/// Registration ends at dequeue, not at reclamation: a held [`Completion`] owns
59/// its operation's storage but is no longer awaiting anything, and counting it
60/// would make rundown wait for a packet it had already received.
61/// `tracked` is consulted only when source tracking is enabled.
62struct PortState {
63    live: OperationRegistry,
64    tracked: Mutex<HashMap<usize, Track>>,
65    /// Each associated endpoint's own outstanding-operation count, keyed by
66    /// its completion key (M1, PR #20 review response via `windows-ioring-sys`'s
67    /// M8). `live` above answers "is a packet still coming for this address",
68    /// port-wide; this answers the same question scoped to one endpoint, which
69    /// is what `AssociatedEndpoint`'s own `Drop` needs -- `CompletionPort::run_down`
70    /// is the wrong scope for it, since it blocks on every endpoint's operations,
71    /// not just one's.
72    endpoint_outstanding: Mutex<HashMap<usize, Arc<AtomicUsize>>>,
73}
74
75impl PortState {
76    fn new() -> Self {
77        Self {
78            live: OperationRegistry::new(),
79            tracked: Mutex::new(HashMap::new()),
80            endpoint_outstanding: Mutex::new(HashMap::new()),
81        }
82    }
83}
84
85/// Lock a mutex, recovering the guard even if a previous holder panicked.
86fn lock<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
87    mutex.lock().unwrap_or_else(|poison| poison.into_inner())
88}
89
90/// An owned I/O completion port.
91pub struct CompletionPort {
92    handle: OwnedHandle,
93    state: Arc<PortState>,
94}
95
96impl CompletionPort {
97    /// Create a new completion port.
98    ///
99    /// `concurrency` is the maximum number of threads the system lets run
100    /// completions for this port concurrently; zero means one per processor.
101    pub fn new(concurrency: u32) -> io::Result<Self> {
102        // SAFETY: creating a fresh port with no associated file handle.
103        let handle = unsafe {
104            CreateIoCompletionPort(INVALID_HANDLE_VALUE, std::ptr::null_mut(), 0, concurrency)
105        };
106        if handle.is_null() {
107            return Err(io::Error::last_os_error());
108        }
109        // SAFETY: the call returned a fresh, exclusively owned port handle.
110        let handle = unsafe { OwnedHandle::from_raw_handle(handle) };
111        Ok(Self {
112            handle,
113            state: Arc::new(PortState::new()),
114        })
115    }
116
117    /// Associate an overlapped endpoint with this port under `key`.
118    ///
119    /// Completions for operations issued on the endpoint are delivered to this
120    /// port and tagged with `key`. The association is permanent for the life of
121    /// the handle, so the returned endpoint borrows the port.
122    ///
123    /// # Errors
124    ///
125    /// Returns [`io::ErrorKind::InvalidInput`] if `key` is already associated
126    /// with a live endpoint on this port (PR #20 review response): a
127    /// completion key is a caller-defined tag, not a unique endpoint identity,
128    /// and `deregister_dequeued` finds an endpoint's outstanding-operation
129    /// counter by looking it up under `key` alone. Associating a second
130    /// endpoint under a key already in use would silently replace the first
131    /// endpoint's counter in `endpoint_outstanding`; completions for the
132    /// first endpoint would then decrement the second's counter (which can
133    /// underflow) while the first's own counter never reaches zero, blocking
134    /// its `Drop` forever. Rejecting the duplicate before native association
135    /// keeps every key's counter unambiguous instead. Also returns the error
136    /// from `CreateIoCompletionPort`.
137    pub fn associate(
138        &self,
139        endpoint: UnassociatedEndpoint,
140        key: usize,
141    ) -> io::Result<AssociatedEndpoint<'_>> {
142        // Read before the handle is taken out, so the mode travels with the
143        // endpoint into association rather than being lost at the boundary.
144        let modes = endpoint.notification_modes();
145        let handle = endpoint.into_handle();
146        let outstanding = Arc::new(AtomicUsize::new(0));
147        {
148            // Checked and reserved under one lock acquisition, so no other
149            // `associate` call can race into the same key between the check
150            // and the insert.
151            let mut endpoint_outstanding = lock(&self.state.endpoint_outstanding);
152            if endpoint_outstanding.contains_key(&key) {
153                return Err(io::Error::new(
154                    io::ErrorKind::InvalidInput,
155                    format!(
156                        "windows-overlapped-io-sys: completion key {key} is already \
157                         associated with a live endpoint on this port; each endpoint must \
158                         use a distinct key"
159                    ),
160                ));
161            }
162            endpoint_outstanding.insert(key, Arc::clone(&outstanding));
163        }
164        // SAFETY: associating a valid handle with a valid port; the concurrency
165        // argument is ignored when an existing port is supplied.
166        let result = unsafe { CreateIoCompletionPort(handle.as_raw_handle(), self.raw(), key, 0) };
167        if result.is_null() {
168            // Roll back the reservation above; nothing else can have used it,
169            // since the endpoint was never associated to receive completions.
170            lock(&self.state.endpoint_outstanding).remove(&key);
171            return Err(io::Error::last_os_error());
172        }
173        Ok(AssociatedEndpoint {
174            port: self,
175            handle,
176            key,
177            modes,
178            outstanding,
179        })
180    }
181
182    /// Post a user-defined wakeup packet to this port.
183    ///
184    /// The packet carries `key` and `bytes_transferred` with a null `OVERLAPPED`,
185    /// which keeps it distinguishable from operation completions; identify it by
186    /// its `key`.
187    pub fn post(&self, key: usize, bytes_transferred: u32) -> io::Result<()> {
188        // SAFETY: the port handle is valid; a null overlapped marks a user packet.
189        let ok = unsafe {
190            PostQueuedCompletionStatus(self.raw(), bytes_transferred, key, std::ptr::null())
191        };
192        if ok == 0 {
193            return Err(io::Error::last_os_error());
194        }
195        Ok(())
196    }
197
198    #[cfg(test)]
199    pub(crate) fn post_raw(
200        &self,
201        key: usize,
202        bytes_transferred: u32,
203        overlapped: *mut OVERLAPPED,
204    ) -> io::Result<()> {
205        // SAFETY: tests use this to simulate an operation completion for a live
206        // operation's OVERLAPPED pointer.
207        let ok = unsafe {
208            PostQueuedCompletionStatus(self.raw(), bytes_transferred, key, overlapped.cast_const())
209        };
210        if ok == 0 {
211            return Err(io::Error::last_os_error());
212        }
213        Ok(())
214    }
215
216    /// Dequeue one completion packet, waiting up to `timeout_ms` milliseconds.
217    ///
218    /// Returns `Ok(None)` when the wait times out with no packet. A packet is
219    /// returned even when its operation failed; the failure is reported through
220    /// [`Completion::error`].
221    pub fn get(&self, timeout_ms: u32) -> io::Result<Option<Completion>> {
222        let mut bytes_transferred: u32 = 0;
223        let mut key: usize = 0;
224        let mut overlapped: *mut OVERLAPPED = std::ptr::null_mut();
225        // SAFETY: all out-parameters are valid for the duration of the call.
226        let ok = unsafe {
227            GetQueuedCompletionStatus(
228                self.raw(),
229                &mut bytes_transferred,
230                &mut key,
231                &mut overlapped,
232                timeout_ms,
233            )
234        };
235        if ok != 0 {
236            return Ok(Some(Completion {
237                key,
238                bytes_transferred,
239                overlapped,
240                error: None,
241                // Deregister as the packet leaves the queue, recovering the
242                // identity in the same step. See `deregister_dequeued`.
243                id: self.deregister_dequeued(key, overlapped),
244                claimed: Cell::new(false),
245            }));
246        }
247
248        let error = io::Error::last_os_error();
249        if overlapped.is_null() {
250            if error.raw_os_error() == Some(WAIT_TIMEOUT as i32) {
251                return Ok(None);
252            }
253            return Err(error);
254        }
255        // A packet for a failed operation was dequeued.
256        Ok(Some(Completion {
257            key,
258            bytes_transferred,
259            overlapped,
260            error: Some(error),
261            id: self.deregister_dequeued(key, overlapped),
262            claimed: Cell::new(false),
263        }))
264    }
265
266    /// Deregister an operation whose packet has just been dequeued, returning the
267    /// identity the registry recorded for it.
268    ///
269    /// Registration ends at *dequeue*, not at reclamation, because the registry
270    /// answers "is a packet still coming for this operation?" -- which is what
271    /// [`run_down`](Self::run_down) waits on and what
272    /// [`cancel`](AssociatedEndpoint::cancel) must not act against. Once the
273    /// packet is off the queue neither is true any longer, and no further packet
274    /// will ever arrive for it.
275    ///
276    /// Keeping the entry until the [`Completion`] was dropped instead made a
277    /// held completion indistinguishable from an undelivered packet, so dropping
278    /// the port while one was held blocked forever in an unbounded `get` waiting
279    /// for a packet that had already been delivered. The completion still owns the
280    /// operation's storage and still frees it on drop; that ownership is simply
281    /// no longer expressed through the registry.
282    ///
283    /// A null pointer (a user packet) and an address this port never registered
284    /// both return `None`, since `remove` reports only what it held. Also
285    /// decrements `key`'s endpoint-scoped outstanding count, but only when a
286    /// real registered operation was actually removed -- a user-posted packet
287    /// (`CompletionPort::post`) never incremented one, so it must not decrement
288    /// one either, even if it happens to carry a key that collides with a live
289    /// endpoint's.
290    fn deregister_dequeued(&self, key: usize, overlapped: *mut OVERLAPPED) -> Option<OperationId> {
291        let id = self.state.live.remove(overlapped);
292        if id.is_some() {
293            if crate::source_tracking_enabled() {
294                lock(&self.state.tracked).remove(&(overlapped as usize));
295            }
296            if let Some(outstanding) = lock(&self.state.endpoint_outstanding).get(&key) {
297                outstanding.fetch_sub(1, Ordering::SeqCst);
298            }
299        }
300        id
301    }
302
303    pub(crate) fn raw(&self) -> HANDLE {
304        self.handle.as_raw_handle()
305    }
306
307    /// The registry of operations submitted through this port whose completion
308    /// packet has not yet been dequeued, for backends that must validate an
309    /// identity before cancelling.
310    ///
311    /// Only the socket backend reaches for this; the file backend cancels
312    /// through `AssociatedEndpoint`, which already holds the port.
313    #[cfg(feature = "socket")]
314    pub(crate) fn live_operations(&self) -> &OperationRegistry {
315        &self.state.live
316    }
317
318    /// The number of operations submitted through this port whose completion
319    /// packet has not yet been dequeued.
320    ///
321    /// A packet that has been dequeued is *not* counted, even if the
322    /// [`Completion`] is still held and its storage not yet released. The count
323    /// measures what the port is still waiting to deliver, which is what
324    /// [`run_down`](Self::run_down) blocks on.
325    #[must_use]
326    pub fn outstanding(&self) -> usize {
327        self.state.live.len()
328    }
329
330    /// Block until a completion packet has been dequeued for every outstanding
331    /// operation.
332    ///
333    /// Every outstanding operation must already be cancelled or otherwise
334    /// destined to complete -- which closing or cancelling the endpoints
335    /// guarantees -- or this waits indefinitely. Each packet dequeued here is
336    /// reclaimed immediately, since the [`Completion`] this creates is dropped
337    /// within the loop.
338    ///
339    /// A [`Completion`] held elsewhere does not keep this waiting: its packet has
340    /// already been delivered, so it is not outstanding. It still owns the
341    /// operation's storage, and still frees it when dropped, which may be after
342    /// this returns and after the port itself is gone.
343    ///
344    /// The port is shareable, so another thread may be consuming completions at
345    /// the same time. Each wait here is therefore bounded (`RUN_DOWN_POLL_MS`)
346    /// and the live count is rechecked after it: a concurrent consumer can
347    /// dequeue the last packet -- and clear its registry entry -- in the window
348    /// between this loop observing a nonzero count and beginning its own wait,
349    /// and an unbounded wait would then block forever on a packet no longer
350    /// coming. Removing a registry entry does not wake a `GetQueuedCompletionStatus`
351    /// already in progress, so the recheck, not a wakeup, is what ends the wait.
352    pub fn run_down(&self) -> io::Result<()> {
353        while self.outstanding() > 0 {
354            self.get(RUN_DOWN_POLL_MS)?;
355        }
356        Ok(())
357    }
358
359    /// Submit an owned operation on this port, running the shared outstanding-
360    /// operation accounting around a caller-supplied native call.
361    ///
362    /// This is the accounting core shared by every endpoint kind: `issue`
363    /// receives only the stable `OVERLAPPED` pointer and performs the single
364    /// native overlapped call (the endpoint supplies its own handle or socket by
365    /// capture). The counting, source tracking, and reclamation on the
366    /// synchronous and failure paths are identical regardless of endpoint kind.
367    ///
368    /// # Safety
369    ///
370    /// `issue` must start exactly one overlapped operation using the provided
371    /// `OVERLAPPED` pointer and no other storage, and must classify the outcome
372    /// correctly: [`Issued::Pending`] only when a completion packet will be
373    /// delivered to this port, [`Issued::Completed`] only when the operation is
374    /// already complete and no packet will arrive, and `Err` only when the
375    /// submission failed and no completion will arrive.
376    ///
377    /// `issue` must not unwind. The operation is registered before it runs and
378    /// deregistered only on the paths below; a panic out of `issue` skips that
379    /// and -- because a panic before starting the I/O is indistinguishable from
380    /// one after -- rundown could then wait forever for a packet that will never
381    /// arrive. A closure that might panic must catch it and return `Err`.
382    #[track_caller]
383    pub(crate) unsafe fn submit_with<P, F>(&self, operation: Operation<P>, issue: F) -> Submitted<P>
384    where
385        P: Send + 'static,
386        F: FnOnce(*mut OVERLAPPED) -> io::Result<Issued>,
387    {
388        // Transfer the operation's storage out; the caller (kernel) owns it until
389        // it is reclaimed. `into_overlapped` arms the type-erased reclaim thunk.
390        let overlapped = operation.into_overlapped();
391        let identity = overlapped as usize;
392        // Stamp this submission with a fresh generation, so the identity names
393        // this operation and not whatever later operation may reuse the address.
394        let id = OperationId::mint(overlapped);
395
396        // Register before issuing so a completion cannot race ahead of the count.
397        let state = &self.state;
398        state.live.insert(id);
399        let tracking = crate::source_tracking_enabled();
400        if tracking {
401            lock(&state.tracked).insert(
402                identity,
403                Track {
404                    location: Location::caller(),
405                    #[cfg(feature = "operation-backtrace")]
406                    backtrace: std::backtrace::Backtrace::capture(),
407                },
408            );
409        }
410
411        match issue(overlapped) {
412            Ok(Issued::Pending) => Submitted::Pending(id),
413            Ok(Issued::Completed { bytes_transferred }) => {
414                // Synchronous completion with no packet to arrive (the
415                // skip-on-success case): balance the count and reclaim inline.
416                state.live.remove(overlapped);
417                if tracking {
418                    lock(&state.tracked).remove(&identity);
419                }
420                // SAFETY: the operation completed synchronously and no packet
421                // will arrive, so the kernel is done with the storage; reclaim
422                // the box we just leaked, exactly once.
423                let mut operation = unsafe { Operation::<P>::from_overlapped(overlapped) };
424                operation.set_state(OperationState::Completed);
425                Submitted::Completed {
426                    operation,
427                    bytes_transferred,
428                }
429            }
430            Err(error) => {
431                state.live.remove(overlapped);
432                if tracking {
433                    lock(&state.tracked).remove(&identity);
434                }
435                // SAFETY: no completion will arrive, so reclaim the operation we
436                // just leaked, exactly once.
437                let mut operation = unsafe { Operation::<P>::from_overlapped(overlapped) };
438                operation.set_state(OperationState::Idle);
439                Submitted::Failed { operation, error }
440            }
441        }
442    }
443
444    fn report_outstanding_at_drop(&self, count: usize) {
445        let tracked = lock(&self.state.tracked);
446        let mut message = format!(
447            "windows-overlapped-io-sys: CompletionPort dropped with {count} operation(s) still \
448             outstanding; call run_down() before dropping to control when this blocks."
449        );
450        if tracked.is_empty() {
451            message.push_str(
452                " Enable source tracking (WINDOWS_OVERLAPPED_IO_SYS_TRACK=1, or \
453                 set_source_tracking) to identify the submit sites.",
454            );
455        } else {
456            message.push_str(" Sources:");
457            for track in tracked.values() {
458                message.push_str("\n  - ");
459                message.push_str(&track.location.to_string());
460                #[cfg(feature = "operation-backtrace")]
461                {
462                    message.push_str("\n    backtrace:\n");
463                    message.push_str(&track.backtrace.to_string());
464                }
465            }
466        }
467        eprintln!("{message}");
468    }
469}
470
471impl fmt::Debug for CompletionPort {
472    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
473        f.debug_struct("CompletionPort")
474            .field("outstanding", &self.outstanding())
475            .finish_non_exhaustive()
476    }
477}
478
479impl Drop for CompletionPort {
480    fn drop(&mut self) {
481        let count = self.outstanding();
482        if count == 0 {
483            return;
484        }
485        // A blocking Drop signals that run_down() was skipped; name the sources.
486        self.report_outstanding_at_drop(count);
487        // Block until the kernel is done with every operation's storage.
488        let _ = self.run_down();
489    }
490}
491
492/// An overlapped endpoint bound to exactly one [`CompletionPort`].
493///
494/// The endpoint owns its handle and borrows the port it is associated with, so
495/// the port cannot be dropped while any endpoint still routes completions to it.
496/// It is intentionally not `Clone`.
497#[derive(Debug)]
498pub struct AssociatedEndpoint<'port> {
499    port: &'port CompletionPort,
500    handle: OwnedHandle,
501    key: usize,
502    modes: crate::NotificationModes,
503    /// This endpoint's own outstanding-operation count (M1, PR #20 review
504    /// response), incremented in `submit` and decremented in
505    /// `CompletionPort::deregister_dequeued`. Shared with the port's
506    /// `endpoint_outstanding` map under this endpoint's key, so `Drop` can
507    /// block on it without needing the port to know about endpoints at all.
508    outstanding: Arc<AtomicUsize>,
509}
510
511impl<'port> AssociatedEndpoint<'port> {
512    /// Borrow the underlying handle for issuing native operations.
513    #[must_use]
514    pub fn handle(&self) -> BorrowedHandle<'_> {
515        self.handle.as_handle()
516    }
517
518    /// The completion key packets from this endpoint are tagged with.
519    #[must_use]
520    pub fn key(&self) -> usize {
521        self.key
522    }
523
524    /// The completion-notification modes this endpoint carries, as declared
525    /// before it was associated.
526    ///
527    /// The adapters read this to classify a synchronous native success: with
528    /// [`crate::NotificationModes::skip_completion_port_on_success`] set, no
529    /// packet will arrive for one, so it is an [`Issued::Completed`] rather than
530    /// an [`Issued::Pending`].
531    #[must_use]
532    pub fn notification_modes(&self) -> crate::NotificationModes {
533        self.modes
534    }
535
536    /// The completion port this endpoint is associated with.
537    #[must_use]
538    pub fn port(&self) -> &'port CompletionPort {
539        self.port
540    }
541
542    /// How many operations submitted on this endpoint have not yet had their
543    /// completion packet dequeued.
544    ///
545    /// Unlike [`CompletionPort::outstanding`], this is scoped to this endpoint
546    /// alone -- what [`AssociatedEndpoint`]'s own blocking `Drop` waits on.
547    #[must_use]
548    pub fn outstanding(&self) -> usize {
549        self.outstanding.load(Ordering::SeqCst)
550    }
551
552    /// Submit an owned operation on this endpoint.
553    ///
554    /// `issue` performs the single native overlapped call using the endpoint's
555    /// handle and the operation's stable `OVERLAPPED` pointer. It classifies the
556    /// outcome as an [`Issued`]: [`Issued::Pending`] when a completion packet
557    /// will be delivered, or [`Issued::Completed`] when the call finished
558    /// synchronously and no packet will arrive -- the state a handle in
559    /// `FILE_SKIP_COMPLETION_PORT_ON_SUCCESS` mode reports on synchronous
560    /// success. It returns `Err` for an immediate failure that yields no
561    /// completion.
562    ///
563    /// On the pending path the operation's storage is transferred to the kernel
564    /// and recovered later with [`Completion::claim`]. On the synchronous and
565    /// failure paths the operation is returned intact through [`Submitted`] so
566    /// its storage can be reused or inspected.
567    ///
568    /// # Panics
569    ///
570    /// Panics if this port already has a live operation registered at the new
571    /// operation's storage address. That cannot happen through ordinary use --
572    /// `operation` owns freshly boxed storage -- and indicates a defect in this
573    /// crate's own bookkeeping rather than in the calling code. See
574    /// [`OperationRegistry::insert`] for the invariant involved.
575    ///
576    /// # Safety
577    ///
578    /// `issue` must start exactly one overlapped operation using the provided
579    /// `OVERLAPPED` pointer and no other storage, and must classify the outcome
580    /// correctly: [`Issued::Pending`] only when a completion packet will be
581    /// delivered to this endpoint's port, [`Issued::Completed`] only when the
582    /// operation is already complete and no packet will arrive, and `Err` only
583    /// when the submission failed and no completion will arrive.
584    ///
585    /// `issue` must not unwind: a panic out of it can leave an operation
586    /// registered with no completion coming, which makes rundown wait forever. A
587    /// closure that might panic must catch it and return `Err`.
588    ///
589    /// `P: 'static` because submitting leaks the operation's storage, to be
590    /// freed later through a thunk carrying no lifetime -- see
591    /// [`Operation::into_overlapped`].
592    #[track_caller]
593    pub unsafe fn submit<P, F>(&self, operation: Operation<P>, issue: F) -> Submitted<P>
594    where
595        P: Send + 'static,
596        F: FnOnce(BorrowedHandle<'_>, *mut OVERLAPPED) -> io::Result<Issued>,
597    {
598        let handle = self.handle();
599        // Incremented before the native call, not after `Submitted::Pending` is
600        // observed: `CompletionPort::get` can run on another thread and dequeue
601        // this same operation's packet before this thread would otherwise have
602        // recorded it, which would underflow the count on decrement. Reversed
603        // below on every path that turns out not to be pending, mirroring
604        // `submit_with`'s own register-before-issue discipline.
605        self.outstanding.fetch_add(1, Ordering::SeqCst);
606        // SAFETY: `issue`'s safety contract (restated on this method) is exactly
607        // what the shared core requires; the endpoint only supplies its handle.
608        let result = unsafe {
609            self.port
610                .submit_with(operation, move |overlapped| issue(handle, overlapped))
611        };
612        if !matches!(result, Submitted::Pending(_)) {
613            self.outstanding.fetch_sub(1, Ordering::SeqCst);
614        }
615        result
616    }
617
618    /// Request cancellation of a single outstanding operation.
619    ///
620    /// Cancellation is only a request: the operation still completes, typically
621    /// with `ERROR_OPERATION_ABORTED`, and that completion remains the point at
622    /// which its storage is reclaimed with [`Completion::claim`].
623    ///
624    /// The identity is checked against this port's live operations first. An
625    /// identity whose operation has already completed is rejected with
626    /// [`io::ErrorKind::NotFound`] and no native call is made, even if another
627    /// operation has since been given the same storage address -- so retaining
628    /// an identity too long can never cancel an unrelated operation.
629    ///
630    /// # Errors
631    ///
632    /// Returns [`io::ErrorKind::NotFound`] if `id` no longer names a live
633    /// operation, or the error from `CancelIoEx` if the native request fails.
634    pub fn cancel(&self, id: OperationId) -> io::Result<()> {
635        // The liveness check and the native call happen under one registry
636        // guard; splitting them would let the address be recycled in between.
637        self.port.state.live.cancel_if_live(id, || {
638            // SAFETY: cancelling by a valid handle and an OVERLAPPED identity
639            // the registry has confirmed still names a live operation, and which
640            // cannot be reclaimed and reissued while the guard is held.
641            let ok = unsafe { CancelIoEx(self.raw_handle(), id.as_ptr()) };
642            if ok == 0 {
643                return Err(io::Error::last_os_error());
644            }
645            Ok(())
646        })
647    }
648
649    /// Request cancellation of every outstanding operation on this endpoint.
650    pub fn cancel_all(&self) -> io::Result<()> {
651        // SAFETY: a null OVERLAPPED cancels all operations on the handle.
652        let ok = unsafe { CancelIoEx(self.raw_handle(), std::ptr::null()) };
653        if ok == 0 {
654            return Err(io::Error::last_os_error());
655        }
656        Ok(())
657    }
658
659    fn raw_handle(&self) -> HANDLE {
660        self.handle.as_raw_handle()
661    }
662}
663
664impl Drop for AssociatedEndpoint<'_> {
665    fn drop(&mut self) {
666        // `self.handle` is not actually closed until *after* this function
667        // returns (Rust drops struct fields in declaration order once the
668        // custom `Drop::drop` body finishes), so relying on close-cancels-
669        // pending-I/O would deadlock here: nothing has told the kernel to
670        // finish anything yet. Cancel explicitly first, while the handle is
671        // still open and the call is valid.
672        if self.outstanding() > 0 {
673            let _ = self.cancel_all();
674        }
675        // Mirrors `CompletionPort::run_down`: bounded waits, rechecking the
676        // live count after each, because a concurrent consumer can dequeue
677        // this endpoint's last packet between the check and the wait. Every
678        // packet dequeued here is for *some* endpoint on this port, not
679        // necessarily this one; whichever it is for still updates that
680        // endpoint's own count via `deregister_dequeued`; only this loop's own
681        // exit condition cares which one just reached zero.
682        while self.outstanding() > 0 {
683            let _ = self.port.get(RUN_DOWN_POLL_MS);
684        }
685        // Safe to drop the shared counter's port-side entry only now: nothing
686        // will ever decrement it again, since no packet can still be coming
687        // for an operation this endpoint submitted.
688        lock(&self.port.state.endpoint_outstanding).remove(&self.key);
689    }
690}
691
692/// How the native call in [`AssociatedEndpoint::submit`] accepted an operation.
693///
694/// `issue` returns this to tell the backend whether a completion packet will be
695/// delivered, so the port's outstanding-operation accounting stays correct.
696///
697/// This asks **"will a completion packet arrive?"**, *not* "did the native call
698/// finish synchronously?". Those come apart precisely because Windows queues a
699/// packet for a synchronously-successful overlapped request too -- see
700/// [`Issued::Pending`], which is where that distinction is spelled out.
701#[derive(Debug, Clone, Copy)]
702pub enum Issued {
703    /// A completion packet will be delivered to the port; the operation's
704    /// storage stays with the kernel until [`Completion::claim`] recovers it.
705    ///
706    /// This covers **both** of the native call's success shapes, and it is the
707    /// right answer for both for the same reason -- a packet is coming either
708    /// way:
709    ///
710    /// - `ERROR_IO_PENDING`: the request has not finished; its packet is queued
711    ///   when it does.
712    /// - **Native success returned immediately** (`TRUE`, or `0` from Winsock):
713    ///   the request has *already* finished, and its packet is *already*
714    ///   queued.
715    ///
716    /// The second is the counter-intuitive one, so it is worth stating why it
717    /// holds. For a handle opened for asynchronous I/O and associated with a
718    /// completion port, the I/O Manager queues a completion packet for every
719    /// request it completes, including one that succeeds immediately without
720    /// returning `ERROR_IO_PENDING`. The single documented exception is
721    /// `FILE_SKIP_COMPLETION_PORT_ON_SUCCESS`, and that flag's own definition
722    /// is what establishes the general rule: it says the I/O Manager "does not
723    /// queue a completion entry to the port, *when it would ordinarily do so*"
724    /// for a request that "returns success immediately without returning
725    /// ERROR_PENDING". Ordinarily -- that is, without the flag -- it does.
726    ///
727    /// So an immediate `TRUE` tells a caller that the *I/O* is done. It says
728    /// nothing about whether the *packet* is still coming, which is the only
729    /// thing this enum is about.
730    Pending,
731    /// The operation finished synchronously and no completion packet will
732    /// arrive -- the outcome a handle in `FILE_SKIP_COMPLETION_PORT_ON_SUCCESS`
733    /// mode reports on synchronous success. `bytes_transferred` is the count the
734    /// native call reported; the operation's storage is reclaimed inline and
735    /// returned through [`Submitted::Completed`].
736    ///
737    /// Reporting this when a packet *will* in fact arrive is a memory-safety
738    /// bug, not a bookkeeping one: `submit_with` treats it as license to drop
739    /// the operation from the port's outstanding set and reclaim its boxed
740    /// storage inline. The packet that was nevertheless queued then arrives
741    /// carrying a dangling `OVERLAPPED`, and claiming it frees that box a
742    /// second time -- while [`CompletionPort::run_down`] has already been told
743    /// there is nothing left to wait for. This is why every adapter that does
744    /// not enable skip-on-success mode reports [`Issued::Pending`] on immediate
745    /// native success.
746    Completed {
747        /// The number of bytes the synchronous call transferred.
748        bytes_transferred: u32,
749    },
750}
751
752/// The outcome of [`AssociatedEndpoint::submit`].
753#[derive(Debug)]
754pub enum Submitted<P> {
755    /// A completion will arrive; the storage was transferred to the kernel and
756    /// is recovered later with [`Completion::claim`]. The [`OperationId`]
757    /// identifies the in-flight operation for cancellation and matching.
758    Pending(OperationId),
759    /// The operation completed synchronously with no completion packet (the
760    /// skip-on-success case); the operation is returned already reclaimed, with
761    /// the bytes the native call transferred.
762    Completed {
763        /// The operation whose synchronous completion was observed inline.
764        operation: Operation<P>,
765        /// The number of bytes the synchronous call transferred.
766        bytes_transferred: u32,
767    },
768    /// Submission failed immediately with no completion; the operation is
769    /// returned so its storage can be reused or dropped.
770    Failed {
771        /// The operation whose submission failed.
772        operation: Operation<P>,
773        /// The immediate failure reported by the native call.
774        error: io::Error,
775    },
776}
777
778/// A completion packet dequeued from a [`CompletionPort`].
779///
780/// Dequeuing removes the operation from the port's outstanding set, so holding a
781/// completion never blocks [`CompletionPort::run_down`] or the port's `Drop`. The
782/// completion still owns the operation's storage until it is dropped or
783/// [`claim`](Self::claim)ed, and may outlive the port it came from.
784pub struct Completion {
785    key: usize,
786    bytes_transferred: u32,
787    overlapped: *mut OVERLAPPED,
788    error: Option<io::Error>,
789    /// The identity of the operation this packet completes, recovered from the
790    /// registry at dequeue time. `None` for a user packet, which has no
791    /// operation. Stored whole rather than as a bare generation, so nothing here
792    /// ever re-pairs an address with a generation.
793    id: Option<OperationId>,
794    claimed: Cell<bool>,
795}
796
797impl fmt::Debug for Completion {
798    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
799        f.debug_struct("Completion")
800            .field("key", &self.key)
801            .field("bytes_transferred", &self.bytes_transferred)
802            .field("overlapped", &self.overlapped)
803            .field("id", &self.id)
804            .field("error", &self.error)
805            .finish_non_exhaustive()
806    }
807}
808
809impl Drop for Completion {
810    fn drop(&mut self) {
811        // Claimed completions handed ownership to the caller; user packets carry
812        // a null overlapped and own nothing.
813        if self.claimed.get() || self.overlapped.is_null() {
814            return;
815        }
816        // The registry entry is already gone -- dequeue removed it -- so this
817        // only has to release the storage the completion still owns.
818        // SAFETY: the completion arrived, so the kernel is done with the storage;
819        // the operation's armed reclaim thunk frees the box exactly once.
820        unsafe { crate::operation::reclaim_from_overlapped(self.overlapped) };
821    }
822}
823
824impl Completion {
825    /// The completion key the packet was tagged with.
826    #[must_use]
827    pub fn key(&self) -> usize {
828        self.key
829    }
830
831    /// The number of bytes transferred by the operation.
832    #[must_use]
833    pub fn bytes_transferred(&self) -> u32 {
834        self.bytes_transferred
835    }
836
837    /// The `OVERLAPPED` pointer identifying the completed operation.
838    ///
839    /// For a user packet this is whatever value was passed to
840    /// [`CompletionPort::post`].
841    #[must_use]
842    pub fn overlapped_ptr(&self) -> *mut OVERLAPPED {
843        self.overlapped
844    }
845
846    /// The identity of the operation this packet completes.
847    ///
848    /// It matches the [`OperationId`] that [`AssociatedEndpoint::submit`]
849    /// returned for the operation, so a caller holding submission-time
850    /// identities can match a completion against them directly. Returns `None`
851    /// for a user packet from [`CompletionPort::post`], which completes no
852    /// operation.
853    #[must_use]
854    pub fn id(&self) -> Option<OperationId> {
855        self.id
856    }
857
858    /// The failure of the completed operation, if it did not succeed.
859    #[must_use]
860    pub fn error(&self) -> Option<&io::Error> {
861        self.error.as_ref()
862    }
863
864    /// Recover the owned operation whose completion this is.
865    ///
866    /// # Safety
867    ///
868    /// This completion must have been produced by submitting an `Operation<P>`
869    /// of this exact type through [`AssociatedEndpoint::submit`], and it must be
870    /// claimed exactly once.
871    pub unsafe fn claim<P>(&self) -> Operation<P> {
872        // Mark claimed so this completion's own drop will not also reclaim it.
873        // The registry entry was already removed at dequeue.
874        self.claimed.set(true);
875        // SAFETY: by this function's contract, the identity is a matching leaked
876        // Operation<P>, reclaimed exactly once here.
877        let mut operation = unsafe { Operation::<P>::from_overlapped(self.overlapped) };
878        operation.set_state(OperationState::Completed);
879        operation
880    }
881}
882
883#[cfg(test)]
884mod tests;