Skip to main content

hyperi_rustlib/concurrency/
mod.rs

1// Project:   hyperi-rustlib
2// File:      src/concurrency/mod.rs
3// Purpose:   Three generic async primitives for HyperI Rust libraries
4// Language:  Rust
5//
6// License:   BUSL-1.1
7// Copyright: (c) 2026 HYPERI PTY LIMITED
8
9//! Async concurrency primitives.
10//!
11//! Three patterns, each generic over the consumer's domain type, each
12//! the canonical way to do the corresponding shape of async work in
13//! HyperI Rust libraries. See
14//! `hyperi-ai/standards/languages/RUST.md` ยง"Three async primitives
15//! every HyperI library uses" for design philosophy and a decision
16//! matrix.
17//!
18//! # Decision matrix
19//!
20//! | Shape | Use |
21//! |---|---|
22//! | Consumer pushes; background batches + writes to a backend | [`BackgroundSink`] + [`SinkDrain`] |
23//! | Timer-driven loop, no inbound queue | [`PeriodicWorker`] + [`PeriodicTask`] |
24//! | Mutable state, command queue, optional oneshot replies | [`ActorHandle`] + [`Actor`] |
25//!
26//! # The hard rule
27//!
28//! **An `async fn` in HyperI libraries MUST yield to the runtime.**
29//! No hidden synchronous I/O. If you need sync I/O from async:
30//!
31//! - Use `tokio::fs` / `tokio::io::AsyncWrite` for occasional async I/O.
32//! - Use [`BackgroundSink`] for high-throughput durable writes.
33//! - Use `tokio::task::spawn_blocking` for one-off unavoidable sync work.
34//! - Don't -- push the sync work to startup, before the runtime is hot.
35//!
36//! The grep-based `tests/sync_in_async.rs` lint enforces this
37//! mechanically: any `async fn` body containing `std::fs::*`,
38//! `std::io::Write::write_*`, `std::thread::sleep`, `reqwest::blocking::*`,
39//! or `parking_lot::*::lock()` held across `.await` fails CI.
40//!
41//! # Considered alternatives
42//!
43//! - `tokio-actors` (v0.6) -- covers `ActorHandle` + `PeriodicWorker`
44//!   cleanly but doesn't provide batched-drain-with-flush-barrier,
45//!   which is the load-bearing case for [`BackgroundSink`]. Wrapping
46//!   it would equal the size of this module's impl. Rejected.
47//! - `tokio-prometheus-metered-channel` (v0.2) -- bounded channel with
48//!   Prometheus metrics, no actor / batch / flush. Too narrow.
49//! - `xtra`, `actix`, `bastion` -- heavy actor frameworks. Don't fit
50//!   the sink shape. Rejected.
51//! - `tracing-appender::non_blocking` -- used elsewhere for the logger
52//!   subscriber; orthogonal to this module's tokio-runtime concerns.
53//!
54//! Rationale per [Alice Ryhl's "Actors with Tokio"](https://ryhl.io/blog/actors-with-tokio/)
55//! and the [2026-05-08 audit](https://github.com/hyperi-io/hyperi-rustlib).
56
57mod actor;
58mod error;
59mod periodic;
60mod sink;
61
62pub use actor::{Actor, ActorConfig, ActorHandle, ActorJoinHandle};
63pub use error::{ActorError, DrainError, SinkError, TickError};
64pub use periodic::{PeriodicTask, PeriodicWorker};
65pub use sink::{BackgroundSink, BackgroundSinkConfig, BackgroundSinkHandle, Overflow, SinkDrain};