@@ -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