Expand description
§win-sync-sys
Memory-safe Rust over the Win32 synchronization API: the objects Windows provides for one thread
to signal another, and for a thread to wait. Thin over Win32 and adding no policy – the -sys
suffix is that promise, as the workspace’s naming decision defines it.
§What is here
Event– an owned event object, created withCreateEventW, that can be set and reset withoutunsafe. A clone (try_clone) is a second handle to the same object, so one part of a program can keep an event to signal while another hands it to something that waits on it.
use std::os::windows::io::OwnedHandle;
use win_sync_sys::{Event, ResetMode};
let ready = Event::new(ResetMode::Auto, false)?;
// A second handle to the same event, for another thread to signal through.
let signaller = ready.try_clone()?;
std::thread::spawn(move || signaller.set())
.join()
.expect("the signalling thread")?;
// `ready` is now signalled. Anything that waits on a handle can take it from
// here; an `Event` converts into an `OwnedHandle` and lends one as well.
let handle: OwnedHandle = ready.into();wait_for_single_object–WaitForSingleObjectover any handle, anEventincluded, with a timeout ofNonefor no limit or aDuration. It reports how the wait ended as aWaitOutcome, so a mutex abandoned by its owner is not mistaken for a signal. A satisfied wait changes the object as the Win32 call does: an auto-reset event is consumed.
use std::time::Duration;
use win_sync_sys::{Event, ResetMode, WaitOutcome, wait_for_single_object};
let event = Event::new(ResetMode::Auto, false)?;
assert_eq!(
wait_for_single_object(&event, Some(Duration::ZERO))?,
WaitOutcome::TimedOut,
);
event.set()?;
assert_eq!(wait_for_single_object(&event, None)?, WaitOutcome::Signalled);
// The wait took the signal.
assert_eq!(
wait_for_single_object(&event, Some(Duration::ZERO))?,
WaitOutcome::TimedOut,
);windows-threadpool-sys accepts an Event as a thread-pool wait target without unsafe, because
an Event is never a mutex – the one kind of object the pool cannot wait on.
wait_for_any,wait_for_all,signal_object_and_wait,wait_for_single_object_exandqueue_user_apc– the rest of the wait functions:WaitForMultipleObjectsExfor up toMAXIMUM_OBJECTS(64) objects, either until any one is signalled, reporting which, or until all are at once, which takes from them atomically;SignalObjectAndWait, which can signal only aSignalableobject, so aWaitOnlyEventis refused by the compiler; and the alertable form of each, anAlertable::Yeswait that an APC ends withAlerted.queue_user_apctakes a Rust closure and runs it on the target thread in its next alertable wait.
use std::time::Duration;
use win_sync_sys::{
Alertable, Event, ResetMode, WaitAnyOutcome, WaitOutcome, queue_user_apc_to_current_thread,
signal_object_and_wait, wait_for_all, wait_for_any,
};
let events = [
Event::new(ResetMode::Auto, false)?,
Event::new(ResetMode::Auto, false)?,
];
let poll = Some(Duration::ZERO);
events[1].set()?;
assert_eq!(wait_for_any(&events, poll, Alertable::No)?, WaitAnyOutcome::Signalled(1));
// All, atomically: one signalled is not enough, and nothing is taken until both are.
events[0].set()?;
assert_eq!(wait_for_all(&events, poll, Alertable::No)?, WaitOutcome::TimedOut);
events[1].set()?;
assert_eq!(wait_for_all(&events, poll, Alertable::No)?, WaitOutcome::Signalled);
// An APC ends an alertable wait, and runs first.
queue_user_apc_to_current_thread(|| println!("ran in the wait"))?;
let outcome = wait_for_any(&events, poll, Alertable::Yes)?;
assert_eq!(outcome, WaitAnyOutcome::Alerted);
// Signal one object and wait on another in one call.
let (ping, pong) = (Event::new(ResetMode::Auto, false)?, Event::new(ResetMode::Auto, true)?);
signal_object_and_wait(&ping, &pong, poll, Alertable::No)?;WaitOnlyEventandduplicate_handle_with_access– a handle to the same object that can do less.Event::try_clone_wait_onlygives a handle to the event that can be waited on and can neither set nor reset it: the kernel refuses aSetEventorResetEventon it.duplicate_handle_with_accessisDuplicateHandlewith the access mask chosen, over any handle and returning anOwnedHandle. Windows does not confine a duplicate to the source’s access, so a holder that sets out to can duplicate a wait-only handle into a wider one; the narrowing guards against a handle used for what it was not handed out for, and is not a boundary within the process.
use std::time::Duration;
use win_sync_sys::{Access, Event, ResetMode, WaitOutcome, duplicate_handle_with_access, wait_for_single_object};
let event = Event::new(ResetMode::Manual, false)?;
// Hand this to something that should wait for the event and never signal it.
let waiter = event.try_clone_wait_only()?;
assert_eq!(wait_for_single_object(&waiter, Some(Duration::ZERO))?, WaitOutcome::TimedOut);
event.set()?;
assert_eq!(wait_for_single_object(&waiter, None)?, WaitOutcome::Signalled);
// The same for a handle that is not an `Event`, such as one received from elsewhere.
let adopted = duplicate_handle_with_access(&event, Access::SYNCHRONIZE)?;
assert_eq!(wait_for_single_object(&adopted, None)?, WaitOutcome::Signalled);§What is not here
PulseEvent, deliberately. Its own documentation calls it unreliable and says it should not be used: a thread waiting on the event can be briefly taken out of its wait by a kernel-mode APC, and a pulse that lands in that moment releases nothing, because a pulse releases only the threads waiting at the instant it happens. The waiter misses a signal it was waiting for, and nothing reports that it did. Microsoft’s replacement is a condition variable.- Named events and
OpenEvent. Every event here is unnamed. Whether named objects belong in this crate is not yet decided. - The other kernel objects – semaphores, mutexes and waitable timers – are planned in this
crate’s
CHECKLIST.md. - The in-process primitives – SRW locks, condition variables, critical sections, one-time
initialization, barriers and
WaitOnAddress– are parked rather than rejected.
The reasoning behind each is in this crate’s DESIGN-NOTES.md.
Structs§
- Access
- An access mask for a duplicated handle: what the holder of the handle may do with the object.
- Event
- An owned event object.
- Wait
Only Event - A handle to an event that can wait on it and cannot set or reset it.
Enums§
- Alertable
- Whether a wait can be ended early by an APC queued to the waiting thread.
- Reset
Mode - How an event returns to unsignalled after it is set.
- Wait
AnyOutcome - How a
wait_for_anythat did not fail ended. - Wait
Outcome - How a wait that did not fail ended.
Constants§
- MAXIMUM_
OBJECTS - The most objects one wait takes:
MAXIMUM_WAIT_OBJECTS.
Traits§
- Signalable
- An object
signal_object_and_waitcan signal: an event, a semaphore or a mutex.
Functions§
- duplicate_
handle_ with_ access - A second handle to the object
handlerefers to, in this process, carrying exactlyaccess. - queue_
user_ apc - Queue
routineto run onthreadthe next time it waits alertably:QueueUserAPC. - queue_
user_ apc_ to_ current_ thread queue_user_apcto the calling thread.- signal_
object_ and_ wait - Signal
signal, then wait forwait, as one step with nothing able to happen between the two:SignalObjectAndWait. - wait_
for_ all - Wait until all of
objectsare signalled at once, for up totimeout:Noneis no limit andDuration::ZEROonly asks.WaitForMultipleObjectsExwithbWaitAlltrue. - wait_
for_ any - Wait until any one of
objectsis signalled, for up totimeout:Noneis no limit andDuration::ZEROonly asks.WaitForMultipleObjectsExwithbWaitAllfalse. - wait_
for_ single_ object - Wait for
objectto be signalled, for up totimeout:Noneis no limit, andDuration::ZEROonly asks whether it is signalled now. - wait_
for_ single_ object_ ex wait_for_single_object, which an APC can end:WaitForSingleObjectEx.