//! System Mutexes //! //! The Windows implementation of mutexes is a little odd and it may not be //! immediately obvious what's going on. The primary oddness is that SRWLock is //! used instead of CriticalSection, and this is done because: //! //! 1. SRWLock is several times faster than CriticalSection according to //! benchmarks performed on both Windows 8 and Windows 7. //! //! 2. CriticalSection allows recursive locking while SRWLock deadlocks. The //! Unix implementation deadlocks so consistency is preferred. See #19962 for //! more details. //! //! 3. While CriticalSection is fair and SRWLock is not, the current Rust policy //! is that there are no guarantees of fairness. //! //! The downside of this approach, however, is that SRWLock is not available on //! Windows XP, so we continue to have a fallback implementation where //! CriticalSection is used and we keep track of who's holding the mutex to //! detect recursive locks. use crate::cell::{Cell, UnsafeCell}; use crate::mem::{self, MaybeUninit}; use crate::sync::atomic::{AtomicUsize, Ordering}; use crate::sys::c; pub struct Mutex { // This is either directly an SRWLOCK (if supported), or a Box otherwise. lock: AtomicUsize, } // Windows SRW Locks are movable (while not borrowed). // ReentrantMutexes (in Inner) are not, but those are stored indirectly through // a Box, so do not move when the Mutex it self is moved. pub type MovableMutex = Mutex; unsafe impl Send for Mutex {} unsafe impl Sync for Mutex {} struct Inner { remutex: ReentrantMutex, held: Cell, } #[derive(Clone, Copy)] enum Kind { SRWLock, CriticalSection, } #[inline] pub unsafe fn raw(m: &Mutex) -> c::PSRWLOCK { debug_assert!(mem::size_of::() <= mem::size_of_val(&m.lock)); &m.lock as *const _ as *mut _ } impl Mutex { pub const fn new() -> Mutex { Mutex { // This works because SRWLOCK_INIT is 0 (wrapped in a struct), so we are also properly // initializing an SRWLOCK here. lock: AtomicUsize::new(0), } } #[inline] pub unsafe fn init(&mut self) {} pub unsafe fn lock(&self) { match kind() { Kind::SRWLock => c::AcquireSRWLockExclusive(raw(self)), Kind::CriticalSection => { let inner = &*self.inner(); inner.remutex.lock(); if inner.held.replace(true) { // It was already locked, so we got a recursive lock which we do not want. inner.remutex.unlock(); panic!("cannot recursively lock a mutex"); } } } } pub unsafe fn try_lock(&self) -> bool { match kind() { Kind::SRWLock => c::TryAcquireSRWLockExclusive(raw(self)) != 0, Kind::CriticalSection => { let inner = &*self.inner(); if !inner.remutex.try_lock() { false } else if inner.held.replace(true) { // It was already locked, so we got a recursive lock which we do not want. inner.remutex.unlock(); false } else { true } } } } pub unsafe fn unlock(&self) { match kind() { Kind::SRWLock => c::ReleaseSRWLockExclusive(raw(self)), Kind::CriticalSection => { let inner = &*(self.lock.load(Ordering::SeqCst) as *const Inner); inner.held.set(false); inner.remutex.unlock(); } } } pub unsafe fn destroy(&self) { match kind() { Kind::SRWLock => {} Kind::CriticalSection => match self.lock.load(Ordering::SeqCst) { 0 => {} n => Box::from_raw(n as *mut Inner).remutex.destroy(), }, } } unsafe fn inner(&self) -> *const Inner { match self.lock.load(Ordering::SeqCst) { 0 => {} n => return n as *const _, } let inner = box Inner { remutex: ReentrantMutex::uninitialized(), held: Cell::new(false) }; inner.remutex.init(); let inner = Box::into_raw(inner); match self.lock.compare_and_swap(0, inner as usize, Ordering::SeqCst) { 0 => inner, n => { Box::from_raw(inner).remutex.destroy(); n as *const _ } } } } fn kind() -> Kind { if c::AcquireSRWLockExclusive::is_available() { Kind::SRWLock } else { Kind::CriticalSection } } pub struct ReentrantMutex { inner: MaybeUninit>, } unsafe impl Send for ReentrantMutex {} unsafe impl Sync for ReentrantMutex {} impl ReentrantMutex { pub const fn uninitialized() -> ReentrantMutex { ReentrantMutex { inner: MaybeUninit::uninit() } } pub unsafe fn init(&self) { c::InitializeCriticalSection(UnsafeCell::raw_get(self.inner.as_ptr())); } pub unsafe fn lock(&self) { c::EnterCriticalSection(UnsafeCell::raw_get(self.inner.as_ptr())); } #[inline] pub unsafe fn try_lock(&self) -> bool { c::TryEnterCriticalSection(UnsafeCell::raw_get(self.inner.as_ptr())) != 0 } pub unsafe fn unlock(&self) { c::LeaveCriticalSection(UnsafeCell::raw_get(self.inner.as_ptr())); } pub unsafe fn destroy(&self) { c::DeleteCriticalSection(UnsafeCell::raw_get(self.inner.as_ptr())); } }