chio_cross_protocol/sync_bridge_shared.rs
1//! Compatibility-only synchronous bridge shim, shared by the outward protocol
2//! edges (chio-a2a-edge, chio-acp-edge). The edges gate their use of it on
3//! their own `compatibility-surface` feature; the helper itself is always
4//! compiled here so both edges can depend on a single definition instead of
5//! textually including this file.
6//!
7//! Mirrors the kernel's sync-bridge gate so the explicit passthrough surface
8//! fails closed under a current-thread runtime instead of deadlocking.
9
10/// Sentinel error returned by [`block_on_tool_server_invoke`] when the
11/// passthrough is invoked from inside a current-thread Tokio runtime.
12/// Mirrors `chio_kernel::KernelError::SyncBridgeIncompatibleWithCurrentThreadRuntime`:
13/// polling an async tool-server future with `futures::executor::block_on`
14/// on the only worker thread can deadlock indefinitely if the future
15/// awaits Tokio I/O. The kernel bridge refuses this case fail-closed,
16/// and the edge shims must match instead of reintroducing the
17/// deadlock through the `compatibility-surface` feature.
18#[derive(Debug, thiserror::Error)]
19#[error(
20 "sync bridge incompatible with current-thread Tokio runtime: \
21 block_on under a current-thread reactor would deadlock the only worker thread; \
22 move the host to a multi-thread runtime or call the async surface directly"
23)]
24pub struct SyncBridgeIncompatibleWithCurrentThreadRuntime;
25
26/// Mirrors `chio_kernel::kernel::block_on_async_tool_dispatch`: on a
27/// multi-thread runtime use `block_in_place` so we yield the runtime;
28/// on a current-thread runtime fail-closed with
29/// [`SyncBridgeIncompatibleWithCurrentThreadRuntime`] instead of
30/// silently parking the only worker thread; with no runtime active,
31/// drive the future with the non-tokio `futures::executor::block_on`.
32pub fn block_on_tool_server_invoke<F, T>(
33 future: F,
34) -> Result<T, SyncBridgeIncompatibleWithCurrentThreadRuntime>
35where
36 F: std::future::Future<Output = T>,
37{
38 match tokio::runtime::Handle::try_current() {
39 Ok(handle) if handle.runtime_flavor() == tokio::runtime::RuntimeFlavor::MultiThread => {
40 Ok(tokio::task::block_in_place(|| handle.block_on(future)))
41 }
42 Ok(_handle) => {
43 // Current-thread runtime active. Bridging here would deadlock
44 // any tool-server future that awaits Tokio I/O. Surface a
45 // typed error so callers see the architectural
46 // incompatibility instead of a silent hang. The passthrough
47 // call site converts this into a Failed passthrough response.
48 Err(SyncBridgeIncompatibleWithCurrentThreadRuntime)
49 }
50 Err(_) => {
51 // No Tokio runtime active. The future cannot collide with a
52 // surrounding reactor; the non-tokio executor is the safe
53 // bridge. This is the path the in-process, compute-only
54 // tool servers used in unit tests rely on.
55 Ok(futures::executor::block_on(future))
56 }
57 }
58}