Skip to content

Commit f1ae35f

Browse files
elnoshclaude
andcommitted
Require a multithreaded runtime when setting up the node
We already note on `Node::start` that we require the outer runtime to be of the multithreaded flavor, but we never check it. We should, as our synchronous API methods enter the runtime via `block_in_place`, which panics on current-thread runtimes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 6480bff commit f1ae35f

1 file changed

Lines changed: 45 additions & 4 deletions

File tree

src/builder.rs

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,8 @@ impl NodeBuilder {
367367
///
368368
/// If not provided, the node will spawn its own runtime or reuse any outer runtime context it
369369
/// can detect.
370+
///
371+
/// Note we require the given runtime to be of the `multithreaded` flavor.
370372
#[cfg_attr(feature = "uniffi", allow(dead_code))]
371373
pub fn set_runtime(&mut self, runtime_handle: tokio::runtime::Handle) -> &mut Self {
372374
self.runtime_handle = Some(runtime_handle);
@@ -910,14 +912,24 @@ impl NodeBuilder {
910912
}
911913

912914
fn setup_runtime(&self, logger: &Arc<Logger>) -> Result<Arc<Runtime>, BuildError> {
913-
if let Some(handle) = self.runtime_handle.as_ref() {
914-
Ok(Arc::new(Runtime::with_handle(handle.clone(), Arc::clone(logger))))
915+
let runtime = if let Some(handle) = self.runtime_handle.as_ref() {
916+
Arc::new(Runtime::with_handle(handle.clone(), Arc::clone(logger)))
915917
} else {
916-
Ok(Arc::new(Runtime::new(Arc::clone(logger)).map_err(|e| {
918+
Arc::new(Runtime::new(Arc::clone(logger)).map_err(|e| {
917919
log_error!(logger, "Failed to setup tokio runtime: {}", e);
918920
BuildError::RuntimeSetupFailed
919-
})?))
921+
})?)
922+
};
923+
924+
if runtime.handle().runtime_flavor() != tokio::runtime::RuntimeFlavor::MultiThread {
925+
log_error!(
926+
logger,
927+
"Failed to setup tokio runtime: we require a multithreaded runtime."
928+
);
929+
return Err(BuildError::RuntimeSetupFailed);
920930
}
931+
932+
Ok(runtime)
921933
}
922934

923935
fn build_with_store_and_logger<S: PaginatedKVStore + Send + Sync + 'static>(
@@ -2698,4 +2710,33 @@ mod tests {
26982710
let node = sanitize_alias(alias);
26992711
assert_eq!(node.err().unwrap(), BuildError::InvalidNodeAlias);
27002712
}
2713+
2714+
#[test]
2715+
fn rejects_non_multithreaded_runtimes() {
2716+
let logger = Arc::new(Logger::new_log_facade());
2717+
let current_thread_runtime =
2718+
tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap();
2719+
2720+
let mut builder = NodeBuilder::new();
2721+
builder.set_runtime(current_thread_runtime.handle().clone());
2722+
assert_eq!(
2723+
builder.setup_runtime(&logger).err(),
2724+
Some(BuildError::RuntimeSetupFailed),
2725+
"a current-thread runtime given via `set_runtime` should be rejected"
2726+
);
2727+
2728+
current_thread_runtime.block_on(async {
2729+
assert_eq!(
2730+
NodeBuilder::new().setup_runtime(&logger).err(),
2731+
Some(BuildError::RuntimeSetupFailed),
2732+
"a detected outer current-thread runtime context should be rejected"
2733+
);
2734+
});
2735+
2736+
let multi_thread_runtime =
2737+
tokio::runtime::Builder::new_multi_thread().enable_all().build().unwrap();
2738+
let mut builder = NodeBuilder::new();
2739+
builder.set_runtime(multi_thread_runtime.handle().clone());
2740+
assert!(builder.setup_runtime(&logger).is_ok(), "a multi-threaded runtime should be used");
2741+
}
27012742
}

0 commit comments

Comments
 (0)