#![allow(unsafe_code)]
#![deny(missing_docs)]
#[cfg(all(windows, target_pointer_width = "32"))]
compile_error!(
"numa-shim supports 64-bit Windows only (owner policy, task #1313/F11, \
enforced by task #1321); 32-bit Windows (target_pointer_width = \"32\") \
is out of scope -- see the crate-level platform-matrix doc comment and \
README.md's platform table for the full policy statement"
);
pub const NO_NODE: u32 = u32::MAX;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct NodeId(u32);
impl NodeId {
pub const fn new(id: u32) -> Option<Self> {
if id == NO_NODE {
None
} else {
Some(Self(id))
}
}
#[must_use]
pub const fn get(self) -> u32 {
self.0
}
}
#[non_exhaustive]
#[derive(Debug)]
pub enum ReserveNumaError {
UnsupportedPlatform,
UnsupportedArchitecture,
InvalidArguments,
InvalidNode,
Os(std::io::Error),
}
impl core::fmt::Display for ReserveNumaError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::UnsupportedPlatform => {
f.write_str("NUMA-preferred reservation is unsupported on this platform")
}
Self::UnsupportedArchitecture => {
f.write_str("Linux architecture without a known SYS_MBIND syscall number")
}
Self::InvalidArguments => {
f.write_str("invalid arguments (reservation contract violation)")
}
Self::InvalidNode => {
f.write_str("NUMA node id cannot be addressed by this platform's nodemask")
}
Self::Os(e) => write!(f, "{e}"),
}
}
}
impl std::error::Error for ReserveNumaError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Os(e) => Some(e),
_ => None,
}
}
}
#[cfg(feature = "vmem-integration")]
pub use aligned_vmem::Reservation;
#[cfg(numa_shim_mock)]
pub mod mock {
use crate::NodeResolution;
use core::cell::{Cell, RefCell};
pub const CALLS_CAP: usize = 4096;
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MockCall {
CurrentNode(u32),
CurrentNodeResolution(NodeResolution),
#[non_exhaustive]
ReservePreferredOnNode {
size: usize,
align: usize,
node: u32,
},
#[non_exhaustive]
InstallPolicy {
node: u32,
reservation_len: usize,
succeeded: bool,
},
PolicyFailureRelease {
node: u32,
},
}
std::thread_local! {
pub(crate) static CALLS: RefCell<Vec<MockCall>> = const { RefCell::new(Vec::new()) };
pub(crate) static CURRENT_NODE_SLOT: Cell<u32> = const { Cell::new(0) };
pub(crate) static POLICY_FAILURE_SLOT: RefCell<Option<(u32, std::io::Error)>> = const { RefCell::new(None) };
}
pub fn drain() -> Vec<MockCall> {
CALLS.with(|c| c.borrow_mut().drain(..).collect())
}
pub fn set_current_node(node: u32) {
CURRENT_NODE_SLOT.with(|c| c.set(node));
}
pub(crate) fn current_node_slot() -> u32 {
CURRENT_NODE_SLOT.with(|c| c.get())
}
pub fn set_policy_failure(node: u32, err: std::io::Error) {
POLICY_FAILURE_SLOT.with(|c| *c.borrow_mut() = Some((node, err)));
}
pub fn clear_policy_failure() {
POLICY_FAILURE_SLOT.with(|c| *c.borrow_mut() = None);
}
#[cfg_attr(not(feature = "vmem-integration"), allow(dead_code))]
pub(crate) fn take_policy_failure_for(node: u32) -> Option<std::io::Error> {
POLICY_FAILURE_SLOT
.try_with(|c| {
if let Ok(mut b) = c.try_borrow_mut() {
b.take().and_then(|(armed_node, err)| {
if armed_node == node {
Some(err)
} else {
*b = Some((armed_node, err));
None
}
})
} else {
None
}
})
.unwrap_or(None)
}
pub(crate) fn record(call: MockCall) {
let _ = CALLS.try_with(|c| {
if let Ok(mut b) = c.try_borrow_mut() {
if b.len() < CALLS_CAP {
b.push(call);
}
}
});
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum NodeResolution {
Resolved(u32),
TopologyUnavailable,
Unavailable,
}
#[must_use]
pub fn current_node_resolution() -> NodeResolution {
#[cfg(numa_shim_mock)]
{
let n = mock::current_node_slot();
let resolution = if n == NO_NODE {
NodeResolution::Unavailable
} else {
NodeResolution::Resolved(n)
};
mock::record(mock::MockCall::CurrentNodeResolution(resolution));
resolution
}
#[cfg(not(numa_shim_mock))]
{
platform::current_node_resolution_impl()
}
}
#[must_use]
pub fn current_node() -> Option<u32> {
#[cfg(numa_shim_mock)]
{
let n = mock::current_node_slot();
mock::record(mock::MockCall::CurrentNode(n));
if n == NO_NODE {
None
} else {
Some(n)
}
}
#[cfg(not(numa_shim_mock))]
{
let raw = platform::current_node_impl();
if raw == NO_NODE {
None
} else {
Some(raw)
}
}
}
#[cfg(feature = "vmem-integration")]
pub fn reserve_preferred_on_node(
size: usize,
align: usize,
node: NodeId,
) -> Result<aligned_vmem::Reservation, ReserveNumaError> {
#[cfg(numa_shim_mock)]
{
mock::record(mock::MockCall::ReservePreferredOnNode {
size,
align,
node: node.get(),
});
if node.get() >= 64 {
return Err(ReserveNumaError::InvalidNode);
}
let r = match aligned_vmem::try_reserve_aligned(size, align) {
Ok(r) => r,
Err(e) => {
return Err(if e.is_invalid_argument() {
ReserveNumaError::InvalidArguments
} else {
ReserveNumaError::Os(std::io::Error::from(e))
})
}
};
let reservation_len = r.reservation_len();
match mock::take_policy_failure_for(node.get()) {
Some(err) => {
mock::record(mock::MockCall::InstallPolicy {
node: node.get(),
reservation_len,
succeeded: false,
});
drop(r);
mock::record(mock::MockCall::PolicyFailureRelease { node: node.get() });
Err(ReserveNumaError::Os(err))
}
None => {
mock::record(mock::MockCall::InstallPolicy {
node: node.get(),
reservation_len,
succeeded: true,
});
Ok(r)
}
}
}
#[cfg(not(numa_shim_mock))]
{
platform::reserve_preferred_on_node_impl(size, align, node)
}
}
#[doc(hidden)]
pub mod cpumap {
pub const MAX_INDEXED_CPUS: usize = 8192;
pub const CPU_UNMAPPED: u8 = u8::MAX;
pub fn parse_each_set_cpu(data: &[u8], mut on_cpu: impl FnMut(u32)) -> bool {
let data = trim_end(data);
for (w, word_str) in data.rsplit(|&b| b == b',').enumerate() {
let val = match parse_hex_u32(word_str) {
Some(v) => v,
None => return false,
};
for bit in 0..32 {
if (val >> bit) & 1 == 1 {
on_cpu((w * 32 + bit) as u32);
}
}
}
true
}
pub fn format_sysfs_path(buf: &mut [u8; 64], node: u32) -> &[u8] {
const PREFIX: &[u8] = b"/sys/devices/system/node/node";
const SUFFIX: &[u8] = b"/cpumap\0";
let mut pos = 0usize;
for &b in PREFIX {
buf[pos] = b;
pos += 1;
}
let mut tmp = [0u8; 10];
let mut n = node;
let mut digits = 0usize;
if n == 0 {
tmp[0] = b'0';
digits = 1;
} else {
while n > 0 {
tmp[digits] = b'0' + (n % 10) as u8;
n /= 10;
digits += 1;
}
tmp[..digits].reverse();
}
for &d in tmp.iter().take(digits) {
buf[pos] = d;
pos += 1;
}
for &b in SUFFIX {
buf[pos] = b;
pos += 1;
}
&buf[..pos]
}
pub fn parse_contains_cpu(data: &[u8], cpu_idx: u32) -> bool {
let mut found = false;
let ok = parse_each_set_cpu(data, |b| {
if b == cpu_idx {
found = true;
}
});
ok && found
}
pub fn trim_end(data: &[u8]) -> &[u8] {
let mut end = data.len();
while end > 0 && (data[end - 1] == b'\n' || data[end - 1] == b'\r' || data[end - 1] == b' ')
{
end -= 1;
}
&data[..end]
}
pub fn parse_hex_u32(s: &[u8]) -> Option<u32> {
if s.is_empty() {
return None;
}
if s.len() > 8 {
return None;
}
let mut val: u32 = 0;
for &b in s {
let digit = match b {
b'0'..=b'9' => b - b'0',
b'a'..=b'f' => b - b'a' + 10,
b'A'..=b'F' => b - b'A' + 10,
_ => return None,
};
val = val.wrapping_shl(4) | digit as u32;
}
Some(val)
}
pub struct ReverseIndex {
map: [u8; MAX_INDEXED_CPUS],
}
impl Default for ReverseIndex {
fn default() -> Self {
Self::new()
}
}
impl ReverseIndex {
pub const fn new() -> Self {
Self {
map: [CPU_UNMAPPED; MAX_INDEXED_CPUS],
}
}
pub fn index_node(&mut self, node: u32, data: &[u8]) -> bool {
if node > 63 {
return false;
}
if !parse_each_set_cpu(data, |_| {}) {
return false;
}
parse_each_set_cpu(data, |cpu| {
if (cpu as usize) < MAX_INDEXED_CPUS {
let entry = &mut self.map[cpu as usize];
if *entry == CPU_UNMAPPED {
*entry = node as u8;
}
}
});
true
}
pub fn lookup(&self, cpu: u32) -> Option<u32> {
let entry = self.map.get(cpu as usize)?;
if *entry == CPU_UNMAPPED {
None
} else {
Some(*entry as u32)
}
}
}
}
#[cfg(all(target_os = "linux", not(miri), not(numa_shim_mock)))]
#[doc(hidden)]
pub mod linux {
use super::NodeResolution;
pub fn dbg_node_resolution_for_cpu(cpu: u32) -> NodeResolution {
match super::platform::cpu_to_numa_node_checked(cpu) {
Some(n) => NodeResolution::Resolved(n),
None => NodeResolution::TopologyUnavailable,
}
}
pub fn dbg_current_node_for_cpu(cpu: u32) -> Option<u32> {
let raw = super::platform::cpu_to_numa_node(cpu);
if raw == super::NO_NODE {
None
} else {
Some(raw)
}
}
}
#[doc(hidden)]
pub mod eintr {
pub const EINTR_RETRY_LIMIT: u32 = 16;
pub fn should_retry_eintr(err: &std::io::Error, consecutive_eintr: u32) -> bool {
err.kind() == std::io::ErrorKind::Interrupted && consecutive_eintr < EINTR_RETRY_LIMIT
}
}
#[cfg(all(target_os = "linux", not(miri)))]
#[cfg_attr(numa_shim_mock, allow(dead_code))]
mod platform {
#[cfg(all(
feature = "vmem-integration",
any(target_arch = "x86_64", target_arch = "aarch64")
))]
use super::mbind_preferred_linux;
#[cfg(feature = "vmem-integration")]
use super::{NodeId, ReserveNumaError};
use super::{NodeResolution, NO_NODE};
pub(super) fn current_node_impl() -> u32 {
let topo = topology();
let cpu = unsafe { libc_sched_getcpu() };
if cpu < 0 {
return NO_NODE;
}
topo.lookup(cpu as u32).unwrap_or(NO_NODE)
}
pub(super) fn current_node_resolution_impl() -> NodeResolution {
let topo = topology();
let cpu = unsafe { libc_sched_getcpu() };
if cpu < 0 {
return NodeResolution::Unavailable;
}
match topo.lookup(cpu as u32) {
Some(n) => NodeResolution::Resolved(n),
None => NodeResolution::TopologyUnavailable,
}
}
#[cfg(feature = "vmem-integration")]
pub(super) fn reserve_preferred_on_node_impl(
size: usize,
align: usize,
node: NodeId,
) -> Result<aligned_vmem::Reservation, ReserveNumaError> {
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
{
let raw_node = node.get();
if raw_node >= 64 {
return Err(ReserveNumaError::InvalidNode);
}
let r = aligned_vmem::try_reserve_aligned(size, align).map_err(|e| {
if e.is_invalid_argument() {
ReserveNumaError::InvalidArguments
} else {
ReserveNumaError::Os(std::io::Error::from(e))
}
})?;
let rc = unsafe {
mbind_preferred_linux(r.reservation_ptr(), r.reservation_len(), raw_node)
};
if rc == -1 {
let err = std::io::Error::last_os_error();
drop(r);
return Err(ReserveNumaError::Os(err));
}
Ok(r)
}
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
{
let _ = (size, align, node);
Err(ReserveNumaError::UnsupportedArchitecture)
}
}
const CPUMAP_READ_BUF_LEN: usize = 4096;
static TOPOLOGY: std::sync::OnceLock<crate::cpumap::ReverseIndex> = std::sync::OnceLock::new();
fn topology() -> &'static crate::cpumap::ReverseIndex {
TOPOLOGY.get_or_init(|| {
let mut index = crate::cpumap::ReverseIndex::new();
let mut buf = [0u8; CPUMAP_READ_BUF_LEN];
for node in 0u32..64 {
let mut path = [0u8; 64];
let path_str = crate::cpumap::format_sysfs_path(&mut path, node);
if let Some(n) = read_cpumap_into(path_str, &mut buf) {
index.index_node(node, &buf[..n]);
}
}
index
})
}
pub(crate) fn cpu_to_numa_node_checked(cpu_idx: u32) -> Option<u32> {
topology().lookup(cpu_idx)
}
pub(crate) fn cpu_to_numa_node(cpu_idx: u32) -> u32 {
cpu_to_numa_node_checked(cpu_idx).unwrap_or(NO_NODE)
}
#[cfg(not(any(target_arch = "sparc", target_arch = "sparc64")))]
const O_CLOEXEC: core::ffi::c_int = 0o2000000;
#[cfg(any(target_arch = "sparc", target_arch = "sparc64"))]
const O_CLOEXEC: core::ffi::c_int = 0x400000;
fn read_cpumap_into(path: &[u8], out: &mut [u8]) -> Option<usize> {
let mut open_eintr_streak = 0u32;
let fd = loop {
let fd = unsafe { libc_open(path.as_ptr() as *const core::ffi::c_char, O_CLOEXEC) };
if fd >= 0 {
break fd;
}
let err = std::io::Error::last_os_error();
if crate::eintr::should_retry_eintr(&err, open_eintr_streak) {
open_eintr_streak += 1;
continue;
}
return None;
};
let mut total = 0usize;
let mut read_eintr_streak = 0u32;
loop {
if total >= out.len() {
unsafe { libc_close(fd) };
return None;
}
let n = unsafe {
libc_read(
fd,
out[total..].as_mut_ptr() as *mut core::ffi::c_void,
out.len() - total,
)
};
if n < 0 {
let err = std::io::Error::last_os_error();
if crate::eintr::should_retry_eintr(&err, read_eintr_streak) {
read_eintr_streak += 1;
continue;
}
unsafe { libc_close(fd) };
return None;
}
if n == 0 {
break; }
total += n as usize;
read_eintr_streak = 0;
}
unsafe { libc_close(fd) };
if total == 0 {
return None;
}
Some(total)
}
extern "C" {
fn sched_getcpu() -> core::ffi::c_int;
fn open(path: *const core::ffi::c_char, flags: core::ffi::c_int, ...) -> core::ffi::c_int;
fn read(
fd: core::ffi::c_int,
buf: *mut core::ffi::c_void,
count: usize,
) -> core::ffi::c_long;
fn close(fd: core::ffi::c_int) -> core::ffi::c_int;
}
unsafe fn libc_sched_getcpu() -> core::ffi::c_int {
sched_getcpu()
}
unsafe fn libc_open(
path: *const core::ffi::c_char,
flags: core::ffi::c_int,
) -> core::ffi::c_int {
open(path, flags)
}
unsafe fn libc_read(
fd: core::ffi::c_int,
buf: *mut core::ffi::c_void,
count: usize,
) -> core::ffi::c_long {
read(fd, buf, count)
}
unsafe fn libc_close(fd: core::ffi::c_int) {
let _ = close(fd);
}
}
#[cfg(all(
target_os = "linux",
not(miri),
feature = "vmem-integration",
any(target_arch = "x86_64", target_arch = "aarch64")
))]
#[cfg_attr(numa_shim_mock, allow(dead_code))]
unsafe fn mbind_preferred_linux(base: *mut u8, len: usize, node: u32) -> i64 {
let nodemask: u64 = 1u64 << node;
let maxnode: u64 = 65;
libc_mbind(
base as *mut core::ffi::c_void,
len as u64,
MPOL_PREFERRED,
&nodemask as *const u64,
maxnode,
0,
)
}
#[cfg(all(target_os = "linux", not(miri), feature = "vmem-integration"))]
#[cfg_attr(numa_shim_mock, allow(dead_code))]
const MPOL_PREFERRED: i32 = 1;
#[cfg(all(
target_os = "linux",
not(miri),
feature = "vmem-integration",
target_arch = "x86_64"
))]
#[cfg_attr(numa_shim_mock, allow(dead_code))]
const SYS_MBIND: i64 = 237;
#[cfg(all(
target_os = "linux",
not(miri),
feature = "vmem-integration",
target_arch = "aarch64"
))]
#[cfg_attr(numa_shim_mock, allow(dead_code))]
const SYS_MBIND: i64 = 235;
#[cfg(all(
target_os = "linux",
not(miri),
feature = "vmem-integration",
any(target_arch = "x86_64", target_arch = "aarch64")
))]
extern "C" {
fn syscall(number: i64, ...) -> i64;
}
#[cfg(all(
target_os = "linux",
not(miri),
feature = "vmem-integration",
any(target_arch = "x86_64", target_arch = "aarch64")
))]
#[cfg_attr(numa_shim_mock, allow(dead_code))]
unsafe fn libc_mbind(
addr: *mut core::ffi::c_void,
len: u64,
mode: i32,
nodemask: *const u64,
maxnode: u64,
flags: u32,
) -> i64 {
syscall(
SYS_MBIND,
addr,
len as usize,
mode as i64,
nodemask,
maxnode as usize,
flags as i64,
)
}
#[cfg(all(windows, not(miri)))]
#[cfg_attr(numa_shim_mock, allow(dead_code))]
mod platform {
#[cfg(feature = "vmem-integration")]
use super::{NodeId, ReserveNumaError};
use super::{NodeResolution, NO_NODE};
pub(super) fn current_node_impl() -> u32 {
let mut proc_num = ProcessorNumber {
group: 0,
number: 0,
reserved: 0,
};
unsafe { GetCurrentProcessorNumberEx(&mut proc_num) };
let mut node: u16 = 0;
let ok = unsafe { GetNumaProcessorNodeEx(&proc_num, &mut node) };
if ok == 0 || node == u16::MAX {
return NO_NODE;
}
node as u32
}
pub(super) fn current_node_resolution_impl() -> NodeResolution {
let mut proc_num = ProcessorNumber {
group: 0,
number: 0,
reserved: 0,
};
unsafe { GetCurrentProcessorNumberEx(&mut proc_num) };
let mut node: u16 = 0;
let ok = unsafe { GetNumaProcessorNodeEx(&proc_num, &mut node) };
if ok == 0 || node == u16::MAX {
return NodeResolution::Unavailable;
}
NodeResolution::Resolved(node as u32)
}
#[cfg(feature = "vmem-integration")]
pub(super) fn reserve_preferred_on_node_impl(
size: usize,
align: usize,
node: NodeId,
) -> Result<aligned_vmem::Reservation, ReserveNumaError> {
reserve_aligned_numa(size, align, node.get())
}
#[cfg(feature = "vmem-integration")]
fn reserve_aligned_numa(
size: usize,
align: usize,
node: u32,
) -> Result<aligned_vmem::Reservation, ReserveNumaError> {
use aligned_vmem::PAGE;
if size == 0 || !align.is_power_of_two() || align < PAGE || !size.is_multiple_of(PAGE) {
return Err(ReserveNumaError::InvalidArguments);
}
let over = size
.checked_add(align)
.ok_or(ReserveNumaError::InvalidArguments)?;
let raw = unsafe {
VirtualAllocExNuma(
GetCurrentProcess(),
core::ptr::null_mut(),
over,
MEM_RESERVE,
PAGE_READWRITE,
node,
)
};
if raw.is_null() {
let err = std::io::Error::last_os_error();
return Err(ReserveNumaError::Os(err));
}
let raw_addr = raw.addr();
let Some(rounded) = raw_addr.checked_add(align - 1) else {
unsafe { VirtualFree(raw, 0, MEM_RELEASE) };
return Err(ReserveNumaError::InvalidArguments);
};
let base_addr = rounded & !(align - 1);
let base = raw.with_addr(base_addr).cast::<u8>();
let committed = unsafe {
VirtualAllocExNuma(
GetCurrentProcess(),
base.cast(),
size,
MEM_COMMIT,
PAGE_READWRITE,
node,
)
};
if committed.is_null() {
let err = std::io::Error::last_os_error();
let _ = unsafe { VirtualFree(raw, 0, MEM_RELEASE) };
return Err(ReserveNumaError::Os(err));
}
if committed.cast::<u8>() != base {
let _ = unsafe { VirtualFree(raw, 0, MEM_RELEASE) };
return Err(ReserveNumaError::Os(std::io::Error::other(
"VirtualAllocExNuma MEM_COMMIT returned an unexpected base — Win32 contract violation (task #1304)"
)));
}
let r = unsafe {
aligned_vmem::Reservation::from_raw_parts(
base,
size,
raw as *mut u8,
over,
align,
false, )
};
Ok(r)
}
#[repr(C)]
struct ProcessorNumber {
group: u16,
number: u8,
reserved: u8,
}
const _: () = {
assert!(core::mem::size_of::<ProcessorNumber>() == 4);
assert!(core::mem::align_of::<ProcessorNumber>() == 2);
assert!(core::mem::offset_of!(ProcessorNumber, group) == 0);
assert!(core::mem::offset_of!(ProcessorNumber, number) == 2);
assert!(core::mem::offset_of!(ProcessorNumber, reserved) == 3);
};
extern "system" {
fn GetCurrentProcessorNumberEx(proc_number: *mut ProcessorNumber);
fn GetNumaProcessorNodeEx(processor: *const ProcessorNumber, node_number: *mut u16) -> i32;
}
#[cfg(feature = "vmem-integration")]
extern "system" {
fn GetCurrentProcess() -> *mut core::ffi::c_void;
fn VirtualAllocExNuma(
h_process: *mut core::ffi::c_void,
lp_address: *mut core::ffi::c_void,
dw_size: usize,
fl_allocation_type: u32,
fl_protect: u32,
nnd_preferred: u32,
) -> *mut core::ffi::c_void;
fn VirtualFree(
lp_address: *mut core::ffi::c_void,
dw_size: usize,
dw_free_type: u32,
) -> i32;
}
#[cfg(feature = "vmem-integration")]
const MEM_RESERVE: u32 = 0x0000_2000;
#[cfg(feature = "vmem-integration")]
const MEM_COMMIT: u32 = 0x0000_1000;
#[cfg(feature = "vmem-integration")]
const MEM_RELEASE: u32 = 0x0000_8000;
#[cfg(feature = "vmem-integration")]
const PAGE_READWRITE: u32 = 0x04;
}
#[cfg(all(target_os = "macos", not(miri)))]
#[cfg_attr(numa_shim_mock, allow(dead_code))]
mod platform {
#[cfg(feature = "vmem-integration")]
use super::{NodeId, ReserveNumaError};
use super::{NodeResolution, NO_NODE};
pub(super) fn current_node_impl() -> u32 {
NO_NODE
}
pub(super) fn current_node_resolution_impl() -> NodeResolution {
NodeResolution::Unavailable
}
#[cfg(feature = "vmem-integration")]
pub(super) fn reserve_preferred_on_node_impl(
size: usize,
align: usize,
node: NodeId,
) -> Result<aligned_vmem::Reservation, ReserveNumaError> {
let _ = (size, align, node);
Err(ReserveNumaError::UnsupportedPlatform)
}
}
#[cfg(miri)]
#[cfg_attr(numa_shim_mock, allow(dead_code))]
mod platform {
#[cfg(feature = "vmem-integration")]
use super::{NodeId, ReserveNumaError};
use super::{NodeResolution, NO_NODE};
pub(super) fn current_node_impl() -> u32 {
NO_NODE
}
pub(super) fn current_node_resolution_impl() -> NodeResolution {
NodeResolution::Unavailable
}
#[cfg(feature = "vmem-integration")]
pub(super) fn reserve_preferred_on_node_impl(
size: usize,
align: usize,
node: NodeId,
) -> Result<aligned_vmem::Reservation, ReserveNumaError> {
let _ = (size, align, node);
Err(ReserveNumaError::UnsupportedPlatform)
}
}
#[cfg(not(any(target_os = "linux", windows, target_os = "macos", miri,)))]
#[cfg_attr(numa_shim_mock, allow(dead_code))]
mod platform {
#[cfg(feature = "vmem-integration")]
use super::{NodeId, ReserveNumaError};
use super::{NodeResolution, NO_NODE};
pub(super) fn current_node_impl() -> u32 {
NO_NODE
}
pub(super) fn current_node_resolution_impl() -> NodeResolution {
NodeResolution::Unavailable
}
#[cfg(feature = "vmem-integration")]
pub(super) fn reserve_preferred_on_node_impl(
size: usize,
align: usize,
node: NodeId,
) -> Result<aligned_vmem::Reservation, ReserveNumaError> {
let _ = (size, align, node);
Err(ReserveNumaError::UnsupportedPlatform)
}
}