Name the executors and fail a block_on on the UI thread

architecture.md §7.1 stated five executors and their thread counts, and
no code named them. dr_ui::executors now does: the Executor enum with
each one's thread name and the count §7.1 gives with its reason, and
spawn, which starts a thread named <executor>:<role> and marks it with
the executor it belongs to. The counts are the stated budget, not yet a
bound: a job still gets a thread of its own when it starts.

run marks its own thread as the UI executor before it builds the
window. net_runtime::build now returns a NetRuntime whose block_on
asserts, in debug and test builds, that the caller is not that thread;
everything else derefs to the tokio runtime. The login, folder-list and
remote-folder workers built the same runtime by hand and now take it
from net_runtime, so their block_on is guarded too.

Tests: a block_on on a thread marked as the UI executor panics naming
the UI thread; the same call on a worker returns; a spawned thread
carries its name and executor.
This commit is contained in:
2026-09-27 07:08:37 -04:00
parent c50d96e949
commit 7be1efff32
5 changed files with 292 additions and 25 deletions
+31 -2
View File
@@ -21,14 +21,43 @@
//! 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.
pub fn build() -> std::io::Result<tokio::runtime::Runtime> {
/// the thread that blocks on it — a thread from `executors::spawn`, normally
/// on `Executor::Network`.
pub fn build() -> std::io::Result<NetRuntime> {
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<F: std::future::Future>(&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
}
}