Skip to main content

win_sync_sys/
signal.rs

1// Copyright (c) 2026 Mike Grier
2//! Signalling one object and waiting on another in one step: `SignalObjectAndWait`.
3
4use std::io;
5use std::os::windows::io::{AsHandle, AsRawHandle};
6use std::time::Duration;
7
8use windows_sys::Win32::System::Threading::SignalObjectAndWait;
9
10use crate::event::Event;
11use crate::wait::{Alertable, WaitOutcome, milliseconds, outcome};
12
13/// An object [`signal_object_and_wait`] can signal: an event, a semaphore or a mutex.
14///
15/// What a type implementing this promises is that signalling its handle is something the call can
16/// do, so a handle that cannot signal is refused when it is passed, not when the call fails. The
17/// wait-only handle ([`WaitOnlyEvent`](crate::WaitOnlyEvent)) does not implement it, and the
18/// compiler says so:
19///
20/// ```compile_fail,E0277
21/// use win_sync_sys::{Alertable, Event, ResetMode, signal_object_and_wait};
22///
23/// let ping = Event::new(ResetMode::Auto, false)?;
24/// let waiter = ping.try_clone_wait_only()?;
25/// let pong = Event::new(ResetMode::Auto, true)?;
26/// // A wait-only handle cannot be signalled.
27/// signal_object_and_wait(&waiter, &pong, None, Alertable::No)?;
28/// # Ok::<(), std::io::Error>(())
29/// ```
30///
31/// It is not sealed: a handle that implements it and cannot in fact be signalled gets the error
32/// from the call, not undefined behaviour.
33pub trait Signalable: AsHandle {}
34
35impl Signalable for Event {}
36
37impl<T: Signalable + ?Sized> Signalable for &T {}
38
39/// Signal `signal`, then wait for `wait`, as one step with nothing able to happen between the two:
40/// `SignalObjectAndWait`.
41///
42/// It is one call to the kernel where a `set` followed by a wait is two, which is what Microsoft
43/// offers it for: a handshake between two threads without the context switch the second call can
44/// cost.
45///
46/// `signal` is set if it is an event, released once if it is a semaphore, and released if it is a
47/// mutex the calling thread owns. A wait that times out or is alerted has **still signalled**: the
48/// signal is not undone. The outcomes are those of a wait on one object, with `wait` changed as
49/// [`wait_for_single_object`](crate::wait_for_single_object) describes.
50///
51/// # Errors
52///
53/// [`io::ErrorKind::InvalidInput`] for a timeout the call cannot express, before anything is
54/// signalled. Otherwise the error from `GetLastError`, and then **nothing was waited on and the
55/// signal may or may not have been sent**: the call reports one failure for either half, so a
56/// caller that needs to know checks the object. Access denied for a `signal` handle without the
57/// right to modify it, or for a `wait` handle without `SYNCHRONIZE`; invalid handle for one that
58/// is not an object the call can signal.
59pub fn signal_object_and_wait(
60    signal: impl Signalable,
61    wait: impl AsHandle,
62    timeout: Option<Duration>,
63    alertable: Alertable,
64) -> io::Result<WaitOutcome> {
65    let milliseconds = milliseconds(timeout)?;
66    // SAFETY: both handles are borrowed, so open for the call, and the wait ends within it.
67    let code = unsafe {
68        SignalObjectAndWait(
69            signal.as_handle().as_raw_handle(),
70            wait.as_handle().as_raw_handle(),
71            milliseconds,
72            alertable.as_bool(),
73        )
74    };
75    outcome(code)
76}
77
78#[cfg(test)]
79mod tests;