win_durable_ioring/windows_backend/wait.rs
1// Copyright (c) 2026 Mike Grier
2//! The Windows backend's notification, waited on (DI-D-55): the ring's completion event, as the
3//! [`CompletionEvent`] the backend hands out.
4
5use std::io;
6use std::os::windows::io::{AsHandle, BorrowedHandle, OwnedHandle};
7use std::time::Duration;
8
9use win_sync_sys::{WaitOutcome, wait_for_single_object};
10
11use durable_ioring::Waitable;
12
13#[cfg(test)]
14mod tests;
15
16/// The ring's completion event, duplicated for the one that waits on it: the notification the
17/// Windows backend hands out. A type of this crate and not a bare `OwnedHandle`, because
18/// [`Waitable`] is the portable crate's trait and a handle is the standard library's, so neither
19/// side could implement one for the other.
20///
21/// One is only made from the ring's own event, by this crate; no safe constructor takes an
22/// arbitrary handle, which could be a mutex the wait would acquire and never release:
23///
24/// ```compile_fail,E0624
25/// use std::os::windows::io::OwnedHandle;
26/// use win_durable_ioring::windows_backend::CompletionEvent;
27///
28/// fn any_handle(handle: OwnedHandle) -> CompletionEvent {
29/// CompletionEvent::new(handle)
30/// }
31/// ```
32#[derive(Debug)]
33pub struct CompletionEvent(OwnedHandle);
34
35impl CompletionEvent {
36 /// The event, taking the handle it is waited on through. Not public: the handle must be an
37 /// event the ring's own completion edge is, with `SYNCHRONIZE` access, which this crate's
38 /// callers make and an arbitrary `OwnedHandle` -- a mutex, say, which a wait would acquire and
39 /// never release -- does not promise.
40 #[must_use]
41 pub(crate) fn new(handle: OwnedHandle) -> Self {
42 Self(handle)
43 }
44
45 /// The handle, to wait on by other means.
46 #[must_use]
47 pub fn into_handle(self) -> OwnedHandle {
48 self.0
49 }
50}
51
52impl AsHandle for CompletionEvent {
53 fn as_handle(&self) -> BorrowedHandle<'_> {
54 self.0.as_handle()
55 }
56}
57
58/// The ring's completion event is auto-reset (the ring crate's D-21), so a wait that sees it
59/// signalled consumes the signal, and a raising before the first wait is seen by it: the ring crate
60/// signals the event once as it attaches it (its D-20).
61///
62/// **One waiter per ring** is the caller's to keep, as the ring crate states it: a second wait on
63/// the same event, or a duplicate of this handle waited on elsewhere, steals wakes.
64impl Waitable for CompletionEvent {
65 fn wait(&mut self, timeout: Option<Duration>) -> io::Result<bool> {
66 match wait_for_single_object(&self.0, timeout)? {
67 WaitOutcome::Signalled => Ok(true),
68 WaitOutcome::TimedOut => Ok(false),
69 // An event is never abandoned, which only a mutex can be, and only an event is made
70 // into one of these. Not reached by a test here: making a mutex needs `unsafe`, which
71 // this crate forbids. `win-sync-sys`' tests reach the ending in the function this
72 // calls. (A wait failing is reached, with a duplicate that has no `SYNCHRONIZE`
73 // access.) The ending is reported, not taken for a signal.
74 other => Err(io::Error::other(format!(
75 "the notification's wait ended as {other:?}"
76 ))),
77 }
78 }
79}