rsevents
This crate contains a cross-platform implementation of auto and manual reset events similar to those found in Microsoft Windows, implemented on top of the core parking lot crate as a cross-platform futex abstraction.
About rsevents
An event is best compared to an awaitable boolean, and can have either of two states: set and unset.
Callers may directly wait on the event itself, foregoing the need for an associated condition variable and mutex.
Depending on the specific type of the event, an event can also be thought of as an efficient implementation of a multi-producer, multi-consumer Channel<()>
(with a manual-reset event being a broadcast version of the same channel).
As with WIN32 events, rsevents come in two flavors, AutoResetEvent
and ManualResetEvent
, which differ in their behavior when it comes to setting (aka signalling) an event.
An AutoResetEvent
, once signalled, permits exactly one (and only one) past or future caller waiting on the same event to unblock, whereas a ManualResetEvent
lacks such fine-grained control and is either signalled and unblocked for all past/future waiters or none (until its state is manually/explicitly changed). Their usages differ tremendously so make sure you are using the correct event type for your needs!
The types in the core rsevents
crate are often useful for implementing other synchronization primitives. The rsevents-extra
crate contains some synchronization types that you might find useful, including
- A countdown event, useful for efficiently waiting until n outstanding tasks distributed to various threads have completed.
- A semaphore, for limiting concurrent access to a section or resource to a maximum of n threads.
Example
The following code is an example wherein the main thread dispatches work to a pool of spawned threads, which is then claimed by the first available thread.
It demonstrates some of the unique properties of auto-reset events (thread-at-a-time signalling, memory coherence between threads calling event.set()
and threads calling event.wait()
, efficient blocking while waiting for work, and waiting with a time limit).
The unergonomic usage of raw pointers (for the SHARED
thread message variable) is merely to illustrate the safety guarantees of auto-reset events - you are free to wrap your shared state in an RwLock
or a simple wrapper type asserting Sync
/Send
exposing a nicer API if convenient.
use Duration;
use ;
// Events are cheap: each one is only a single byte!
static TASK_READY: AutoResetEvent = new;
static DISPATCHED: AutoResetEvent = new;
Types
Structs ManualResetEvent
and AutoResetEvent
both implement the Awaitable
trait, which exposes an API that permits waiting indefinitely, waiting for zero time, and waiting for a fixed time limit (Duration
) for an event to be triggered.
Dependent crates building their own synchronization primitives on rsevents
types should similarly implement Awaitable
to expose a unified interface for awaiting on objects (and should re-export the Awaitable
trait (or all of rsevents
) so that end users do not have to separately add a dependency on rsevents
to their Cargo.toml
).
See the documentation for more info.
When to use
Generally speaking, a mutex or condition variable should always be preferred when it comes to protecting a critical section and ensuring exclusive access due to their well-understood synchronization paradigms and wide support. However, there are other times when a synchronization primitve not coupled to an explicit critical section or protected data is required, in which case it similarly does not make sense to use a mutex and a critical section when a single alternative synchronization primitive is what is actually required.
Events are somewhat like a hypothetical multi-producer, multi-consumer RwLock
that doesn't own the data it protects.
Auto-reset events (like AutoResetEvent
) are great for signalling and often used to easily build other synchronization primitives themselves without needing to use futexes or pay the price of one or more mutexes.
As such, events afford more freedom than the standard library synchronization primitives like Mutex
, RwLock
, or CondVar
, but are also tools you have to be much more careful while using - with some exceptions.
Manual reset events (like ManualResetEvent
) are actually incredibly easy and flexible to use for broadcasting a signal to all threads (affecting both already-waiting and not-yet-waiting threads) and are incredibly convenient for waiting indefinitely or for a fixed length of time on some non-thread-safe condition (such as a global abort indicator).
About
rsevents is written and maintained by Mahmoud Al-Qudsi <mqudsi@neosmart.net> of NeoSmart Technologies <https://neosmart.net/> and released to the general public under the terms of the MIT public license.