Skip to main content

AsyncRegistry

Struct AsyncRegistry 

Source
pub struct AsyncRegistry<E: Send + Sync + 'static> { /* private fields */ }
Available on crate feature 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-local JoinAll combinator. Faster total wall-clock for handlers that perform real .await work, 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>

Source

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());
Source

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());
Source

pub fn register<F, Fut>(&self, handler: F) -> HandlerId
where F: Fn(&E) -> Fut + Send + Sync + 'static, Fut: Future<Output = ()> + Send + 'static,

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();
    }
});
Source

pub fn register_with_priority<F, Fut>( &self, priority: i32, handler: F, ) -> HandlerId
where F: Fn(&E) -> Fut + Send + Sync + 'static, Fut: Future<Output = ()> + Send + 'static,

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);
Source

pub fn register_guard<F, Fut>( self: &Arc<Self>, handler: F, ) -> AsyncHandlerGuard<E>
where F: Fn(&E) -> Fut + Send + Sync + 'static, Fut: Future<Output = ()> + Send + 'static,

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);
Source

pub fn register_guard_with_priority<F, Fut>( self: &Arc<Self>, priority: i32, handler: F, ) -> AsyncHandlerGuard<E>
where F: Fn(&E) -> Fut + Send + Sync + 'static, Fut: Future<Output = ()> + Send + 'static,

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);
Source

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));
Source

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);
Source

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);
Source

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());
Source

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));
Source

pub fn on_panic<F>(&self, callback: F)
where F: Fn(&PanicInfo<'_>) + Send + Sync + 'static,

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);
Source

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();
Source

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);
Source

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"]);

Trait Implementations§

Source§

impl<E: Send + Sync + 'static> Debug for AsyncRegistry<E>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<E: Send + Sync + 'static> Default for AsyncRegistry<E>

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl<E> !Freeze for AsyncRegistry<E>

§

impl<E> !RefUnwindSafe for AsyncRegistry<E>

§

impl<E> Send for AsyncRegistry<E>

§

impl<E> Sync for AsyncRegistry<E>

§

impl<E> Unpin for AsyncRegistry<E>

§

impl<E> UnsafeUnpin for AsyncRegistry<E>

§

impl<E> !UnwindSafe for AsyncRegistry<E>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.