Highlights
- Cross-platform — Linux, macOS, Windows.
- Lock-free reads via
ArcSwapsnapshots. Many threads cannotifyconcurrently with no coordination. - Zero allocation on the no-panic sync notify path.
- Panic isolation — a panicking handler does not stop siblings nor
propagate to the caller. Optional
on_paniccallback for observability. Works for both sync handlers and async futures. - Priority ordering —
register_with_priority(i32, ...). Higher fires first; ties broken in registration order. SyncRegistry<E>— generic over the event type. Handlers receive&E.AsyncRegistry<E>(feature:async) — same lock-free storage, futures-returning handlers, concurrent or sequential dispatch.- RAII guards —
register_guardreturns aHandlerGuard/AsyncHandlerGuardthat unregisters on drop. Send + Sync— share registries freely across threads.
Status
Active development — release-candidate gate. v0.9.0 is the final
pre-1.0 documentation pass: a v1.0 stability contract
(STABILITY-1.0.md), internal architecture
walk-through (ARCHITECTURE.md), and
per-platform behavior notes (PLATFORM-NOTES.md)
all land in this release. The next release will be 1.0.0-rc.1.
v0.8.0 added the integration-pattern reference (PATTERNS.md) with four canonical patterns plus runnable examples for each.
v0.7.0 was the hardening milestone:
property-based invariant tests via proptest, an Arc::strong_count
leak canary over 10 000 register/unregister cycles, a cargo-fuzz
target scaffold, and a published threat model. The
public API contains zero unsafe code.
v0.6.0 shipped the performance verification: every target in the
Performance Contract is met with significant headroom — sync notify
at ~10 ns / 1 handler / 1 thread, ~25 ns / 4 handlers / 16
threads contended, and dhat-verified zero heap allocations on
the hot path. Async concurrent dispatch is ~180 ns / 1 handler. See
docs/PERFORMANCE.md for the full measurements.
v0.5.0 added AsyncRegistry with concurrent + sequential dispatch,
AsyncHandlerGuard, and panic isolation across .await, behind the
async feature. The synchronous side (v0.4.0) — SyncRegistry, priority
ordering, RAII guards, panic isolation — remains the default. See
.dev/ROADMAP.md for the path to 1.0.
Public API is not yet frozen — minor releases may break it. Pin specific versions; expect changes pre-1.0.
When to use it
Use registry-io when you have:
- Multiple components that need notification when something happens (
configreload,file change,transaction commit,metric event, etc.). - Fast, in-process handlers measured in microseconds or less.
- A need to register and unregister handlers dynamically.
- Performance-critical paths where channel allocation would dominate.
Do not use registry-io when you have:
- Cross-process or cross-network delivery needs — use NATS, Redis pub/sub, or similar message brokers.
- Heavy handler workloads requiring backpressure — use
tokio::sync::broadcastor channels. - Event sourcing or durability requirements — use a real event log.
Quick start
[]
= "0.9"
use Arc;
use ;
use SyncRegistry;
let registry: = new;
let total = new;
let sink = clone;
let id = registry.register;
registry.notify;
registry.notify;
assert_eq!;
assert!;
Priority ordering
use ;
use SyncRegistry;
let registry: = new;
let order = new;
let o = clone;
let _ = registry.register_with_priority;
let o = clone;
let _ = registry.register;
let o = clone;
let _ = registry.register_with_priority;
registry.notify;
assert_eq!;
RAII guards
use Arc;
use SyncRegistry;
let registry = new;
// guard drops here -> handler is unregistered
assert!;
Panic isolation
use SyncRegistry;
let registry: = new;
registry.on_panic;
let _ = registry.register;
let _ = registry.register;
registry.notify; // returns cleanly; both effects observed
Async handlers (feature: async)
[]
= { = "0.9", = ["async"] }
= { = "1", = ["rt-multi-thread", "macros"] }
use Arc;
use ;
use r#AsyncRegistry;
async
Same lock-free read path as SyncRegistry. Panics in handler futures are
caught via an internal CatchUnwind adapter and surfaced through
on_panic, just like sync handlers.
See examples/ for runnable programs and docs/API.md
for the full reference.
Design philosophy
- Sync-first. The fast path is synchronous, runs on the calling thread, allocates nothing, and dispatches in nanoseconds.
- Lock-free reads. Multiple threads can call
notify()concurrently without contention. - Zero allocation on the hot path. Notify walks the handler list and dispatches without any heap allocation in the no-panic case.
- Focused scope. This is a local, in-process notification primitive. Not a message bus, not a distributed event system, not a pub/sub broker.
Documentation
docs/API.md— full API reference with examples per item.docs/PATTERNS.md— four canonical integration patterns (hot reload, audit fan-out, metric events, transaction hooks), each with a runnable example.docs/ARCHITECTURE.md— internal structure walk-through, hot/slow path code excerpts, design decisions.docs/PERFORMANCE.md— cost model, benchmarks, and concurrency characteristics.docs/SECURITY.md— threat model, panic isolation detail, fuzz methodology.docs/STABILITY-1.0.md— the v1.0 stability contract (what's frozen, what can change).docs/PLATFORM-NOTES.md— per-platform behavior nuances (Linux/macOS/Windows)..dev/ROADMAP.md— milestone plan to 1.0.CHANGELOG.md— release history.
Standards
- REPS (Rust Efficiency & Performance Standards) governs every decision.
See
REPS.md. - MSRV: Rust 1.85.
- Edition: 2024.
- Cross-platform: Linux, macOS, Windows.
License
Dual-licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT License (LICENSE-MIT)
at your option.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.