@@ -1694,6 +1694,16 @@ mod tests {
16941694 } ) ;
16951695 }
16961696
1697+ fn test_spawn_pinned < R : Runner > ( runner : R )
1698+ where
1699+ R :: Context : Spawner ,
1700+ {
1701+ runner. start ( |context| async move {
1702+ let handle = context. pinned ( 0 ) . spawn ( |_| async move { 42 } ) ;
1703+ assert ! ( matches!( handle. await , Ok ( 42 ) ) ) ;
1704+ } ) ;
1705+ }
1706+
16971707 fn test_spawn < R : Runner > ( runner : R )
16981708 where
16991709 R :: Context : Spawner + Clock ,
@@ -3325,6 +3335,12 @@ mod tests {
33253335 test_spawn_dedicated ( executor) ;
33263336 }
33273337
3338+ #[ test]
3339+ fn test_deterministic_spawn_pinned ( ) {
3340+ let executor = deterministic:: Runner :: default ( ) ;
3341+ test_spawn_pinned ( executor) ;
3342+ }
3343+
33283344 #[ test]
33293345 fn test_deterministic_spawn ( ) {
33303346 let runner = deterministic:: Runner :: default ( ) ;
@@ -3674,6 +3690,79 @@ mod tests {
36743690 test_spawn_dedicated ( executor) ;
36753691 }
36763692
3693+ #[ test]
3694+ fn test_tokio_spawn_pinned ( ) {
3695+ let executor = tokio:: Runner :: default ( ) ;
3696+ test_spawn_pinned ( executor) ;
3697+ }
3698+
3699+ #[ test]
3700+ fn test_tokio_spawn_pinned_dedicated_thread ( ) {
3701+ // Verify that pinned implies dedicated.
3702+ let executor = tokio:: Runner :: default ( ) ;
3703+ executor. start ( |context| async move {
3704+ let root_thread = std:: thread:: current ( ) . id ( ) ;
3705+ let task_thread = context
3706+ . pinned ( 0 )
3707+ . spawn ( |_| async move { std:: thread:: current ( ) . id ( ) } )
3708+ . await
3709+ . unwrap ( ) ;
3710+ // The task should run on a different thread than the root thread.
3711+ assert_ne ! ( root_thread, task_thread) ;
3712+ } ) ;
3713+ }
3714+
3715+ #[ cfg( target_os = "linux" ) ]
3716+ #[ test]
3717+ fn test_tokio_spawn_pinned_correct_core ( ) {
3718+ // Verify that a pinned task is actually running on the expected core,
3719+ // for every available core.
3720+ let num_cores = crate :: available_cores ( ) . unwrap ( ) ;
3721+ let executor = tokio:: Runner :: default ( ) ;
3722+ executor. start ( |context| async move {
3723+ for core in 0 ..num_cores {
3724+ let actual = context
3725+ . clone ( )
3726+ . pinned ( core)
3727+ . spawn ( |_| async move {
3728+ // SAFETY: `sched_getcpu` is a read-only query with no
3729+ // preconditions.
3730+ unsafe { libc:: sched_getcpu ( ) as usize }
3731+ } )
3732+ . await
3733+ . unwrap ( ) ;
3734+ assert_eq ! ( actual, core) ;
3735+ }
3736+ } ) ;
3737+ }
3738+
3739+ #[ cfg( target_os = "linux" ) ]
3740+ #[ test]
3741+ fn test_tokio_spawn_pinned_same_core ( ) {
3742+ // Verify that two separate tasks pinned to the same core run on
3743+ // different threads but report the same CPU.
3744+ let executor = tokio:: Runner :: default ( ) ;
3745+ executor. start ( |context| async move {
3746+ let t1 = context. clone ( ) . pinned ( 1 ) . spawn ( |_| async move {
3747+ // SAFETY: `sched_getcpu` is a read-only query with no
3748+ // preconditions.
3749+ ( std:: thread:: current ( ) . id ( ) , unsafe { libc:: sched_getcpu ( ) } )
3750+ } ) ;
3751+ let t2 = context. clone ( ) . pinned ( 1 ) . spawn ( |_| async move {
3752+ // SAFETY: `sched_getcpu` is a read-only query with no
3753+ // preconditions.
3754+ ( std:: thread:: current ( ) . id ( ) , unsafe { libc:: sched_getcpu ( ) } )
3755+ } ) ;
3756+ let ( r1, r2) = futures:: future:: join ( t1, t2) . await ;
3757+ let ( thread1, cpu1) = r1. unwrap ( ) ;
3758+ let ( thread2, cpu2) = r2. unwrap ( ) ;
3759+ // Different dedicated threads.
3760+ assert_ne ! ( thread1, thread2) ;
3761+ // Same core.
3762+ assert_eq ! ( cpu1, cpu2) ;
3763+ } ) ;
3764+ }
3765+
36773766 #[ test]
36783767 fn test_tokio_spawn ( ) {
36793768 let runner = tokio:: Runner :: default ( ) ;
0 commit comments