#![allow(non_snake_case)]
#![allow(dead_code)]
use crate::ported::machine::Machine;
#[cfg(target_os = "linux")]
use crate::ported::object::{Arg, Object, Object_isA};
#[cfg(target_os = "linux")]
use crate::ported::process::{Process, Process_class, Process_getPid};
#[cfg(target_os = "linux")]
use core::any::Any;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Affinity {
pub host: *mut Machine,
pub size: u32,
pub used: u32,
pub cpus: Vec<u32>,
}
pub fn Affinity_new(host: *mut Machine) -> Affinity {
Affinity {
host,
size: 8,
used: 0,
cpus: vec![0; 8],
}
}
pub fn Affinity_delete(this: Affinity) {
let _ = this;
}
pub fn Affinity_add(this: &mut Affinity, id: u32) {
if this.used == this.size {
this.size *= 2;
this.cpus.resize(this.size as usize, 0);
}
this.cpus[this.used as usize] = id;
this.used += 1;
}
#[cfg(target_os = "linux")]
pub fn Affinity_get(p: &Process, host: *mut Machine) -> Option<Affinity> {
let mut cpuset: libc::cpu_set_t = unsafe { core::mem::zeroed() };
let ok = unsafe {
libc::sched_getaffinity(
Process_getPid(p),
core::mem::size_of::<libc::cpu_set_t>(),
&mut cpuset,
)
} == 0;
if !ok {
return None;
}
let mut affinity = Affinity_new(host);
let existingCPUs = unsafe { (*host).existingCPUs };
for i in 0..existingCPUs {
if unsafe { libc::CPU_ISSET(i as usize, &cpuset) } {
Affinity_add(&mut affinity, i);
}
}
Some(affinity)
}
#[cfg(target_os = "linux")]
pub fn Affinity_set(p: &Process, arg: Arg) -> bool {
let this: &Affinity = match arg {
Arg::V(v) => unsafe { &*(v as *const Affinity) },
Arg::I(_) => panic!("Affinity_set: Arg must carry the Affinity* in arg.v"),
};
let mut cpuset: libc::cpu_set_t = unsafe { core::mem::zeroed() };
unsafe { libc::CPU_ZERO(&mut cpuset) };
for i in 0..this.used {
unsafe { libc::CPU_SET(this.cpus[i as usize] as usize, &mut cpuset) };
}
unsafe {
libc::sched_setaffinity(
Process_getPid(p),
core::mem::size_of::<core::ffi::c_ulong>(),
&cpuset,
) == 0
}
}
#[cfg(target_os = "linux")]
pub fn Affinity_rowSet(row: &dyn Object, arg: Arg) -> bool {
assert!(Object_isA(Some(row), &Process_class));
let p = (row as &dyn Any)
.downcast_ref::<Process>()
.expect("Affinity_rowSet: row is not a Process");
Affinity_set(p, arg)
}
#[cfg(target_os = "linux")]
pub fn Affinity_rowGet(row: &dyn Object, host: *mut Machine) -> Option<Affinity> {
assert!(Object_isA(Some(row), &Process_class));
let p = (row as &dyn Any)
.downcast_ref::<Process>()
.expect("Affinity_rowGet: row is not a Process");
Affinity_get(p, host)
}
#[cfg(test)]
mod tests {
use super::*;
fn fresh() -> Affinity {
Affinity_new(core::ptr::null_mut())
}
#[test]
fn affinity_new_initial_state() {
let a = fresh();
assert_eq!(a.size, 8);
assert_eq!(a.used, 0);
assert_eq!(a.cpus, vec![0; 8]);
assert!(a.host.is_null());
}
#[test]
fn affinity_add_appends_within_capacity() {
let mut a = fresh();
for id in 0..8u32 {
Affinity_add(&mut a, id * 10);
}
assert_eq!(a.used, 8);
assert_eq!(a.size, 8); assert_eq!(&a.cpus[..8], &[0, 10, 20, 30, 40, 50, 60, 70]);
}
#[test]
fn affinity_add_doubles_capacity_on_overflow() {
let mut a = fresh();
for id in 0..8u32 {
Affinity_add(&mut a, id);
}
assert_eq!(a.size, 8);
assert_eq!(a.used, 8);
Affinity_add(&mut a, 100);
assert_eq!(a.size, 16);
assert_eq!(a.used, 9);
assert_eq!(a.cpus.len(), 16);
assert_eq!(a.cpus[8], 100);
}
#[test]
fn affinity_add_doubles_repeatedly() {
let mut a = fresh();
for id in 0..17u32 {
Affinity_add(&mut a, id);
}
assert_eq!(a.used, 17);
assert_eq!(a.size, 32);
assert_eq!(a.cpus.len(), 32);
for id in 0..17u32 {
assert_eq!(a.cpus[id as usize], id);
}
}
#[test]
fn affinity_add_preserves_existing_on_growth() {
let mut a = fresh();
for id in 0..8u32 {
Affinity_add(&mut a, 1000 + id);
}
Affinity_add(&mut a, 9999); for id in 0..8u32 {
assert_eq!(a.cpus[id as usize], 1000 + id);
}
assert_eq!(a.cpus[8], 9999);
}
#[cfg(target_os = "linux")]
use crate::ported::machine::Machine;
#[cfg(target_os = "linux")]
use crate::ported::object::{Arg, Object};
#[cfg(target_os = "linux")]
use crate::ported::process::{Process, Process_setPid};
#[cfg(target_os = "linux")]
fn online_host() -> Machine {
let ncpu = unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) };
let existing = if ncpu > 0 { ncpu as u32 } else { 1 };
let mut host = Machine::default();
host.existingCPUs = existing;
host
}
#[cfg(target_os = "linux")]
fn current_thread_process() -> Process {
let mut p = Process::default();
Process_setPid(&mut p, 0);
p
}
#[cfg(target_os = "linux")]
#[test]
fn affinity_get_reports_current_thread_cpus() {
let mut host = online_host();
let existing = host.existingCPUs;
let hp: *mut Machine = &mut host;
let p = current_thread_process();
let aff = Affinity_get(&p, hp).expect("sched_getaffinity(0) must succeed");
assert!(aff.used >= 1);
assert_eq!(aff.host, hp);
for k in 0..aff.used as usize {
assert!(aff.cpus[k] < existing);
}
}
#[cfg(target_os = "linux")]
#[test]
fn affinity_set_roundtrips_current_mask() {
let mut host = online_host();
let hp: *mut Machine = &mut host;
let p = current_thread_process();
let mut aff = Affinity_get(&p, hp).expect("get must succeed");
let arg = Arg::V(&mut aff as *mut Affinity as *mut core::ffi::c_void);
assert!(Affinity_set(&p, arg));
}
#[cfg(target_os = "linux")]
#[test]
fn affinity_rowget_and_rowset_delegate_through_object_isa() {
let mut host = online_host();
let hp: *mut Machine = &mut host;
let p = current_thread_process();
let mut aff = Affinity_rowGet(&p as &dyn Object, hp).expect("rowGet must succeed");
assert!(aff.used >= 1);
let arg = Arg::V(&mut aff as *mut Affinity as *mut core::ffi::c_void);
assert!(Affinity_rowSet(&p as &dyn Object, arg));
}
#[cfg(target_os = "linux")]
#[test]
#[should_panic]
fn affinity_rowget_rejects_non_process_row() {
let row = crate::ported::row::Row::default();
let mut host = Machine::default();
let hp: *mut Machine = &mut host;
let _ = Affinity_rowGet(&row as &dyn Object, hp);
}
}