@@ -3,8 +3,10 @@ use std::str;
33
44use rustc_middle:: ty:: layout:: LayoutOf ;
55use rustc_span:: Symbol ;
6+ use rustc_target:: abi:: Size ;
67use rustc_target:: spec:: abi:: Abi ;
78
9+ use crate :: concurrency:: cpu_affinity:: CpuAffinityMask ;
810use crate :: shims:: alloc:: EvalContextExt as _;
911use crate :: shims:: unix:: * ;
1012use crate :: * ;
@@ -571,6 +573,99 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
571573 let result = this. nanosleep ( req, rem) ?;
572574 this. write_scalar ( Scalar :: from_i32 ( result) , dest) ?;
573575 }
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+ }
574669
575670 // Miscellaneous
576671 "isatty" => {
0 commit comments