@@ -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