@@ -369,10 +369,18 @@ impl NodeBuilder {
369369 ///
370370 /// If not provided, the node will spawn its own runtime or reuse any outer runtime context it
371371 /// can detect.
372+ ///
373+ /// Note we require the given runtime to be of the `multithreaded` flavor.
372374 #[ cfg_attr( feature = "uniffi" , allow( dead_code) ) ]
373- pub fn set_runtime ( & mut self , runtime_handle : tokio:: runtime:: Handle ) -> & mut Self {
375+ pub fn set_runtime (
376+ & mut self , runtime_handle : tokio:: runtime:: Handle ,
377+ ) -> Result < & mut Self , BuildError > {
378+ if runtime_handle. runtime_flavor ( ) != tokio:: runtime:: RuntimeFlavor :: MultiThread {
379+ return Err ( BuildError :: RuntimeSetupFailed ) ;
380+ }
381+
374382 self . runtime_handle = Some ( runtime_handle) ;
375- self
383+ Ok ( self )
376384 }
377385
378386 /// Configures the [`Node`] instance to source its chain data from the given Esplora server.
@@ -912,14 +920,24 @@ impl NodeBuilder {
912920 }
913921
914922 fn setup_runtime ( & self , logger : & Arc < Logger > ) -> Result < Arc < Runtime > , BuildError > {
915- if let Some ( handle) = self . runtime_handle . as_ref ( ) {
916- Ok ( Arc :: new ( Runtime :: with_handle ( handle. clone ( ) , Arc :: clone ( logger) ) ) )
923+ let runtime = if let Some ( handle) = self . runtime_handle . as_ref ( ) {
924+ Arc :: new ( Runtime :: with_handle ( handle. clone ( ) , Arc :: clone ( logger) ) )
917925 } else {
918- Ok ( Arc :: new ( Runtime :: new ( Arc :: clone ( logger) ) . map_err ( |e| {
926+ Arc :: new ( Runtime :: new ( Arc :: clone ( logger) ) . map_err ( |e| {
919927 log_error ! ( logger, "Failed to setup tokio runtime: {}" , e) ;
920928 BuildError :: RuntimeSetupFailed
921- } ) ?) )
929+ } ) ?)
930+ } ;
931+
932+ if runtime. handle ( ) . runtime_flavor ( ) != tokio:: runtime:: RuntimeFlavor :: MultiThread {
933+ log_error ! (
934+ logger,
935+ "Failed to setup tokio runtime: we require a multithreaded runtime."
936+ ) ;
937+ return Err ( BuildError :: RuntimeSetupFailed ) ;
922938 }
939+
940+ Ok ( runtime)
923941 }
924942
925943 fn build_with_store_and_logger < S : PaginatedKVStore + Send + Sync + ' static > (
@@ -2706,4 +2724,32 @@ mod tests {
27062724 let node = sanitize_alias ( alias) ;
27072725 assert_eq ! ( node. err( ) . unwrap( ) , BuildError :: InvalidNodeAlias ) ;
27082726 }
2727+
2728+ #[ test]
2729+ fn rejects_non_multithreaded_runtimes ( ) {
2730+ let logger = Arc :: new ( Logger :: new_log_facade ( ) ) ;
2731+ let current_thread_runtime =
2732+ tokio:: runtime:: Builder :: new_current_thread ( ) . enable_all ( ) . build ( ) . unwrap ( ) ;
2733+
2734+ let mut builder = NodeBuilder :: new ( ) ;
2735+ assert_eq ! (
2736+ builder. set_runtime( current_thread_runtime. handle( ) . clone( ) ) . err( ) ,
2737+ Some ( BuildError :: RuntimeSetupFailed ) ,
2738+ "a current-thread runtime given via `set_runtime` should be rejected"
2739+ ) ;
2740+
2741+ current_thread_runtime. block_on ( async {
2742+ assert_eq ! (
2743+ NodeBuilder :: new( ) . setup_runtime( & logger) . err( ) ,
2744+ Some ( BuildError :: RuntimeSetupFailed ) ,
2745+ "a detected outer current-thread runtime context should be rejected"
2746+ ) ;
2747+ } ) ;
2748+
2749+ let multi_thread_runtime =
2750+ tokio:: runtime:: Builder :: new_multi_thread ( ) . enable_all ( ) . build ( ) . unwrap ( ) ;
2751+ let mut builder = NodeBuilder :: new ( ) ;
2752+ builder. set_runtime ( multi_thread_runtime. handle ( ) . clone ( ) ) . unwrap ( ) ;
2753+ assert ! ( builder. setup_runtime( & logger) . is_ok( ) , "a multi-threaded runtime should be used" ) ;
2754+ }
27092755}
0 commit comments