feat(event): add AutoResetEvent - #310
Conversation
There was a problem hiding this comment.
🔵 Needs a closer look
The concurrency-sensitive synchronization changes warrant final human review, with documentation reflow nits remaining.
Pull request overview
Adds event::AutoResetEvent, a coalescing, cancellation-safe notification primitive for one waiter.
Changes:
- Implements FIFO signaling, cancellation handoff, owned waits, and synchronization semantics.
- Adds behavioral, concurrency, trait, and panic-safety tests.
- Updates documentation, API maps, changelog, and adds a worker example.
File summaries
| File | Summary |
|---|---|
tests-integration/tests/traits_test.rs |
Adds trait coverage. |
tests-integration/tests/auto_reset_event_test.rs |
Tests event behavior and concurrency. |
README.md |
Updates the API map. |
examples/src/coalesced_worker.rs |
Adds a coalesced worker example. |
examples/Cargo.toml |
Registers the example and feature. |
CHANGELOG.md |
Records the new feature. |
asyncband/src/lib.rs |
Updates crate API documentation. |
asyncband/src/event/mod.rs |
Documents and exports the event. |
asyncband/src/event/auto_reset.rs |
Implements AutoResetEvent. |
Review details
Suppressed comments (14)
asyncband/src/event/auto_reset.rs:40
- This newly added Markdown paragraph is wrapped across source lines, but the repository documentation convention in
AGENTS.md:42-44requires each prose paragraph to occupy one source line. Please reflow it before merging.
/// Waiting consumes a signal without returning it on completion. Unlike a
/// [`ManualResetEvent`](super::ManualResetEvent), this event does not release all observers of a
/// condition. Unlike a semaphore, it does not count unused signals or return a permit guard.
asyncband/src/event/auto_reset.rs:48
- This newly added Markdown paragraph is wrapped across source lines, but the repository documentation convention in
AGENTS.md:42-44requires each prose paragraph to occupy one source line. Please reflow it before merging.
/// Memory operations sequenced before a `set` are visible after a wait or
/// [`try_wait`](Self::try_wait) consumes its signal. This includes sets coalesced into a stored
/// signal and signals passed on after cancellation. The event carries no application state: when it
/// is used to notify changes to an external predicate, callers must synchronize access to that
/// predicate separately.
asyncband/src/event/auto_reset.rs:36
- This newly added Markdown paragraph is wrapped across source lines, but the repository documentation convention in
AGENTS.md:42-44requires each prose paragraph to occupy one source line. Please reflow it before merging.
/// Each [`set`](Self::set) assigns a signal to the oldest registered wait, or stores one signal
/// if no wait is queued. Repeated sets coalesce only while an unassigned signal is stored. A
/// signal assigned to a wait belongs to that wait until it completes or is cancelled; subsequent
/// sets can release other waits even before previously selected waits are polled again.
asyncband/src/event/auto_reset.rs:89
- This newly added Markdown paragraph is wrapped across source lines, but the repository documentation convention in
AGENTS.md:42-44requires each prose paragraph to occupy one source line. Please reflow it before merging.
/// A stored signal is available to a future wait. Further sets coalesce while it remains
/// unassigned. Creating a wait future does not register it; registration happens when it is
/// first polled without a stored signal.
asyncband/src/event/auto_reset.rs:94
- This newly added Markdown paragraph is wrapped across source lines, but the repository documentation convention in
AGENTS.md:42-44requires each prose paragraph to occupy one source line. Please reflow it before merging.
/// Panics if waking the selected task panics. Its signal remains assigned and can still be
/// consumed by polling that wait or passed on by dropping it.
asyncband/src/event/auto_reset.rs:105
- This newly added Markdown paragraph is wrapped across source lines, but the repository documentation convention in
AGENTS.md:42-44requires each prose paragraph to occupy one source line. Please reflow it before merging.
/// This never takes a signal assigned to another wait or bypasses a queued wait. A `false`
/// result is only a snapshot; use [`wait`](Self::wait) to wait for a future signal.
asyncband/src/event/auto_reset.rs:122
- This newly added Markdown paragraph is wrapped across source lines, but the repository documentation convention in
AGENTS.md:42-44requires each prose paragraph to occupy one source line. Please reflow it before merging.
/// The first poll consumes a stored signal immediately, or joins the FIFO waiter queue.
/// Merely creating this future neither reserves a signal nor establishes a queue position.
/// Signals assigned to other waits cannot be consumed by this wait.
asyncband/src/event/auto_reset.rs:129
- This newly added Markdown paragraph is wrapped across source lines, but the repository documentation convention in
AGENTS.md:42-44requires each prose paragraph to occupy one source line. Please reflow it before merging.
/// Dropping this future before it returns `Ready` removes its registration. If a signal was
/// assigned to it, that signal is passed to the oldest queued wait or stored for a future
/// wait, coalescing with any signal already stored. Retrying a cancelled wait joins the back
/// of the queue. Dropping a completed wait does not return its consumed signal.
asyncband/src/event/auto_reset.rs:141
- This newly added Markdown paragraph is wrapped across source lines, but the repository documentation convention in
AGENTS.md:42-44requires each prose paragraph to occupy one source line. Please reflow it before merging.
/// The future owns the [`Arc`], making it suitable for spawned tasks. Its registration,
/// fairness, and cancellation semantics match [`wait`](Self::wait).
asyncband/src/event/mod.rs:22
- These newly added Markdown paragraphs are wrapped across source lines, but the repository documentation convention in
AGENTS.md:42-44requires each prose paragraph to occupy one source line. Please reflow this paragraph before merging.
//! A [`ManualResetEvent`] releases all registered waits and remains ready until explicitly reset.
//! An [`AutoResetEvent`] releases one wait per assigned signal and consumes that signal when the
//! wait completes. With no queued waits, it retains at most one signal, coalescing further sets.
asyncband/src/event/mod.rs:26
- This newly added Markdown paragraph is wrapped across source lines, but the repository documentation convention in
AGENTS.md:42-44requires each prose paragraph to occupy one source line. Please reflow it before merging.
//! Both types retain state, unlike a condition variable's unbuffered notifications. Use a
//! semaphore when unused permits must accumulate, or a watch channel when each receiver needs to
//! observe state changes independently.
asyncband/src/event/mod.rs:46
- This newly added Markdown paragraph is wrapped across source lines, but the repository documentation convention in
AGENTS.md:42-44requires each prose paragraph to occupy one source line. Please reflow it before merging.
//! An auto-reset event is useful for a single worker that rechecks external state after a signal.
//! Publish the state before calling `set`, and check the predicate in a loop. A signal arriving
//! between the predicate check and the first poll is retained, so the worker does not miss it.
//! A leftover signal can cause an extra predicate check without implying new work.
asyncband/src/event/mod.rs:50
- This newly added Markdown paragraph is wrapped across source lines, but the repository documentation convention in
AGENTS.md:42-44requires each prose paragraph to occupy one source line. Please reflow it before merging.
//! Multiple waits on an auto-reset event compete for signals. This does not broadcast a predicate
//! change to every observer, and the simple check-then-wait loop is not a general multi-consumer
//! queue protocol: several changes can coalesce before those consumers register their waits.
examples/src/coalesced_worker.rs:21
- This newly added Markdown paragraph is wrapped across source lines, but the repository documentation convention in
AGENTS.md:42-44requires each prose paragraph to occupy one source line. Please reflow it before merging.
//! One worker rebuilds a snapshot of the latest requested revision. Intermediate revisions may
//! coalesce: this is not a queue of jobs that must each run, or a broadcast to multiple observers.
- Files reviewed: 9/9 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Summary
Add
event::AutoResetEventfor coalescing notifications to one waiter under the existingeventfeature. Both event types exposeis_setfor non-consuming state inspection andtry_waitfor an immediate wait: ManualResetEvent stays set on success, while AutoResetEvent consumes a stored signal.Keep the types in separate modules with matching method order and documentation. Examples demonstrate migrations from Tokio Notify for a cache worker and index-revision readers, plus a worker that coalesces requests across multiple rebuilds. Downstream draft scopedb/cache2#52 replaces
watch<()>drain notifications and pins the event implementation by Git revision for CI.Design Notes
Each auto-reset
setassigns a signal to a registered wait or retains one unassigned signal. Assigned signals belong to their waits until completion or cancellation; cancellation transfers or restores the signal.resetclears only an unassigned signal,is_setobserves only that stored state, andtry_waitcannot take another wait's assigned signal. Reuse the internal mutex and waiter list, keeping wakes and waker destruction outside the lock.Validation:
cargo x test,cargo x lint, and thecoalesced_worker,notify_vs_event, andonce_cell_vs_lazy_cellexamples passed. Event tests cover non-consuming inspection, immediate waits, reset, cancellation handoff, waker lifecycle, wake panics, and publication races.Closes #292.