pub struct AsyncRegistry<E: Send + Sync + 'static> { /* private fields */ }async only.Expand description
Asynchronous event registry.
Same lock-free, ArcSwap-backed read path as crate::SyncRegistry, but
handlers return a future of (). Two dispatch modes are available:
AsyncRegistry::notify— drives every handler concurrently via a crate-localJoinAllcombinator. Faster total wall-clock for handlers that perform real.awaitwork, since they make progress in parallel under the runtime.AsyncRegistry::notify_sequential— awaits each handler in order. Use when downstream ordering or back-pressure between handlers matters.
Each handler future is wrapped in a crate-local CatchUnwind adapter
so a panic during poll is isolated from sibling handlers and from the
caller awaiting notify.
§Type parameter
E is the event type. Handlers receive &E but return a 'static
future, so they must clone whatever they need from &E before
async move { ... }.
§Examples
use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
use registry_io::r#async::AsyncRegistry;
let registry: AsyncRegistry<u64> = AsyncRegistry::new();
let total = Arc::new(AtomicU64::new(0));
let sink = Arc::clone(&total);
let _ = registry.register(move |value| {
let sink = Arc::clone(&sink);
let v = *value;
async move {
sink.fetch_add(v, Ordering::Relaxed);
}
});
registry.notify(&7).await;
assert_eq!(total.load(Ordering::Relaxed), 7);Implementations§
Source§impl<E: Send + Sync + 'static> AsyncRegistry<E>
impl<E: Send + Sync + 'static> AsyncRegistry<E>
Sourcepub fn new() -> Self
pub fn new() -> Self
Create a new, empty async registry.
§Examples
use registry_io::r#async::AsyncRegistry;
let registry: AsyncRegistry<u32> = AsyncRegistry::new();
assert!(registry.is_empty());Sourcepub fn with_capacity(capacity: usize) -> Self
pub fn with_capacity(capacity: usize) -> Self
Create a new, empty async registry with pre-allocated handler capacity.
§Examples
use registry_io::r#async::AsyncRegistry;
let registry: AsyncRegistry<u64> = AsyncRegistry::with_capacity(16);
assert!(registry.is_empty());Sourcepub fn register<F, Fut>(&self, handler: F) -> HandlerId
pub fn register<F, Fut>(&self, handler: F) -> HandlerId
Register an async handler at the default priority (0).
The handler is a closure Fn(&E) -> impl Future<Output = ()>. The
returned future must be 'static: clone any data from &E you need
before the inner async move { ... }.
§Examples
use registry_io::r#async::AsyncRegistry;
let registry: AsyncRegistry<String> = AsyncRegistry::new();
let _ = registry.register(|event| {
let owned = event.clone();
async move {
// Pretend we awaited something useful here.
let _ = owned.len();
}
});Sourcepub fn register_with_priority<F, Fut>(
&self,
priority: i32,
handler: F,
) -> HandlerId
pub fn register_with_priority<F, Fut>( &self, priority: i32, handler: F, ) -> HandlerId
Register an async handler with an explicit priority.
Dispatch order at notify time follows the same rule as
crate::SyncRegistry::register_with_priority: higher priority
fires first, ties broken in registration order. In concurrent
dispatch (AsyncRegistry::notify) priority controls the order in
which futures are spawned into the join, not the order they
complete in.
§Examples
use registry_io::r#async::AsyncRegistry;
let registry: AsyncRegistry<()> = AsyncRegistry::new();
let _ = registry.register_with_priority(100, |_| async move {});
let _ = registry.register(|_| async move {});
let _ = registry.register_with_priority(-10, |_| async move {});
assert_eq!(registry.handler_count(), 3);Sourcepub fn register_guard<F, Fut>(
self: &Arc<Self>,
handler: F,
) -> AsyncHandlerGuard<E>
pub fn register_guard<F, Fut>( self: &Arc<Self>, handler: F, ) -> AsyncHandlerGuard<E>
Register an async handler and return a RAII
AsyncHandlerGuard that auto-unregisters when dropped.
Requires the registry to be wrapped in Arc so the guard can hold
a std::sync::Weak reference.
§Examples
use std::sync::Arc;
use registry_io::r#async::AsyncRegistry;
let registry = Arc::new(AsyncRegistry::<u32>::new());
{
let _guard = registry.register_guard(|_| async move {});
assert_eq!(registry.handler_count(), 1);
}
assert_eq!(registry.handler_count(), 0);Sourcepub fn register_guard_with_priority<F, Fut>(
self: &Arc<Self>,
priority: i32,
handler: F,
) -> AsyncHandlerGuard<E>
pub fn register_guard_with_priority<F, Fut>( self: &Arc<Self>, priority: i32, handler: F, ) -> AsyncHandlerGuard<E>
Like AsyncRegistry::register_guard but with an explicit
priority value. Higher priorities fire first; ties broken in
registration order. See
AsyncRegistry::register_with_priority.
§Examples
use std::sync::Arc;
use registry_io::r#async::AsyncRegistry;
let registry = Arc::new(AsyncRegistry::<&'static str>::new());
let _hi = registry.register_guard_with_priority(100, |evt| {
let s = *evt;
async move { let _ = s; }
});
let _lo = registry.register_guard_with_priority(-5, |evt| {
let s = *evt;
async move { let _ = s; }
});
assert_eq!(registry.handler_count(), 2);Sourcepub fn unregister(&self, id: HandlerId) -> bool
pub fn unregister(&self, id: HandlerId) -> bool
Unregister an async handler by id. Returns true if a handler was
found and removed.
§Examples
use registry_io::r#async::AsyncRegistry;
let registry: AsyncRegistry<()> = AsyncRegistry::new();
let id = registry.register(|_| async move {});
assert!(registry.unregister(id));
assert!(!registry.unregister(id));Sourcepub fn clear(&self)
pub fn clear(&self)
Remove every registered handler.
In-flight notify* calls that already loaded the snapshot still run
every handler in their snapshot to completion.
§Examples
use registry_io::r#async::AsyncRegistry;
let registry: AsyncRegistry<()> = AsyncRegistry::new();
for _ in 0..5 {
let _ = registry.register(|_| async move {});
}
registry.clear();
assert_eq!(registry.handler_count(), 0);Sourcepub fn handler_count(&self) -> usize
pub fn handler_count(&self) -> usize
Current handler count. O(1) atomic snapshot.
§Examples
use registry_io::r#async::AsyncRegistry;
let registry: AsyncRegistry<()> = AsyncRegistry::new();
assert_eq!(registry.handler_count(), 0);
let _ = registry.register(|_| async move {});
assert_eq!(registry.handler_count(), 1);Sourcepub fn is_empty(&self) -> bool
pub fn is_empty(&self) -> bool
true if no handlers are registered.
§Examples
use registry_io::r#async::AsyncRegistry;
let registry: AsyncRegistry<()> = AsyncRegistry::new();
assert!(registry.is_empty());
let _ = registry.register(|_| async move {});
assert!(!registry.is_empty());Sourcepub fn contains(&self, id: HandlerId) -> bool
pub fn contains(&self, id: HandlerId) -> bool
true if a handler with id is currently registered.
§Examples
use registry_io::r#async::AsyncRegistry;
let registry: AsyncRegistry<()> = AsyncRegistry::new();
let id = registry.register(|_| async move {});
assert!(registry.contains(id));
assert!(registry.unregister(id));
assert!(!registry.contains(id));Sourcepub fn on_panic<F>(&self, callback: F)
pub fn on_panic<F>(&self, callback: F)
Install a panic callback fired once per panicking handler future
during notify*. Replaces any previously installed callback.
Second-order panics inside the callback itself are caught and
discarded.
§Examples
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use registry_io::r#async::AsyncRegistry;
let registry: AsyncRegistry<()> = AsyncRegistry::new();
let count = Arc::new(AtomicUsize::new(0));
let sink = Arc::clone(&count);
registry.on_panic(move |_| {
let _ = sink.fetch_add(1, Ordering::Relaxed);
});
let _ = registry.register(|_| async move { panic!("oops") });
registry.notify(&()).await;
assert_eq!(count.load(Ordering::Relaxed), 1);Sourcepub fn clear_panic_callback(&self)
pub fn clear_panic_callback(&self)
Remove any previously installed panic callback. Subsequent
handler panics during notify* become silent.
§Examples
use registry_io::r#async::AsyncRegistry;
let registry: AsyncRegistry<()> = AsyncRegistry::new();
registry.on_panic(|_| {});
registry.clear_panic_callback();Sourcepub async fn notify(&self, event: &E)
pub async fn notify(&self, event: &E)
Dispatch event to every registered handler concurrently.
Builds one future per handler, then awaits them all via the
crate-local JoinAll combinator. Each handler future is wrapped in
a crate-local CatchUnwind adapter so a panic in one handler does
not poison the join — its sibling handlers continue.
§Examples
use std::sync::Arc;
use std::sync::atomic::{AtomicU32, Ordering};
use registry_io::r#async::AsyncRegistry;
let registry: AsyncRegistry<u32> = AsyncRegistry::new();
let total = Arc::new(AtomicU32::new(0));
for _ in 0..4 {
let sink = Arc::clone(&total);
let _ = registry.register(move |value| {
let sink = Arc::clone(&sink);
let v = *value;
async move {
sink.fetch_add(v, Ordering::Relaxed);
}
});
}
registry.notify(&10).await;
assert_eq!(total.load(Ordering::Relaxed), 40);Sourcepub async fn notify_sequential(&self, event: &E)
pub async fn notify_sequential(&self, event: &E)
Dispatch event to every registered handler sequentially, in
priority order.
Each handler’s future is awaited to completion before the next one starts. Use this when handlers must observe a strict happens-before relationship with one another.
§Examples
use std::sync::{Arc, Mutex};
use registry_io::r#async::AsyncRegistry;
let registry: AsyncRegistry<()> = AsyncRegistry::new();
let log: Arc<Mutex<Vec<&'static str>>> = Arc::new(Mutex::new(Vec::new()));
let l = Arc::clone(&log);
let _ = registry.register_with_priority(10, move |_| {
let l = Arc::clone(&l);
async move { l.lock().unwrap().push("first"); }
});
let l = Arc::clone(&log);
let _ = registry.register(move |_| {
let l = Arc::clone(&l);
async move { l.lock().unwrap().push("second"); }
});
registry.notify_sequential(&()).await;
assert_eq!(log.lock().unwrap().as_slice(), &["first", "second"]);