| //! 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<Inner> 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<bool>, |
| } |
| |
| #[derive(Clone, Copy)] |
| enum Kind { |
| SRWLock, |
| CriticalSection, |
| } |
| |
| #[inline] |
| pub unsafe fn raw(m: &Mutex) -> c::PSRWLOCK { |
| debug_assert!(mem::size_of::<c::SRWLOCK>() <= 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<UnsafeCell<c::CRITICAL_SECTION>>, |
| } |
| |
| 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())); |
| } |
| } |