@@ -3,8 +3,10 @@ use std::str;
3
3
4
4
use rustc_middle:: ty:: layout:: LayoutOf ;
5
5
use rustc_span:: Symbol ;
6
+ use rustc_target:: abi:: Size ;
6
7
use rustc_target:: spec:: abi:: Abi ;
7
8
9
+ use crate :: concurrency:: cpu_affinity:: CpuAffinityMask ;
8
10
use crate :: shims:: alloc:: EvalContextExt as _;
9
11
use crate :: shims:: unix:: * ;
10
12
use crate :: * ;
@@ -571,6 +573,99 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
571
573
let result = this. nanosleep ( req, rem) ?;
572
574
this. write_scalar ( Scalar :: from_i32 ( result) , dest) ?;
573
575
}
576
+ "sched_getaffinity" => {
577
+ // Currently this function does not exist on all Unixes, e.g. on macOS.
578
+ if !matches ! ( & * this. tcx. sess. target. os, "linux" | "freebsd" | "android" ) {
579
+ throw_unsup_format ! (
580
+ "`sched_getaffinity` is not supported on {}" ,
581
+ this. tcx. sess. target. os
582
+ ) ;
583
+ }
584
+
585
+ let [ pid, cpusetsize, mask] =
586
+ this. check_shim ( abi, Abi :: C { unwind : false } , link_name, args) ?;
587
+ let pid = this. read_scalar ( pid) ?. to_u32 ( ) ?;
588
+ let cpusetsize = this. read_target_usize ( cpusetsize) ?;
589
+ let mask = this. read_pointer ( mask) ?;
590
+
591
+ // TODO: when https://github.com/rust-lang/miri/issues/3730 is fixed this should use its notion of tid/pid
592
+ let thread_id = match pid {
593
+ 0 => this. active_thread ( ) ,
594
+ _ => throw_unsup_format ! ( "`sched_getaffinity` is only supported with a pid of 0 (indicating the current thread)" ) ,
595
+ } ;
596
+
597
+ // The mask is stored in chunks, and the size must be a whole number of chunks.
598
+ let chunk_size = CpuAffinityMask :: chunk_size ( this) ;
599
+
600
+ if this. ptr_is_null ( mask) ? {
601
+ let einval = this. eval_libc ( "EFAULT" ) ;
602
+ this. set_last_error ( einval) ?;
603
+ this. write_scalar ( Scalar :: from_i32 ( -1 ) , dest) ?;
604
+ } else if cpusetsize == 0 || cpusetsize. checked_rem ( chunk_size) . unwrap ( ) != 0 {
605
+ // we only copy whole chunks of size_of::<c_ulong>()
606
+ let einval = this. eval_libc ( "EINVAL" ) ;
607
+ this. set_last_error ( einval) ?;
608
+ this. write_scalar ( Scalar :: from_i32 ( -1 ) , dest) ?;
609
+ } else if let Some ( cpuset) = this. machine . thread_cpu_affinity . get ( & thread_id) {
610
+ let cpuset = cpuset. clone ( ) ;
611
+ // we only copy whole chunks of size_of::<c_ulong>()
612
+ let byte_count = Ord :: min ( cpuset. as_slice ( ) . len ( ) , cpusetsize. try_into ( ) . unwrap ( ) ) ;
613
+ this. write_bytes_ptr ( mask, cpuset. as_slice ( ) [ ..byte_count] . iter ( ) . copied ( ) ) ?;
614
+ this. write_scalar ( Scalar :: from_i32 ( 0 ) , dest) ?;
615
+ } else {
616
+ // The thread whose ID is pid could not be found
617
+ let einval = this. eval_libc ( "ESRCH" ) ;
618
+ this. set_last_error ( einval) ?;
619
+ this. write_scalar ( Scalar :: from_i32 ( -1 ) , dest) ?;
620
+ }
621
+ }
622
+ "sched_setaffinity" => {
623
+ // Currently this function does not exist on all Unixes, e.g. on macOS.
624
+ if !matches ! ( & * this. tcx. sess. target. os, "linux" | "freebsd" | "android" ) {
625
+ throw_unsup_format ! (
626
+ "`sched_setaffinity` is not supported on {}" ,
627
+ this. tcx. sess. target. os
628
+ ) ;
629
+ }
630
+
631
+ let [ pid, cpusetsize, mask] =
632
+ this. check_shim ( abi, Abi :: C { unwind : false } , link_name, args) ?;
633
+ let pid = this. read_scalar ( pid) ?. to_u32 ( ) ?;
634
+ let cpusetsize = this. read_target_usize ( cpusetsize) ?;
635
+ let mask = this. read_pointer ( mask) ?;
636
+
637
+ // TODO: when https://github.com/rust-lang/miri/issues/3730 is fixed this should use its notion of tid/pid
638
+ let thread_id = match pid {
639
+ 0 => this. active_thread ( ) ,
640
+ _ => throw_unsup_format ! ( "`sched_setaffinity` is only supported with a pid of 0 (indicating the current thread)" ) ,
641
+ } ;
642
+
643
+ if this. ptr_is_null ( mask) ? {
644
+ let einval = this. eval_libc ( "EFAULT" ) ;
645
+ this. set_last_error ( einval) ?;
646
+ this. write_scalar ( Scalar :: from_i32 ( -1 ) , dest) ?;
647
+ } else {
648
+ // NOTE: cpusetsize might be smaller than `CpuAffinityMask::CPU_MASK_BYTES`.
649
+ // Any unspecified bytes are treated as zero here (none of the CPUs are configured).
650
+ // This is not exactly documented, so we assume that this is the behavior in practice.
651
+ let bits_slice = this. read_bytes_ptr_strip_provenance ( mask, Size :: from_bytes ( cpusetsize) ) ?;
652
+ // This ignores the bytes beyond `CpuAffinityMask::CPU_MASK_BYTES`
653
+ let bits_array: [ u8 ; CpuAffinityMask :: CPU_MASK_BYTES ] =
654
+ std:: array:: from_fn ( |i| bits_slice. get ( i) . copied ( ) . unwrap_or ( 0 ) ) ;
655
+ match CpuAffinityMask :: from_array ( this, this. machine . num_cpus , bits_array) {
656
+ Some ( cpuset) => {
657
+ this. machine . thread_cpu_affinity . insert ( thread_id, cpuset) ;
658
+ this. write_scalar ( Scalar :: from_i32 ( 0 ) , dest) ?;
659
+ }
660
+ None => {
661
+ // The intersection between the mask and the available CPUs was empty.
662
+ let einval = this. eval_libc ( "EINVAL" ) ;
663
+ this. set_last_error ( einval) ?;
664
+ this. write_scalar ( Scalar :: from_i32 ( -1 ) , dest) ?;
665
+ }
666
+ }
667
+ }
668
+ }
574
669
575
670
// Miscellaneous
576
671
"isatty" => {
0 commit comments