//! The tokio runtime every network worker is built from. //! //! One function rather than a builder repeated at each call site, because the //! choice it encodes is not obvious and was wrong in eleven places at once. //! //! **Why not `new_current_thread`.** A current-thread runtime drives its //! reactor only while the thread is inside `block_on`. On Android that left //! reqwest's connection future unpolled: the await never resolved, so the //! worker neither failed nor returned. Every symptom was an absence — no error, //! no panic for `catch_unwind` to catch, no log line, and a channel that closed //! only when the thread was finally torn down. What the user saw was a sign-in //! that stopped, and a library that quietly scanned itself rather than adopting //! the shards the server already held. //! //! A multi-thread runtime owns worker threads that poll the reactor regardless. //! One worker is enough: these are single request-response flows, not //! throughput-bound work. //! //! **Why not `enable_all`.** That also starts the signal driver, which wants //! process-wide signal handling. Inside an Android app the runtime already owns //! that, and a worker thread claiming it is asking for trouble. These flows need //! IO (reqwest) and time (poll intervals, timeouts) and nothing else. //! //! **Why a wrapper.** [`NetRuntime::block_on`] is tokio's, behind the guard //! that fails a debug or test build when it is called on the UI thread //! (NFR-ARCH-1; see `executors::assert_not_ui`). Everything else derefs to the //! runtime. /// Build a runtime suitable for a network worker thread. /// /// Call from the spawned thread, not from the caller: the runtime must live on /// the thread that blocks on it — a thread from `executors::spawn`, normally /// on `Executor::Network`. pub fn build() -> std::io::Result { tokio::runtime::Builder::new_multi_thread() .worker_threads(1) .enable_io() .enable_time() .build() .map(NetRuntime) } /// A network worker's runtime, whose `block_on` refuses the UI thread. pub struct NetRuntime(tokio::runtime::Runtime); impl NetRuntime { /// TRACES: NFR-ARCH-1 | NFR-P9 /// Tokio's `block_on`, after asserting the caller is not the UI thread. #[track_caller] pub fn block_on(&self, future: F) -> F::Output { crate::executors::assert_not_ui("block_on"); self.0.block_on(future) } } impl std::ops::Deref for NetRuntime { type Target = tokio::runtime::Runtime; fn deref(&self) -> &Self::Target { &self.0 } }