use core::ptr;
#[cfg(any(
target_os = "linux",
target_os = "android",
target_os = "freebsd",
target_os = "macos",
target_os = "ios",
target_os = "watchos",
windows
))]
pub use super::atomic_wait::AtomicParker;
#[cfg(feature = "parking_lot")]
pub use super::parking_lot::ParkingLotParker;
#[cfg(all(feature = "pthread", unix))]
pub use super::pthread::PthreadParker;
pub use super::spin::SpinParker;
#[cfg(feature = "std")]
pub use super::std::StdParker;
use crate::sync::{condvar::CondVar, mutex::Mutex};
pub trait Parker: Send + Sync + 'static {
const NEVER_BLOCKS: bool = false;
const INIT: Self;
#[doc(hidden)]
fn new() -> Self
where
Self: Sized,
{
Self::INIT
}
fn parked_state(&self) -> *mut () {
ptr::null_mut()
}
unsafe fn park_until<T>(&self, notified: impl FnMut() -> Option<T>) -> T;
unsafe fn unpark(&self, parked_state: *mut ());
}
#[derive(Debug)]
pub struct CondVarParker<M: Mutex, C: CondVar<M>, const NOTIFY_WITH_MUTEX_ACQUIRED: bool> {
mutex: M,
condvar: C,
}
impl<M: Mutex, C: CondVar<M>, const NOTIFY_WITH_MUTEX_ACQUIRED: bool> Parker
for CondVarParker<M, C, NOTIFY_WITH_MUTEX_ACQUIRED>
{
#[cfg(not(loom))]
#[allow(clippy::declare_interior_mutable_const)]
const INIT: Self = Self {
mutex: M::INIT,
condvar: C::INIT,
};
#[cfg(loom)]
const INIT: Self = unimplemented!();
fn new() -> Self {
Self {
mutex: M::new(),
condvar: C::new(),
}
}
#[inline]
unsafe fn park_until<T>(&self, mut notified: impl FnMut() -> Option<T>) -> T {
let mut guard = self.mutex.lock();
loop {
if let Some(res) = notified() {
unsafe { self.mutex.unlock(guard) };
return res;
}
guard = unsafe { self.condvar.wait(&self.mutex, guard) };
}
}
#[inline]
unsafe fn unpark(&self, _parked_state: *mut ()) {
let lock = self.mutex.lock();
if NOTIFY_WITH_MUTEX_ACQUIRED {
self.condvar.notify_one();
}
unsafe { self.mutex.unlock(lock) };
if !NOTIFY_WITH_MUTEX_ACQUIRED {
self.condvar.notify_one();
}
}
}
cfg_if::cfg_if! {
if #[cfg(any(loom, miri))] {
type DefaultParkerImpl = StdParker;
} else if #[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd", target_os = "macos", target_os = "ios", target_os = "watchos", windows))] {
type DefaultParkerImpl = AtomicParker;
} else if #[cfg(feature = "parking_lot")] {
type DefaultParkerImpl = ParkingLotParker;
} else if #[cfg(feature = "std")] {
type DefaultParkerImpl = StdParker;
} else if #[cfg(all(feature = "pthread", unix))] {
type DefaultParkerImpl = PthreadParker;
} else {
type DefaultParkerImpl = SpinParker;
}
}
pub type DefaultParker = DefaultParkerImpl;