ntex_rt/lib.rs
1//! Single-threaded asynchronous runtime infrastructure for ntex.
2//!
3//! A [`System`] owns the runtime configuration and coordinates one or more
4//! [`Arbiter`] execution threads. Each arbiter runs local futures on its own
5//! thread, allowing tasks to use types that are not `Send`.
6//!
7//! Use [`System::build()`] to configure a system, [`spawn()`] to start a local
8//! task, and [`spawn_blocking()`] for blocking work.
9//!
10//! Local [`JoinHandle`]s are neither `Send` nor `Sync`, even for `Send` outputs.
11//!
12//! [`Handle::spawn()`] submits a `Send` future to the handle's runtime and
13//! returns a [`RemoteJoinHandle`] for the calling thread. On the runtime's own thread
14//! the task is spawned directly. From other threads the future and later its
15//! `Send` output share one allocation, which is passed to the runtime thread:
16//!
17//! ```no_run
18//! # async fn example(handle: ntex_rt::Handle) {
19//! let task = handle.spawn(async { 42 });
20//! assert_eq!(task.await.unwrap(), 42);
21//! # }
22//! ```
23//!
24//! Dropping the handle detaches the task, [`RemoteJoinHandle::cancel()`] cancels it.
25//! A submitted future is polled and dropped on the runtime thread; shutdown or
26//! cancellation before producing a result yields a join error. No task is
27//! guaranteed to finish. Rejected submissions are never polled and their
28//! futures may be dropped on the submitting thread.
29//!
30//! Use [`Handle::spawn_detached()`] for fire-and-forget work. Its output need
31//! not be `Send` and is discarded on the owner.
32//!
33//! Remote join handles also remain on their calling thread, even when the
34//! submission uses the runtime's local fast path. Both the submitted future
35//! and its output must be `Send`:
36//!
37//! ```compile_fail
38//! # fn example(handle: ntex_rt::Handle) {
39//! handle.spawn(async { std::rc::Rc::new(42) });
40//! # }
41//! ```
42//!
43//! [`LocalWaker`] owns a thread-confined wake handle. It specializes native
44//! task wakes on their owner thread and preserves standard waker behavior
45//! for other runtimes and wrapped wakers.
46//!
47//! Runtime backends are selected with Cargo features:
48//!
49//! - no runtime feature uses the native ntex runtime
50//! - `tokio` uses the Tokio-based runtime adapter
51//! - `compio` uses the Compio-based runtime adapter
52//!
53//! The `tokio` and `compio` features are mutually exclusive. The [`rt`] module
54//! always exposes the native scheduler, including [`Runtime`] and
55//! [`RuntimeBuilder`], independently of which adapter supplies the
56//! crate-level spawning functions and [`Handle`].
57//!
58//! A [`Runner`] supplies the event loop used by [`SystemRunner`] and arbiter
59//! threads. Building a system only stores its configuration; execution begins
60//! when the returned system runner is driven.
61//!
62//! Native event-loop integrations implement [`Driver`] and use [`PollResult`]
63//! from [`Runtime::poll`] to decide whether to wait, poll again, or stop.
64//! Runtime, arbiter, and blocking pool metric types are grouped in [`metrics`].
65#![deny(clippy::pedantic)]
66#![allow(
67 clippy::missing_errors_doc,
68 clippy::missing_fields_in_debug,
69 clippy::must_use_candidate
70)]
71
72mod arbiter;
73mod builder;
74mod busy;
75#[cfg(target_os = "linux")]
76mod capture;
77mod driver;
78mod handle;
79mod local_waker;
80pub mod metrics;
81mod pool;
82mod remote;
83pub mod rt;
84pub mod signals;
85mod system;
86mod task;
87#[cfg(test)]
88mod testing;
89mod watchdog;
90
91#[doc(hidden)]
92#[cfg(feature = "compio")]
93pub mod rt_compio;
94#[doc(hidden)]
95#[cfg(feature = "tokio")]
96pub mod rt_tokio;
97
98pub use self::arbiter::{Arbiter, get_item, remove_all_items, set_item, with_item};
99pub use self::builder::{Builder, SystemRunner};
100pub use self::driver::{BlockFuture, Driver, DriverType, Notify, PollResult, Runner};
101pub use self::local_waker::LocalWaker;
102pub use self::pool::{BlockingError, BlockingResult, ThreadPool, spawn_blocking};
103pub use self::rt::{Runtime, RuntimeBuilder};
104pub use self::system::{Id, System};
105pub use self::task::{task_callbacks, task_opt_callbacks};
106
107#[cfg(feature = "tokio")]
108pub use self::rt_tokio::*;
109
110#[cfg(all(feature = "compio", not(feature = "tokio")))]
111pub use self::rt_compio::*;
112
113#[cfg(all(not(feature = "tokio"), not(feature = "compio")))]
114pub use self::rt::{Handle, JoinError, JoinHandle, RemoteJoinHandle, now, spawn, spawn_detached};
115
116pub(crate) type HashMap<K, V> = std::collections::HashMap<K, V, foldhash::fast::RandomState>;
117
118/// Runs `f` while an earlier panic is pending, the process aborts if `f`
119/// panics.
120pub(crate) fn abort_on_panic<R>(f: impl FnOnce() -> R) -> R {
121 struct Abort;
122
123 impl Drop for Abort {
124 fn drop(&mut self) {
125 abort_on_second_panic();
126 }
127 }
128
129 let guard = Abort;
130 let result = f();
131 std::mem::forget(guard);
132 result
133}
134
135/// Aborts the process on a panic raised while an earlier panic is unwinding
136/// or held to be resumed.
137#[cold]
138pub(crate) fn abort_on_second_panic() -> ! {
139 if let Err(payload) = std::panic::catch_unwind(|| {
140 log::error!("Panic during shutdown after an earlier panic; aborting");
141 }) {
142 std::mem::forget(payload);
143 }
144 std::process::abort();
145}