use super::plan::Processor;
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum Did {
#[cfg_attr(
not(windows),
expect(
dead_code,
reason = "only Windows takes the process into a job whose affinity limit this is"
)
)]
Pinned { group: u16, mask: u64 },
#[cfg_attr(
not(target_os = "linux"),
expect(dead_code, reason = "only Linux answers with a taskset list")
)]
External { list: String },
Nothing { reason: String },
}
static TAKEN: std::sync::OnceLock<Did> = std::sync::OnceLock::new();
#[cfg(windows)]
static JOB: std::sync::OnceLock<win32job::Job> = std::sync::OnceLock::new();
pub(crate) fn take(seats: &[Processor]) -> Did {
let did = TAKEN.get_or_init(|| acted(seats)).clone();
if !matches!(did, Did::Pinned { .. }) {
eprintln!(
"[core] placement = \"pinned\": {}",
words(&did, seats.len())
);
}
did
}
pub(crate) fn clause(seats: &[Processor]) -> String {
let did = match TAKEN.get() {
Some(did) => did.clone(),
None => would(seats),
};
words(&did, seats.len())
}
fn words(did: &Did, cores: usize) -> String {
match did {
Did::Pinned { .. } => format!(
"hard affinity: the process is held to the {cores} planned cores by a Job Object"
),
Did::External { list } => format!(
"hard affinity: start the binary under `taskset -c {list}`; this arm sets nothing inside the process"
),
Did::Nothing { reason } => format!("hard affinity: nothing is pinned ({reason})"),
}
}
#[cfg(windows)]
fn acted(seats: &[Processor]) -> Did {
let (group, mask) = match would(seats) {
Did::Pinned { group, mask } => (group, mask),
external @ Did::External { .. } | external @ Did::Nothing { .. } => return external,
};
let Ok(wide) = usize::try_from(mask) else {
return Did::Nothing {
reason: "the plan's mask does not fit this build's word".to_owned(),
};
};
let mut info = win32job::ExtendedLimitInfo::default();
info.limit_affinity(wide).limit_silent_breakaway_ok();
let job = match win32job::Job::create_with_limit_info(&info) {
Ok(job) => job,
Err(err) => {
return Did::Nothing {
reason: format!("a job with that affinity limit was refused ({err})"),
};
}
};
if let Err(err) = job.assign_current_process() {
return Did::Nothing {
reason: format!("this process could not join the job ({err})"),
};
}
if JOB.set(job).is_err() {
return Did::Nothing {
reason: "the process was already taken".to_owned(),
};
}
Did::Pinned { group, mask }
}
#[cfg(not(windows))]
fn acted(seats: &[Processor]) -> Did {
would(seats)
}
fn would(seats: &[Processor]) -> Did {
if seats.is_empty() {
return Did::Nothing {
reason: "the plan leaves every processor to the operating system".to_owned(),
};
}
#[cfg(windows)]
{
let group = match desktop_ffi::cpu::thread_group() {
Ok(group) => group,
Err(err) => {
return Did::Nothing {
reason: format!("this thread's processor group is unreadable ({err:?})"),
};
}
};
let mask = seats
.iter()
.filter(|seat| seat.group == group.group)
.filter_map(|seat| 1_u64.checked_shl(seat.number))
.fold(0, |mask, bit| mask | bit)
& group.mask;
if mask == 0 {
return Did::Nothing {
reason: "the plan names no processor of this thread's group".to_owned(),
};
}
Did::Pinned {
group: group.group,
mask,
}
}
#[cfg(target_os = "linux")]
{
Did::External {
list: seats
.iter()
.map(|seat| seat.number.to_string())
.collect::<Vec<_>>()
.join(","),
}
}
#[cfg(not(any(windows, target_os = "linux")))]
{
Did::Nothing {
reason: "this platform has no hard-affinity call".to_owned(),
}
}
}
#[cfg(test)]
#[allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
reason = "test code"
)]
mod tests {
use super::*;
#[cfg(windows)]
#[test]
fn the_job_reports_the_mask_the_arm_asked_for() {
let group = desktop_ffi::cpu::thread_group().unwrap();
let seats: Vec<Processor> = (0..64)
.filter(|&number| group.mask >> number & 1 == 1)
.take(2)
.map(|number| Processor {
group: group.group,
number,
})
.collect();
assert_eq!(seats.len(), 2, "this machine lends two processors");
let did = take(&seats);
let Did::Pinned { mask, .. } = did else {
panic!("the arm pinned nothing: {did:?}");
};
assert_eq!(desktop_ffi::cpu::thread_group().unwrap().mask, mask);
}
#[cfg(target_os = "linux")]
#[test]
fn linux_asks_to_be_started_under_taskset() {
let seats = vec![
Processor {
group: 0,
number: 2,
},
Processor {
group: 0,
number: 4,
},
];
assert_eq!(
would(&seats),
Did::External {
list: "2,4".to_owned()
}
);
}
#[cfg(not(any(windows, target_os = "linux")))]
#[test]
fn a_platform_without_the_call_says_so() {
let seats = vec![Processor {
group: 0,
number: 0,
}];
let Did::Nothing { reason } = would(&seats) else {
panic!("this platform is expected to have no hard-affinity call");
};
assert!(reason.contains("no hard-affinity call"), "{reason}");
assert!(clause(&seats).contains("nothing is pinned"));
}
#[test]
fn an_empty_plan_pins_nothing_and_says_so() {
let Did::Nothing { reason } = would(&[]) else {
panic!("an empty plan asks nothing of any platform");
};
assert!(reason.contains("leaves every processor"), "{reason}");
}
}