use std::time::Duration;
#[cfg(not(loom))]
pub use parking_lot::{Condvar, Mutex};
#[cfg(not(loom))]
pub use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize};
#[cfg(not(loom))]
pub use std::sync::Arc;
#[cfg(loom)]
pub use loom::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize};
#[cfg(loom)]
pub use loom::sync::Arc;
#[cfg(loom)]
pub use shim::{Condvar, Mutex};
#[cfg(loom)]
mod shim {
use std::ops::{Deref, DerefMut};
pub struct Mutex<T>(loom::sync::Mutex<T>);
pub struct MutexGuard<'a, T>(Option<loom::sync::MutexGuard<'a, T>>);
impl<T> Mutex<T> {
pub fn new(value: T) -> Self {
Mutex(loom::sync::Mutex::new(value))
}
pub fn lock(&self) -> MutexGuard<'_, T> {
MutexGuard(Some(self.0.lock().expect("not poisoned")))
}
}
impl<T> Deref for MutexGuard<'_, T> {
type Target = T;
fn deref(&self) -> &T {
self.0.as_ref().expect("held")
}
}
impl<T> DerefMut for MutexGuard<'_, T> {
fn deref_mut(&mut self) -> &mut T {
self.0.as_mut().expect("held")
}
}
pub struct Condvar(loom::sync::Condvar);
impl Condvar {
pub fn new() -> Self {
Condvar(loom::sync::Condvar::new())
}
pub fn wait<T>(&self, guard: &mut MutexGuard<'_, T>) {
let held = guard.0.take().expect("held");
guard.0 = Some(self.0.wait(held).expect("not poisoned"));
}
pub fn notify_all(&self) {
self.0.notify_all();
}
}
}
const SPIN: Duration = Duration::from_micros(50);
#[cfg(not(loom))]
pub fn spin_until(done: impl Fn() -> bool) -> bool {
use std::sync::atomic::Ordering;
use std::sync::OnceLock;
use std::time::Instant;
static SPINNERS: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
static CORES: OnceLock<usize> = OnceLock::new();
let cores = *CORES.get_or_init(|| std::thread::available_parallelism().map_or(4, |n| n.get()));
if SPINNERS.fetch_add(1, Ordering::Relaxed) >= cores {
SPINNERS.fetch_sub(1, Ordering::Relaxed);
return done();
}
let spun = (|| {
for _ in 0..64 {
if done() {
return true;
}
std::hint::spin_loop();
}
let start = Instant::now();
while start.elapsed() < SPIN {
if done() {
return true;
}
std::thread::yield_now();
}
done()
})();
SPINNERS.fetch_sub(1, Ordering::Relaxed);
spun
}
#[cfg(loom)]
pub fn spin_until(done: impl Fn() -> bool) -> bool {
let _ = SPIN;
done()
}