use std::fmt::Debug;
use std::iter;
use windows::Win32::System::JobObjects::{
IsProcessInJob, JOBOBJECT_CPU_RATE_CONTROL_INFORMATION, JobObjectCpuRateControlInformation,
JobObjectGroupInformationEx, QueryInformationJobObject,
};
use windows::Win32::System::Kernel::PROCESSOR_NUMBER;
use windows::Win32::System::Registry::{
HKEY_LOCAL_MACHINE, RRF_RT_REG_DWORD, RRF_RT_REG_SZ, RegGetValueW,
};
use windows::Win32::System::SystemInformation::{
GROUP_AFFINITY, GetLogicalProcessorInformationEx, LOGICAL_PROCESSOR_RELATIONSHIP,
SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX,
};
use windows::Win32::System::Threading::{
GetActiveProcessorCount, GetCurrentProcess, GetCurrentProcessorNumberEx, GetCurrentThread,
GetMaximumProcessorCount, GetMaximumProcessorGroupCount, GetNumaHighestNodeNumber,
GetProcessDefaultCpuSetMasks, GetThreadGroupAffinity, GetThreadSelectedCpuSetMasks,
SetThreadSelectedCpuSetMasks,
};
use windows::core::{BOOL, PCWSTR, Result};
use crate::pal::windows::Bindings;
#[derive(Debug, Default)]
pub(crate) struct BuildTargetBindings;
impl Bindings for BuildTargetBindings {
fn get_active_processor_count(&self, group_number: u16) -> u32 {
unsafe { GetActiveProcessorCount(group_number) }
}
fn get_maximum_processor_count(&self, group_number: u16) -> u32 {
unsafe { GetMaximumProcessorCount(group_number) }
}
fn get_maximum_processor_group_count(&self) -> u16 {
unsafe { GetMaximumProcessorGroupCount() }
}
fn get_current_processor_number_ex(&self) -> PROCESSOR_NUMBER {
unsafe { GetCurrentProcessorNumberEx() }
}
unsafe fn get_logical_processor_information_ex(
&self,
relationship_type: LOGICAL_PROCESSOR_RELATIONSHIP,
buffer: Option<*mut SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX>,
returned_length: *mut u32,
) -> Result<()> {
unsafe { GetLogicalProcessorInformationEx(relationship_type, buffer, returned_length) }
}
fn get_numa_highest_node_number(&self) -> u32 {
let mut result: u32 = 0;
unsafe { GetNumaHighestNodeNumber(&raw mut result) }
.expect("platform refused to inform us about memory region count");
result
}
fn get_current_process_default_cpu_set_masks(&self) -> Vec<GROUP_AFFINITY> {
let current_process = unsafe { GetCurrentProcess() };
let max_group_count = self.get_maximum_processor_group_count();
let mut buffer = vec![GROUP_AFFINITY::default(); max_group_count as usize];
let mut required_mask_count = 0;
unsafe {
GetProcessDefaultCpuSetMasks(
current_process,
Some(&mut buffer),
&raw mut required_mask_count,
)
}
.expect("platform refused to provide the current process default processor affinity");
buffer.truncate(required_mask_count as usize);
buffer
}
fn get_current_thread_cpu_set_masks(&self) -> Vec<GROUP_AFFINITY> {
let current_thread = unsafe { GetCurrentThread() };
let max_group_count = self.get_maximum_processor_group_count();
let mut buffer = vec![GROUP_AFFINITY::default(); max_group_count as usize];
let mut required_mask_count = 0;
unsafe {
GetThreadSelectedCpuSetMasks(
current_thread,
Some(&mut buffer),
&raw mut required_mask_count,
)
}
.expect("platform refused to provide the current process default processor affinity");
buffer.truncate(required_mask_count as usize);
buffer
}
fn set_current_thread_cpu_set_masks(&self, masks: &[GROUP_AFFINITY]) {
let current_thread = unsafe { GetCurrentThread() };
unsafe { SetThreadSelectedCpuSetMasks(current_thread, Some(masks)) }
.expect("platform refused to accept a new current thread processor affinity");
}
#[cfg_attr(coverage_nightly, coverage(off))]
fn get_current_job_cpu_set_masks(&self) -> Vec<GROUP_AFFINITY> {
let current_process = unsafe { GetCurrentProcess() };
let mut result: BOOL = BOOL::default();
unsafe {
IsProcessInJob(current_process, None, &raw mut result).expect(
"platform refused to confirm or deny whether the current process is part of a job",
);
}
if !result.as_bool() {
return Vec::new();
}
let mut buffer =
vec![GROUP_AFFINITY::default(); self.get_maximum_processor_group_count() as usize];
let mut bytes_written: u32 = 0;
let buffer_len_items: u32 = buffer.len().try_into().expect(
"platform does not support more than u32 processor groups, so this can never overflow",
);
let size_of_group_affinity = size_of::<GROUP_AFFINITY>()
.try_into()
.expect("struct of known size guaranteed to fit in u32");
let buffer_len_bytes = buffer_len_items.checked_mul(size_of_group_affinity)
.expect("even under extreme processor group counts, we cannot overflow u32 by having too many GROUP_AFFINITYs");
unsafe {
QueryInformationJobObject(
None,
JobObjectGroupInformationEx,
buffer.as_mut_ptr().cast(),
buffer_len_bytes,
Some(&raw mut bytes_written),
)
}
.expect("platform refused to provide the process's current job processor affinity");
buffer.truncate(
bytes_written
.checked_div(
size_of::<GROUP_AFFINITY>()
.try_into()
.expect("struct of known size guaranteed to fit in u32"),
)
.expect("GROUP_AFFINITY is not a ZST, so there can be no division by zero")
as usize,
);
buffer
}
fn get_current_thread_legacy_group_affinity(&self) -> GROUP_AFFINITY {
let current_thread = unsafe { GetCurrentThread() };
let mut aff = GROUP_AFFINITY::default();
unsafe { GetThreadGroupAffinity(current_thread, &raw mut aff) }
.expect("platform refused to provide the current thread's legacy processor affinity");
aff
}
fn get_processor_max_mhz(&self, max_processor_count: usize) -> Vec<u32> {
(0..max_processor_count)
.map(read_processor_nominal_max_mhz)
.collect()
}
fn get_processor_name_strings(&self, max_processor_count: usize) -> Vec<Option<String>> {
(0..max_processor_count)
.map(read_processor_name_string)
.collect()
}
#[cfg_attr(coverage_nightly, coverage(off))]
fn get_current_job_cpu_rate_control(&self) -> Option<JOBOBJECT_CPU_RATE_CONTROL_INFORMATION> {
let current_process = unsafe { GetCurrentProcess() };
let mut result: BOOL = BOOL::default();
unsafe {
IsProcessInJob(current_process, None, &raw mut result).expect(
"platform refused to confirm or deny whether the current process is part of a job",
);
}
if !result.as_bool() {
return None;
}
let mut result = JOBOBJECT_CPU_RATE_CONTROL_INFORMATION::default();
let result_size_u32 = size_of::<JOBOBJECT_CPU_RATE_CONTROL_INFORMATION>()
.try_into()
.expect("struct of known size guaranteed to fit in u32");
unsafe {
QueryInformationJobObject(
None,
JobObjectCpuRateControlInformation,
(&raw mut result).cast(),
result_size_u32,
None,
)
}
.expect("platform refused to provide the process's current job processor time constraints");
Some(result)
}
}
fn read_processor_nominal_max_mhz(processor_id: usize) -> u32 {
let subkey = to_nul_terminated_wide(&format!(
"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\{processor_id}"
));
let value_name = to_nul_terminated_wide("~MHz");
let mut value: u32 = 0;
let mut value_size_bytes: u32 =
u32::try_from(size_of::<u32>()).expect("the size of a u32 always fits in a u32");
let status = unsafe {
RegGetValueW(
HKEY_LOCAL_MACHINE,
PCWSTR(subkey.as_ptr()),
PCWSTR(value_name.as_ptr()),
RRF_RT_REG_DWORD,
None,
Some((&raw mut value).cast()),
Some(&raw mut value_size_bytes),
)
};
if status.is_ok() { value } else { 0 }
}
#[cfg_attr(coverage_nightly, coverage(off))]
fn read_processor_name_string(processor_id: usize) -> Option<String> {
let subkey = to_nul_terminated_wide(&format!(
"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\{processor_id}"
));
let value_name = to_nul_terminated_wide("ProcessorNameString");
let mut value_size_bytes: u32 = 0;
let status = unsafe {
RegGetValueW(
HKEY_LOCAL_MACHINE,
PCWSTR(subkey.as_ptr()),
PCWSTR(value_name.as_ptr()),
RRF_RT_REG_SZ,
None,
None,
Some(&raw mut value_size_bytes),
)
};
if status.is_err() || value_size_bytes == 0 {
return None;
}
let mut buffer: Vec<u16> = vec![0; (value_size_bytes as usize).div_ceil(size_of::<u16>())];
let status = unsafe {
RegGetValueW(
HKEY_LOCAL_MACHINE,
PCWSTR(subkey.as_ptr()),
PCWSTR(value_name.as_ptr()),
RRF_RT_REG_SZ,
None,
Some(buffer.as_mut_ptr().cast()),
Some(&raw mut value_size_bytes),
)
};
if status.is_err() {
return None;
}
let units: Vec<u16> = buffer.into_iter().take_while(|&unit| unit != 0).collect();
let text = String::from_utf16_lossy(&units);
let trimmed = text.trim();
if trimmed.is_empty() {
None
} else {
Some(trimmed.to_string())
}
}
fn to_nul_terminated_wide(value: &str) -> Vec<u16> {
value.encode_utf16().chain(iter::once(0)).collect()
}