@@ -123,11 +123,7 @@ pub trait Spawner: Clone + Send + Sync + 'static {
123123
124124 /// Enqueue a task to be executed (without consuming the context).
125125 ///
126- /// Unlike a future, a spawned task will start executing immediately (even if the caller
127- /// does not await the handle).
128- ///
129- /// In some cases, it may be useful to spawn a task without consuming the context (e.g. starting
130- /// an actor that already has a reference to context).
126+ /// The semantics are the same as [Spawner::spawn].
131127 ///
132128 /// # Warning
133129 ///
@@ -161,6 +157,22 @@ pub trait Spawner: Clone + Send + Sync + 'static {
161157 F : FnOnce ( Self ) -> T + Send + ' static ,
162158 T : Send + ' static ;
163159
160+ /// Enqueue a blocking task to be executed (without consuming the context).
161+ ///
162+ /// The semantics are the same as [Spawner::spawn_blocking].
163+ ///
164+ /// # Warning
165+ ///
166+ /// If this function is used to spawn multiple tasks from the same context,
167+ /// the runtime will panic to prevent accidental misuse.
168+ fn spawn_blocking_ref < F , T > (
169+ & mut self ,
170+ dedicated : bool ,
171+ ) -> impl FnOnce ( F ) -> Handle < T > + ' static
172+ where
173+ F : FnOnce ( ) -> T + Send + ' static ,
174+ T : Send + ' static ;
175+
164176 /// Signals the runtime to stop execution and that all outstanding tasks
165177 /// should perform any required cleanup and exit. This method is idempotent and
166178 /// can be called multiple times.
@@ -933,6 +945,32 @@ mod tests {
933945 } ) ;
934946 }
935947
948+ fn test_spawn_blocking_ref < R : Runner > ( runner : R , dedicated : bool )
949+ where
950+ R :: Context : Spawner ,
951+ {
952+ runner. start ( |mut context| async move {
953+ let spawn = context. spawn_blocking_ref ( dedicated) ;
954+ let handle = spawn ( || 42 ) ;
955+ let result = handle. await ;
956+ assert ! ( matches!( result, Ok ( 42 ) ) ) ;
957+ } ) ;
958+ }
959+
960+ fn test_spawn_blocking_ref_duplicate < R : Runner > ( runner : R , dedicated : bool )
961+ where
962+ R :: Context : Spawner ,
963+ {
964+ runner. start ( |mut context| async move {
965+ let spawn = context. spawn_blocking_ref ( dedicated) ;
966+ let result = spawn ( || 42 ) . await ;
967+ assert ! ( matches!( result, Ok ( 42 ) ) ) ;
968+
969+ // Ensure context is consumed
970+ context. spawn_blocking ( dedicated, |_| 42 ) ;
971+ } ) ;
972+ }
973+
936974 fn test_spawn_blocking_abort < R : Runner > ( runner : R , dedicated : bool )
937975 where
938976 R :: Context : Spawner ,
@@ -1157,6 +1195,23 @@ mod tests {
11571195 }
11581196 }
11591197
1198+ #[ test]
1199+ fn test_deterministic_spawn_blocking_ref ( ) {
1200+ for dedicated in [ false , true ] {
1201+ let executor = deterministic:: Runner :: default ( ) ;
1202+ test_spawn_blocking_ref ( executor, dedicated) ;
1203+ }
1204+ }
1205+
1206+ #[ test]
1207+ #[ should_panic]
1208+ fn test_deterministic_spawn_blocking_ref_duplicate ( ) {
1209+ for dedicated in [ false , true ] {
1210+ let executor = deterministic:: Runner :: default ( ) ;
1211+ test_spawn_blocking_ref_duplicate ( executor, dedicated) ;
1212+ }
1213+ }
1214+
11601215 #[ test]
11611216 fn test_deterministic_metrics ( ) {
11621217 let executor = deterministic:: Runner :: default ( ) ;
@@ -1311,6 +1366,23 @@ mod tests {
13111366 }
13121367 }
13131368
1369+ #[ test]
1370+ fn test_tokio_spawn_blocking_ref ( ) {
1371+ for dedicated in [ false , true ] {
1372+ let executor = tokio:: Runner :: default ( ) ;
1373+ test_spawn_blocking_ref ( executor, dedicated) ;
1374+ }
1375+ }
1376+
1377+ #[ test]
1378+ #[ should_panic]
1379+ fn test_tokio_spawn_blocking_ref_duplicate ( ) {
1380+ for dedicated in [ false , true ] {
1381+ let executor = tokio:: Runner :: default ( ) ;
1382+ test_spawn_blocking_ref_duplicate ( executor, dedicated) ;
1383+ }
1384+ }
1385+
13141386 #[ test]
13151387 fn test_tokio_metrics ( ) {
13161388 let executor = tokio:: Runner :: default ( ) ;
0 commit comments