use std::sync::OnceLock;
use std::sync::atomic::{AtomicU32, Ordering};
use crate::ordering::read_tsc;
pub const DEFAULT_MONITOR_BUDGET_CYCLES: u64 = 90_000;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MonitorWaitKind {
Waitpkg,
Mwaitx,
ArmWfe,
}
struct MonitorConfig {
kind: Option<MonitorWaitKind>,
budget_cycles: u64,
}
fn config() -> &'static MonitorConfig {
static CONFIG: OnceLock<MonitorConfig> = OnceLock::new();
CONFIG.get_or_init(|| {
let disabled = std::env::var_os("SUBETHA_NO_MONITOR_WAIT")
.is_some_and(|v| v == "1");
let budget_cycles = std::env::var("SUBETHA_MONITOR_WAIT_CYCLES")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or_else(default_budget_cycles);
MonitorConfig {
kind: if disabled { None } else { detect_kind() },
budget_cycles,
}
})
}
pub fn monitor_wait_kind() -> Option<MonitorWaitKind> {
config().kind
}
pub fn monitor_wait_budget_cycles() -> u64 {
config().budget_cycles
}
#[cfg(target_arch = "x86_64")]
fn detect_kind() -> Option<MonitorWaitKind> {
use core::arch::x86_64::__cpuid;
let max_basic = core::arch::x86_64::__cpuid_count(0, 0).eax;
if max_basic >= 7 {
let leaf7 = core::arch::x86_64::__cpuid_count(7, 0);
if leaf7.ecx & (1 << 5) != 0 {
return Some(MonitorWaitKind::Waitpkg);
}
}
let max_extended = __cpuid(0x8000_0000).eax;
if max_extended >= 0x8000_0001 {
let ext1 = __cpuid(0x8000_0001);
if ext1.ecx & (1 << 29) != 0 {
return Some(MonitorWaitKind::Mwaitx);
}
}
None
}
#[cfg(target_arch = "aarch64")]
fn detect_kind() -> Option<MonitorWaitKind> {
Some(MonitorWaitKind::ArmWfe)
}
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
fn detect_kind() -> Option<MonitorWaitKind> {
None
}
#[cfg(not(target_arch = "aarch64"))]
fn default_budget_cycles() -> u64 {
DEFAULT_MONITOR_BUDGET_CYCLES
}
#[cfg(target_arch = "aarch64")]
fn default_budget_cycles() -> u64 {
(crate::ordering::counter_frequency_hz() * 28 / 1_000_000).max(64)
}
#[inline]
pub fn monitor_wait_u32(atomic: &AtomicU32, expected: u32, budget_cycles: u64) -> bool {
let Some(kind) = monitor_wait_kind() else {
return false;
};
monitor_wait_u32_with(kind, atomic, expected, budget_cycles)
}
#[cfg(target_arch = "x86_64")]
pub fn monitor_wait_u32_with(
kind: MonitorWaitKind,
atomic: &AtomicU32,
expected: u32,
budget_cycles: u64,
) -> bool {
let deadline = read_tsc().wrapping_add(budget_cycles);
let addr = atomic.as_ptr() as *const u8;
loop {
unsafe {
match kind {
MonitorWaitKind::Waitpkg => umonitor(addr),
MonitorWaitKind::Mwaitx => monitorx(addr),
MonitorWaitKind::ArmWfe => return false,
}
}
if atomic.load(Ordering::Acquire) != expected {
return true;
}
let now = read_tsc();
let remaining = deadline.wrapping_sub(now);
if remaining == 0 || remaining > i64::MAX as u64 {
return atomic.load(Ordering::Acquire) != expected;
}
unsafe {
match kind {
MonitorWaitKind::Waitpkg => umwait(deadline),
MonitorWaitKind::Mwaitx => {
mwaitx(remaining.min(u32::MAX as u64) as u32)
}
MonitorWaitKind::ArmWfe => return false,
}
}
if atomic.load(Ordering::Acquire) != expected {
return true;
}
if read_tsc().wrapping_sub(deadline) <= i64::MAX as u64 {
return atomic.load(Ordering::Acquire) != expected;
}
}
}
#[cfg(target_arch = "aarch64")]
pub fn monitor_wait_u32_with(
kind: MonitorWaitKind,
atomic: &AtomicU32,
expected: u32,
budget_cycles: u64,
) -> bool {
if kind != MonitorWaitKind::ArmWfe {
return false;
}
let deadline = read_tsc().wrapping_add(budget_cycles);
let addr = atomic.as_ptr();
loop {
let cur: u32;
unsafe {
core::arch::asm!(
"ldaxr {v:w}, [{a}]",
v = out(reg) cur,
a = in(reg) addr,
options(nostack, preserves_flags),
);
}
if cur != expected {
unsafe {
core::arch::asm!("clrex", options(nomem, nostack, preserves_flags));
}
return true;
}
let now = read_tsc();
if deadline.wrapping_sub(now) > i64::MAX as u64
|| deadline == now
{
unsafe {
core::arch::asm!("clrex", options(nomem, nostack, preserves_flags));
}
return atomic.load(Ordering::Acquire) != expected;
}
unsafe {
core::arch::asm!("wfe", options(nomem, nostack, preserves_flags));
}
if atomic.load(Ordering::Acquire) != expected {
return true;
}
}
}
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
pub fn monitor_wait_u32_with(
_kind: MonitorWaitKind,
_atomic: &AtomicU32,
_expected: u32,
_budget_cycles: u64,
) -> bool {
false
}
#[inline]
pub fn monitor_wait_u64(
atomic: &std::sync::atomic::AtomicU64,
expected: u64,
budget_cycles: u64,
) -> bool {
let Some(kind) = monitor_wait_kind() else {
return false;
};
monitor_wait_u64_with(kind, atomic, expected, budget_cycles)
}
#[cfg(target_arch = "x86_64")]
pub fn monitor_wait_u64_with(
kind: MonitorWaitKind,
atomic: &std::sync::atomic::AtomicU64,
expected: u64,
budget_cycles: u64,
) -> bool {
let deadline = read_tsc().wrapping_add(budget_cycles);
let addr = atomic.as_ptr() as *const u8;
loop {
unsafe {
match kind {
MonitorWaitKind::Waitpkg => umonitor(addr),
MonitorWaitKind::Mwaitx => monitorx(addr),
MonitorWaitKind::ArmWfe => return false,
}
}
if atomic.load(Ordering::Acquire) != expected {
return true;
}
let now = read_tsc();
let remaining = deadline.wrapping_sub(now);
if remaining == 0 || remaining > i64::MAX as u64 {
return atomic.load(Ordering::Acquire) != expected;
}
unsafe {
match kind {
MonitorWaitKind::Waitpkg => umwait(deadline),
MonitorWaitKind::Mwaitx => {
mwaitx(remaining.min(u32::MAX as u64) as u32)
}
MonitorWaitKind::ArmWfe => return false,
}
}
if atomic.load(Ordering::Acquire) != expected {
return true;
}
if read_tsc().wrapping_sub(deadline) <= i64::MAX as u64 {
return atomic.load(Ordering::Acquire) != expected;
}
}
}
#[cfg(target_arch = "aarch64")]
pub fn monitor_wait_u64_with(
kind: MonitorWaitKind,
atomic: &std::sync::atomic::AtomicU64,
expected: u64,
budget_cycles: u64,
) -> bool {
if kind != MonitorWaitKind::ArmWfe {
return false;
}
let deadline = read_tsc().wrapping_add(budget_cycles);
let addr = atomic.as_ptr();
loop {
let cur: u64;
unsafe {
core::arch::asm!(
"ldaxr {v}, [{a}]",
v = out(reg) cur,
a = in(reg) addr,
options(nostack, preserves_flags),
);
}
if cur != expected {
unsafe {
core::arch::asm!("clrex", options(nomem, nostack, preserves_flags));
}
return true;
}
let now = read_tsc();
if deadline.wrapping_sub(now) > i64::MAX as u64 || deadline == now {
unsafe {
core::arch::asm!("clrex", options(nomem, nostack, preserves_flags));
}
return atomic.load(Ordering::Acquire) != expected;
}
unsafe {
core::arch::asm!("wfe", options(nomem, nostack, preserves_flags));
}
if atomic.load(Ordering::Acquire) != expected {
return true;
}
}
}
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
pub fn monitor_wait_u64_with(
_kind: MonitorWaitKind,
_atomic: &std::sync::atomic::AtomicU64,
_expected: u64,
_budget_cycles: u64,
) -> bool {
false
}
#[cfg(target_arch = "x86_64")]
#[inline]
unsafe fn monitorx(addr: *const u8) {
unsafe {
core::arch::asm!(
"monitorx",
in("rax") addr,
in("ecx") 0u32,
in("edx") 0u32,
options(nostack, preserves_flags),
);
}
}
#[cfg(target_arch = "x86_64")]
#[inline]
unsafe fn mwaitx(max_cycles: u32) {
unsafe {
core::arch::asm!(
"xchg {scratch}, rbx",
"mwaitx",
"xchg {scratch}, rbx",
scratch = inout(reg) max_cycles as u64 => _,
in("eax") 0u32,
in("ecx") 2u32,
options(nostack, preserves_flags),
);
}
}
#[cfg(target_arch = "x86_64")]
#[inline]
unsafe fn umonitor(addr: *const u8) {
unsafe {
core::arch::asm!(
"umonitor {addr}",
addr = in(reg) addr,
options(nostack, preserves_flags),
);
}
}
#[cfg(target_arch = "x86_64")]
#[inline]
unsafe fn umwait(deadline_tsc: u64) {
let lo = deadline_tsc as u32;
let hi = (deadline_tsc >> 32) as u32;
unsafe {
core::arch::asm!(
"umwait {ctl:e}",
ctl = in(reg) 1u32,
in("eax") lo,
in("edx") hi,
options(nostack),
);
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;
use std::time::{Duration, Instant};
#[test]
fn detection_runs_and_is_cached() {
let first = monitor_wait_kind();
let second = monitor_wait_kind();
assert_eq!(first, second, "probe must be stable across calls");
println!("monitor-wait kind: {first:?}, budget {} cycles",
monitor_wait_budget_cycles());
}
#[test]
fn returns_immediately_when_value_already_differs() {
let atomic = AtomicU32::new(7);
let t0 = Instant::now();
let changed = monitor_wait_u32(&atomic, 5, 500_000_000);
let elapsed = t0.elapsed();
if monitor_wait_kind().is_some() {
assert!(changed, "value != expected must report changed");
} else {
assert!(!changed, "unsupported tier reports false");
}
assert!(elapsed < Duration::from_millis(200),
"must not consume the whole budget: {elapsed:?}");
}
#[test]
fn budget_expiry_returns_false_when_nothing_stores() {
if monitor_wait_kind().is_none() {
return;
}
let atomic = AtomicU32::new(1);
let t0 = Instant::now();
let changed = monitor_wait_u32(&atomic, 1, 30_000_000);
let elapsed = t0.elapsed();
assert!(!changed, "no store happened; must report expiry");
assert!(elapsed >= Duration::from_micros(500),
"expiry must actually wait, got {elapsed:?}");
assert!(elapsed < Duration::from_secs(2),
"expiry must be bounded, got {elapsed:?}");
}
#[test]
fn cross_thread_store_wakes_the_waiter() {
if monitor_wait_kind().is_none() {
return;
}
let atomic = Arc::new(AtomicU32::new(0));
let waker_side = Arc::clone(&atomic);
let h = std::thread::spawn(move || {
std::thread::sleep(Duration::from_millis(5));
waker_side.store(1, Ordering::Release);
});
let t0 = Instant::now();
let changed = monitor_wait_u32(&atomic, 0, 9_000_000_000);
let elapsed = t0.elapsed();
h.join().expect("storer thread");
assert!(changed, "store must wake the monitor waiter");
assert!(elapsed < Duration::from_millis(500),
"wake must arrive promptly, got {elapsed:?}");
}
}