11//! # Async Actors
22//!
3- //! `tonari-actor` lets you freely combine sync (blocking) and ` async` actors within one system.
3+ //! `tonari-actor` lets you freely combine sync (blocking) and async actors within one system.
44//!
55//! While sync actors implement the [`Actor`](crate::Actor) trait and are spawned using the
66//! [`System::spawn()`], [`System::prepare()`] and [`System::prepare_fn()`] family of methods,
7- //! ` async` actors implement [`AsyncActor`] and are spawned using [`System::spawn_async()`],
7+ //! async actors implement [`AsyncActor`] and are spawned using [`System::spawn_async()`],
88//! [`System::prepare_async()`] and [`System::prepare_async_fn()`].
99//!
10- //! Sync and ` async` actors share the same [`Addr`] and [`Recipient`](crate::Recipient) types.
10+ //! Sync and async actors share the same [`Addr`] and [`Recipient`](crate::Recipient) types.
1111//!
12- //! `async` actors share the same paradigm as sync actors: each one gets its own OS-level thread.
13- //! More specifically a single-threaded async runtime is spawned for every ` async` actor.
12+ //! Async actors share the same paradigm as sync actors: each one gets its own OS-level thread.
13+ //! More specifically a single-threaded async runtime is spawned for every async actor.
1414//!
15- //! `tonari-actor` currently uses the [`tokio`] ecosystem, more specifically its [`LocalRuntime`][^tokio].
15+ //! `tonari-actor` currently uses the [`tokio`][^tokio] ecosystem, more specifically its
16+ //! [`LocalRuntime`]. It allows spawning futures that are _not_ [`Send`], which means you
17+ //! can use [`Rc`](https://doc.rust-lang.org/std/rc/struct.Rc.html) and
18+ //! [`RefCell`](https://doc.rust-lang.org/std/cell/struct.RefCell.html) instead of
19+ //! [`Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html) and mutexes in your futures.
20+ //! It also allows the [`AsyncActor`] trait (and the implementors) to use the `async fn` syntax.
1621//!
17- //! TODO explain tokio feature flags and that downstreams may need to enable more.
22+ //! Tokio's [`LocalRuntime`] is currently [gated behind the `tokio_unstable` _Rust
23+ //! flag_](https://docs.rs/tokio/latest/tokio/index.html#unstable-features). Note that this isn't
24+ //! a cargo feature flag (that would go to `Cargo.toml`); it goes to `.cargo/config.toml` or
25+ //! `RUSTFLAGS`, which override the former. It needs to be specified in our leaf project/workspace
26+ //! (it doesn't propagate from `tonari-actor`). Stabilization of [`LocalRuntime`] is tracked in
27+ //! [tokio-rs/tokio#7558](https://github.com/tokio-rs/tokio/issues/7558).
1828//!
19- //! TODO lacking feature: block on
29+ //! With [`AsyncActor::handle()`] being an `async fn`, you gain an access to the wide async library
30+ //! ecosystem (currently those compatible with [`tokio`]), and you can employ concurrency (still
31+ //! within the single thread) when processing each message by using one of future combinators.
2032//!
21- //! [^tokio]: TODO explain that any runtime is sufficient (no dependency on tokio-specific features)
22- //! we only need to spawn _some_ runtime in the actor loop. tokio was just a pragmatic choice.
23- //! we could add support for alternative ones, even runtime-configurable.
33+ //! But the incoming messages are still processed sequentially (the actor framework won't start
34+ //! multiple concurrent [`AsyncActor::handle()`] futures of a given actor). If you want to process
35+ //! the _messages_ concurrently, spawn an async task and return from the `handle()` method.
36+ //!
37+ //! Async tasks can be spawned using [`tokio::task::spawn_local()`], or [`tokio::spawn()`] if the
38+ //! [`Send`] bound of the latter doesn't limit you.
39+ //!
40+ //! Note that an async equivalent of [`crate::SpawnBuilderWithAddress::run_and_block()`] is not
41+ //! currently implemented (contributions welcome).
42+ //!
43+ //! [^tokio]: on logical level, `tonari-actor` isn't tied to any specific async runtime (it doesn't
44+ //! do any runtime-specific operations like I/O or timers), it just needs to spawn _some_ async
45+ //! runtime in the actor loop. Tokio was just a pragmatic choice that many crates in the
46+ //! ecosystem use. We could add support for alternative ones, even runtime-configurable.
2447
2548use crate :: {
2649 ActorError , Addr , BareContext , Capacity , Control , Priority , RegistryEntry , System ,
@@ -32,8 +55,8 @@ use std::{any::type_name, fmt, future, thread};
3255use tokio:: runtime:: LocalRuntime ;
3356
3457/// The actor trait - async variant.
35- // Ad. the #[allow]: using `async` fn in a trait doesn't allow us to specify `Send` (or other)
36- // bounds, but we don't really need any bounds, because TODO - we use single-threaded runtime
58+ // Ad. the #[allow]: using `async fn` in a trait doesn't allow us to specify `Send` (or other)
59+ // bounds, but we don't really need any bounds, because we use [`LocalRuntime`].
3760#[ allow( async_fn_in_trait) ]
3861pub trait AsyncActor {
3962 /// The expected type of a message to be received.
@@ -270,6 +293,9 @@ impl System {
270293 // 1. If multiple futures in the combinator are ready, it should return the one with
271294 // higher priority (control > high > normal);
272295 // 2. Otherwise it would wait for the first message to be ready and return that.
296+ //
297+ // Tokio's `select` macro documentation contains nice survey of ecosystem alternatives:
298+ // https://docs.rs/tokio/latest/tokio/macro.select.html#racing-futures
273299 let received = select_biased ! (
274300 control = control_stream. next( ) => {
275301 Received :: Control ( control. expect( "We keep control_tx alive through addr." ) )
0 commit comments