use core::{
ptr,
sync::atomic::{AtomicPtr, AtomicUsize, Ordering},
};
use std::time::Instant;
mod bindings;
mod keyed_event;
mod waitaddress;
enum Backend {
KeyedEvent(keyed_event::KeyedEvent),
WaitAddress(waitaddress::WaitAddress),
}
static BACKEND: AtomicPtr<Backend> = AtomicPtr::new(ptr::null_mut());
impl Backend {
#[inline]
fn get() -> &'static Backend {
let backend_ptr = BACKEND.load(Ordering::Acquire);
if !backend_ptr.is_null() {
return unsafe { &*backend_ptr };
};
Backend::create()
}
#[cold]
fn create() -> &'static Backend {
let backend;
if let Some(waitaddress) = waitaddress::WaitAddress::create() {
backend = Backend::WaitAddress(waitaddress);
} else if let Some(keyed_event) = keyed_event::KeyedEvent::create() {
backend = Backend::KeyedEvent(keyed_event);
} else {
panic!(
"parking_lot requires either NT Keyed Events (WinXP+) or \
WaitOnAddress/WakeByAddress (Win8+)"
);
}
let backend_ptr = Box::into_raw(Box::new(backend));
match BACKEND.compare_exchange(
ptr::null_mut(),
backend_ptr,
Ordering::Release,
Ordering::Acquire,
) {
Ok(_) => unsafe { &*backend_ptr },
Err(global_backend_ptr) => {
unsafe {
let _ = Box::from_raw(backend_ptr);
&*global_backend_ptr
}
}
}
}
}
pub struct ThreadParker {
key: AtomicUsize,
backend: &'static Backend,
}
impl super::ThreadParkerT for ThreadParker {
type UnparkHandle = UnparkHandle;
const IS_CHEAP_TO_CONSTRUCT: bool = true;
#[inline]
fn new() -> ThreadParker {
ThreadParker {
key: AtomicUsize::new(0),
backend: Backend::get(),
}
}
#[inline]
unsafe fn prepare_park(&self) {
match *self.backend {
Backend::KeyedEvent(ref x) => x.prepare_park(&self.key),
Backend::WaitAddress(ref x) => x.prepare_park(&self.key),
}
}
#[inline]
unsafe fn timed_out(&self) -> bool {
match *self.backend {
Backend::KeyedEvent(ref x) => x.timed_out(&self.key),
Backend::WaitAddress(ref x) => x.timed_out(&self.key),
}
}
#[inline]
unsafe fn park(&self) {
match *self.backend {
Backend::KeyedEvent(ref x) => x.park(&self.key),
Backend::WaitAddress(ref x) => x.park(&self.key),
}
}
#[inline]
unsafe fn park_until(&self, timeout: Instant) -> bool {
match *self.backend {
Backend::KeyedEvent(ref x) => x.park_until(&self.key, timeout),
Backend::WaitAddress(ref x) => x.park_until(&self.key, timeout),
}
}
#[inline]
unsafe fn unpark_lock(&self) -> UnparkHandle {
match *self.backend {
Backend::KeyedEvent(ref x) => UnparkHandle::KeyedEvent(x.unpark_lock(&self.key)),
Backend::WaitAddress(ref x) => UnparkHandle::WaitAddress(x.unpark_lock(&self.key)),
}
}
}
pub enum UnparkHandle {
KeyedEvent(keyed_event::UnparkHandle),
WaitAddress(waitaddress::UnparkHandle),
}
impl super::UnparkHandleT for UnparkHandle {
#[inline]
unsafe fn unpark(self) {
match self {
UnparkHandle::KeyedEvent(x) => x.unpark(),
UnparkHandle::WaitAddress(x) => x.unpark(),
}
}
}
#[inline]
pub fn thread_yield() {
unsafe {
bindings::Sleep(0);
}
}