Skip to content

Commit fd1a5e6

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 (1M context) <noreply@anthropic.com>
1 parent 6480bff commit fd1a5e6

1 file changed

Lines changed: 52 additions & 6 deletions

File tree

src/builder.rs

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -367,10 +367,18 @@ 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))]
371-
pub fn set_runtime(&mut self, runtime_handle: tokio::runtime::Handle) -> &mut Self {
373+
pub fn set_runtime(
374+
&mut self, runtime_handle: tokio::runtime::Handle,
375+
) -> Result<&mut Self, BuildError> {
376+
if runtime_handle.runtime_flavor() != tokio::runtime::RuntimeFlavor::MultiThread {
377+
return Err(BuildError::RuntimeSetupFailed);
378+
}
379+
372380
self.runtime_handle = Some(runtime_handle);
373-
self
381+
Ok(self)
374382
}
375383

376384
/// Configures the [`Node`] instance to source its chain data from the given Esplora server.
@@ -910,14 +918,24 @@ impl NodeBuilder {
910918
}
911919

912920
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))))
921+
let runtime = if let Some(handle) = self.runtime_handle.as_ref() {
922+
Arc::new(Runtime::with_handle(handle.clone(), Arc::clone(logger)))
915923
} else {
916-
Ok(Arc::new(Runtime::new(Arc::clone(logger)).map_err(|e| {
924+
Arc::new(Runtime::new(Arc::clone(logger)).map_err(|e| {
917925
log_error!(logger, "Failed to setup tokio runtime: {}", e);
918926
BuildError::RuntimeSetupFailed
919-
})?))
927+
})?)
928+
};
929+
930+
if runtime.handle().runtime_flavor() != tokio::runtime::RuntimeFlavor::MultiThread {
931+
log_error!(
932+
logger,
933+
"Failed to setup tokio runtime: we require a multithreaded runtime."
934+
);
935+
return Err(BuildError::RuntimeSetupFailed);
920936
}
937+
938+
Ok(runtime)
921939
}
922940

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

0 commit comments

Comments
 (0)