@@ -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,101 @@ 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 actual representation of the CpuAffinityMask is [c_ulong; _], in practice either
598+ //
599+ // - [u32; 32] on 32-bit platforms
600+ // - [u64; 16] everywhere else
601+ let chunk_size = CpuAffinityMask :: chunk_size ( & this. tcx . sess . target ) ;
602+
603+ if this. ptr_is_null ( mask) ? {
604+ let einval = this. eval_libc ( "EFAULT" ) ;
605+ this. set_last_error ( einval) ?;
606+ this. write_scalar ( Scalar :: from_i32 ( -1 ) , dest) ?;
607+ } else if cpusetsize == 0 || cpusetsize. checked_rem ( chunk_size) . unwrap ( ) != 0 {
608+ // we only copy whole chunks of size_of::<c_ulong>()
609+ let einval = this. eval_libc ( "EINVAL" ) ;
610+ this. set_last_error ( einval) ?;
611+ this. write_scalar ( Scalar :: from_i32 ( -1 ) , dest) ?;
612+ } else if let Some ( cpuset) = this. machine . thread_cpu_affinity . get ( & thread_id) {
613+ let cpuset = cpuset. clone ( ) ;
614+ // we only copy whole chunks of size_of::<c_ulong>()
615+ let byte_count = Ord :: min ( cpuset. as_slice ( ) . len ( ) , cpusetsize. try_into ( ) . unwrap ( ) ) ;
616+ this. write_bytes_ptr ( mask, cpuset. as_slice ( ) [ ..byte_count] . iter ( ) . copied ( ) ) ?;
617+ this. write_scalar ( Scalar :: from_i32 ( 0 ) , dest) ?;
618+ } else {
619+ // The thread whose ID is pid could not be found
620+ let einval = this. eval_libc ( "ESRCH" ) ;
621+ this. set_last_error ( einval) ?;
622+ this. write_scalar ( Scalar :: from_i32 ( -1 ) , dest) ?;
623+ }
624+ }
625+ "sched_setaffinity" => {
626+ // Currently this function does not exist on all Unixes, e.g. on macOS.
627+ if !matches ! ( & * this. tcx. sess. target. os, "linux" | "freebsd" | "android" ) {
628+ throw_unsup_format ! (
629+ "`sched_setaffinity` is not supported on {}" ,
630+ this. tcx. sess. target. os
631+ ) ;
632+ }
633+
634+ let [ pid, cpusetsize, mask] =
635+ this. check_shim ( abi, Abi :: C { unwind : false } , link_name, args) ?;
636+ let pid = this. read_scalar ( pid) ?. to_u32 ( ) ?;
637+ let cpusetsize = this. read_target_usize ( cpusetsize) ?;
638+ let mask = this. read_pointer ( mask) ?;
639+
640+ // TODO: when https://github.com/rust-lang/miri/issues/3730 is fixed this should use its notion of tid/pid
641+ let thread_id = match pid {
642+ 0 => this. active_thread ( ) ,
643+ _ => throw_unsup_format ! ( "`sched_setaffinity` is only supported with a pid of 0 (indicating the current thread)" ) ,
644+ } ;
645+
646+ #[ allow( clippy:: map_entry) ]
647+ if this. ptr_is_null ( mask) ? {
648+ let einval = this. eval_libc ( "EFAULT" ) ;
649+ this. set_last_error ( einval) ?;
650+ this. write_scalar ( Scalar :: from_i32 ( -1 ) , dest) ?;
651+ } else {
652+ // NOTE: cpusetsize might be smaller than `CpuAffinityMask::CPU_MASK_BYTES`
653+ let bits_slice = this. read_bytes_ptr_strip_provenance ( mask, Size :: from_bytes ( cpusetsize) ) ?;
654+ // This ignores the bytes beyond `CpuAffinityMask::CPU_MASK_BYTES`
655+ let bits_array: [ u8 ; CpuAffinityMask :: CPU_MASK_BYTES ] =
656+ std:: array:: from_fn ( |i| bits_slice. get ( i) . copied ( ) . unwrap_or ( 0 ) ) ;
657+ match CpuAffinityMask :: from_array ( & this. tcx . sess . target , this. machine . num_cpus , bits_array) {
658+ Some ( cpuset) => {
659+ this. machine . thread_cpu_affinity . insert ( thread_id, cpuset) ;
660+ this. write_scalar ( Scalar :: from_i32 ( 0 ) , dest) ?;
661+ }
662+ None => {
663+ // The intersection between the mask and the available CPUs was empty.
664+ let einval = this. eval_libc ( "EINVAL" ) ;
665+ this. set_last_error ( einval) ?;
666+ this. write_scalar ( Scalar :: from_i32 ( -1 ) , dest) ?;
667+ }
668+ }
669+ }
670+ }
574671
575672 // Miscellaneous
576673 "isatty" => {
0 commit comments