From ac6996345de2cc482cb164335336b9af1b03a320 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Thu, 17 Mar 2022 12:28:31 +0100 Subject: Move pthread locks to own module. --- library/std/src/sys/unix/condvar.rs | 197 ---------------------- library/std/src/sys/unix/locks/mod.rs | 8 + library/std/src/sys/unix/locks/pthread_condvar.rs | 197 ++++++++++++++++++++++ library/std/src/sys/unix/locks/pthread_mutex.rs | 102 +++++++++++ library/std/src/sys/unix/locks/pthread_remutex.rs | 46 +++++ library/std/src/sys/unix/locks/pthread_rwlock.rs | 143 ++++++++++++++++ library/std/src/sys/unix/mod.rs | 4 +- library/std/src/sys/unix/mutex.rs | 144 ---------------- library/std/src/sys/unix/rwlock.rs | 143 ---------------- library/std/src/sys_common/condvar.rs | 5 +- library/std/src/sys_common/condvar/check.rs | 8 +- library/std/src/sys_common/mutex.rs | 2 +- library/std/src/sys_common/remutex.rs | 2 +- library/std/src/sys_common/rwlock.rs | 2 +- 14 files changed, 506 insertions(+), 497 deletions(-) delete mode 100644 library/std/src/sys/unix/condvar.rs create mode 100644 library/std/src/sys/unix/locks/mod.rs create mode 100644 library/std/src/sys/unix/locks/pthread_condvar.rs create mode 100644 library/std/src/sys/unix/locks/pthread_mutex.rs create mode 100644 library/std/src/sys/unix/locks/pthread_remutex.rs create mode 100644 library/std/src/sys/unix/locks/pthread_rwlock.rs delete mode 100644 library/std/src/sys/unix/mutex.rs delete mode 100644 library/std/src/sys/unix/rwlock.rs (limited to 'library') diff --git a/library/std/src/sys/unix/condvar.rs b/library/std/src/sys/unix/condvar.rs deleted file mode 100644 index 61261c0aa84..00000000000 --- a/library/std/src/sys/unix/condvar.rs +++ /dev/null @@ -1,197 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::sys::mutex::{self, Mutex}; -use crate::time::Duration; - -pub struct Condvar { - inner: UnsafeCell, -} - -pub type MovableCondvar = Box; - -unsafe impl Send for Condvar {} -unsafe impl Sync for Condvar {} - -const TIMESPEC_MAX: libc::timespec = - libc::timespec { tv_sec: ::MAX, tv_nsec: 1_000_000_000 - 1 }; - -fn saturating_cast_to_time_t(value: u64) -> libc::time_t { - if value > ::MAX as u64 { ::MAX } else { value as libc::time_t } -} - -impl Condvar { - pub const fn new() -> Condvar { - // Might be moved and address is changing it is better to avoid - // initialization of potentially opaque OS data before it landed - Condvar { inner: UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER) } - } - - #[cfg(any( - target_os = "macos", - target_os = "ios", - target_os = "l4re", - target_os = "android", - target_os = "redox" - ))] - pub unsafe fn init(&mut self) {} - - // NOTE: ESP-IDF's PTHREAD_COND_INITIALIZER support is not released yet - // So on that platform, init() should always be called - // Moreover, that platform does not have pthread_condattr_setclock support, - // hence that initialization should be skipped as well - #[cfg(target_os = "espidf")] - pub unsafe fn init(&mut self) { - let r = libc::pthread_cond_init(self.inner.get(), crate::ptr::null()); - assert_eq!(r, 0); - } - - #[cfg(not(any( - target_os = "macos", - target_os = "ios", - target_os = "l4re", - target_os = "android", - target_os = "redox", - target_os = "espidf" - )))] - pub unsafe fn init(&mut self) { - use crate::mem::MaybeUninit; - let mut attr = MaybeUninit::::uninit(); - let r = libc::pthread_condattr_init(attr.as_mut_ptr()); - assert_eq!(r, 0); - let r = libc::pthread_condattr_setclock(attr.as_mut_ptr(), libc::CLOCK_MONOTONIC); - assert_eq!(r, 0); - let r = libc::pthread_cond_init(self.inner.get(), attr.as_ptr()); - assert_eq!(r, 0); - let r = libc::pthread_condattr_destroy(attr.as_mut_ptr()); - assert_eq!(r, 0); - } - - #[inline] - pub unsafe fn notify_one(&self) { - let r = libc::pthread_cond_signal(self.inner.get()); - debug_assert_eq!(r, 0); - } - - #[inline] - pub unsafe fn notify_all(&self) { - let r = libc::pthread_cond_broadcast(self.inner.get()); - debug_assert_eq!(r, 0); - } - - #[inline] - pub unsafe fn wait(&self, mutex: &Mutex) { - let r = libc::pthread_cond_wait(self.inner.get(), mutex::raw(mutex)); - debug_assert_eq!(r, 0); - } - - // This implementation is used on systems that support pthread_condattr_setclock - // where we configure condition variable to use monotonic clock (instead of - // default system clock). This approach avoids all problems that result - // from changes made to the system time. - #[cfg(not(any( - target_os = "macos", - target_os = "ios", - target_os = "android", - target_os = "espidf" - )))] - pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { - use crate::mem; - - let mut now: libc::timespec = mem::zeroed(); - let r = libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut now); - assert_eq!(r, 0); - - // Nanosecond calculations can't overflow because both values are below 1e9. - let nsec = dur.subsec_nanos() + now.tv_nsec as u32; - - let sec = saturating_cast_to_time_t(dur.as_secs()) - .checked_add((nsec / 1_000_000_000) as libc::time_t) - .and_then(|s| s.checked_add(now.tv_sec)); - let nsec = nsec % 1_000_000_000; - - let timeout = - sec.map(|s| libc::timespec { tv_sec: s, tv_nsec: nsec as _ }).unwrap_or(TIMESPEC_MAX); - - let r = libc::pthread_cond_timedwait(self.inner.get(), mutex::raw(mutex), &timeout); - assert!(r == libc::ETIMEDOUT || r == 0); - r == 0 - } - - // This implementation is modeled after libcxx's condition_variable - // https://github.com/llvm-mirror/libcxx/blob/release_35/src/condition_variable.cpp#L46 - // https://github.com/llvm-mirror/libcxx/blob/release_35/include/__mutex_base#L367 - #[cfg(any( - target_os = "macos", - target_os = "ios", - target_os = "android", - target_os = "espidf" - ))] - pub unsafe fn wait_timeout(&self, mutex: &Mutex, mut dur: Duration) -> bool { - use crate::ptr; - use crate::time::Instant; - - // 1000 years - let max_dur = Duration::from_secs(1000 * 365 * 86400); - - if dur > max_dur { - // OSX implementation of `pthread_cond_timedwait` is buggy - // with super long durations. When duration is greater than - // 0x100_0000_0000_0000 seconds, `pthread_cond_timedwait` - // in macOS Sierra return error 316. - // - // This program demonstrates the issue: - // https://gist.github.com/stepancheg/198db4623a20aad2ad7cddb8fda4a63c - // - // To work around this issue, and possible bugs of other OSes, timeout - // is clamped to 1000 years, which is allowable per the API of `wait_timeout` - // because of spurious wakeups. - - dur = max_dur; - } - - // First, figure out what time it currently is, in both system and - // stable time. pthread_cond_timedwait uses system time, but we want to - // report timeout based on stable time. - let mut sys_now = libc::timeval { tv_sec: 0, tv_usec: 0 }; - let stable_now = Instant::now(); - let r = libc::gettimeofday(&mut sys_now, ptr::null_mut()); - debug_assert_eq!(r, 0); - - let nsec = dur.subsec_nanos() as libc::c_long + (sys_now.tv_usec * 1000) as libc::c_long; - let extra = (nsec / 1_000_000_000) as libc::time_t; - let nsec = nsec % 1_000_000_000; - let seconds = saturating_cast_to_time_t(dur.as_secs()); - - let timeout = sys_now - .tv_sec - .checked_add(extra) - .and_then(|s| s.checked_add(seconds)) - .map(|s| libc::timespec { tv_sec: s, tv_nsec: nsec }) - .unwrap_or(TIMESPEC_MAX); - - // And wait! - let r = libc::pthread_cond_timedwait(self.inner.get(), mutex::raw(mutex), &timeout); - debug_assert!(r == libc::ETIMEDOUT || r == 0); - - // ETIMEDOUT is not a totally reliable method of determining timeout due - // to clock shifts, so do the check ourselves - stable_now.elapsed() < dur - } - - #[inline] - #[cfg(not(target_os = "dragonfly"))] - pub unsafe fn destroy(&self) { - let r = libc::pthread_cond_destroy(self.inner.get()); - debug_assert_eq!(r, 0); - } - - #[inline] - #[cfg(target_os = "dragonfly")] - pub unsafe fn destroy(&self) { - let r = libc::pthread_cond_destroy(self.inner.get()); - // On DragonFly pthread_cond_destroy() returns EINVAL if called on - // a condvar that was just initialized with - // libc::PTHREAD_COND_INITIALIZER. Once it is used or - // pthread_cond_init() is called, this behaviour no longer occurs. - debug_assert!(r == 0 || r == libc::EINVAL); - } -} diff --git a/library/std/src/sys/unix/locks/mod.rs b/library/std/src/sys/unix/locks/mod.rs new file mode 100644 index 00000000000..f07a9f93b79 --- /dev/null +++ b/library/std/src/sys/unix/locks/mod.rs @@ -0,0 +1,8 @@ +mod pthread_condvar; +mod pthread_mutex; +mod pthread_remutex; +mod pthread_rwlock; +pub use pthread_condvar::{Condvar, MovableCondvar}; +pub use pthread_mutex::{MovableMutex, Mutex}; +pub use pthread_remutex::ReentrantMutex; +pub use pthread_rwlock::{MovableRWLock, RWLock}; diff --git a/library/std/src/sys/unix/locks/pthread_condvar.rs b/library/std/src/sys/unix/locks/pthread_condvar.rs new file mode 100644 index 00000000000..099aa68706f --- /dev/null +++ b/library/std/src/sys/unix/locks/pthread_condvar.rs @@ -0,0 +1,197 @@ +use crate::cell::UnsafeCell; +use crate::sys::locks::{pthread_mutex, Mutex}; +use crate::time::Duration; + +pub struct Condvar { + inner: UnsafeCell, +} + +pub type MovableCondvar = Box; + +unsafe impl Send for Condvar {} +unsafe impl Sync for Condvar {} + +const TIMESPEC_MAX: libc::timespec = + libc::timespec { tv_sec: ::MAX, tv_nsec: 1_000_000_000 - 1 }; + +fn saturating_cast_to_time_t(value: u64) -> libc::time_t { + if value > ::MAX as u64 { ::MAX } else { value as libc::time_t } +} + +impl Condvar { + pub const fn new() -> Condvar { + // Might be moved and address is changing it is better to avoid + // initialization of potentially opaque OS data before it landed + Condvar { inner: UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER) } + } + + #[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "l4re", + target_os = "android", + target_os = "redox" + ))] + pub unsafe fn init(&mut self) {} + + // NOTE: ESP-IDF's PTHREAD_COND_INITIALIZER support is not released yet + // So on that platform, init() should always be called + // Moreover, that platform does not have pthread_condattr_setclock support, + // hence that initialization should be skipped as well + #[cfg(target_os = "espidf")] + pub unsafe fn init(&mut self) { + let r = libc::pthread_cond_init(self.inner.get(), crate::ptr::null()); + assert_eq!(r, 0); + } + + #[cfg(not(any( + target_os = "macos", + target_os = "ios", + target_os = "l4re", + target_os = "android", + target_os = "redox", + target_os = "espidf" + )))] + pub unsafe fn init(&mut self) { + use crate::mem::MaybeUninit; + let mut attr = MaybeUninit::::uninit(); + let r = libc::pthread_condattr_init(attr.as_mut_ptr()); + assert_eq!(r, 0); + let r = libc::pthread_condattr_setclock(attr.as_mut_ptr(), libc::CLOCK_MONOTONIC); + assert_eq!(r, 0); + let r = libc::pthread_cond_init(self.inner.get(), attr.as_ptr()); + assert_eq!(r, 0); + let r = libc::pthread_condattr_destroy(attr.as_mut_ptr()); + assert_eq!(r, 0); + } + + #[inline] + pub unsafe fn notify_one(&self) { + let r = libc::pthread_cond_signal(self.inner.get()); + debug_assert_eq!(r, 0); + } + + #[inline] + pub unsafe fn notify_all(&self) { + let r = libc::pthread_cond_broadcast(self.inner.get()); + debug_assert_eq!(r, 0); + } + + #[inline] + pub unsafe fn wait(&self, mutex: &Mutex) { + let r = libc::pthread_cond_wait(self.inner.get(), pthread_mutex::raw(mutex)); + debug_assert_eq!(r, 0); + } + + // This implementation is used on systems that support pthread_condattr_setclock + // where we configure condition variable to use monotonic clock (instead of + // default system clock). This approach avoids all problems that result + // from changes made to the system time. + #[cfg(not(any( + target_os = "macos", + target_os = "ios", + target_os = "android", + target_os = "espidf" + )))] + pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { + use crate::mem; + + let mut now: libc::timespec = mem::zeroed(); + let r = libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut now); + assert_eq!(r, 0); + + // Nanosecond calculations can't overflow because both values are below 1e9. + let nsec = dur.subsec_nanos() + now.tv_nsec as u32; + + let sec = saturating_cast_to_time_t(dur.as_secs()) + .checked_add((nsec / 1_000_000_000) as libc::time_t) + .and_then(|s| s.checked_add(now.tv_sec)); + let nsec = nsec % 1_000_000_000; + + let timeout = + sec.map(|s| libc::timespec { tv_sec: s, tv_nsec: nsec as _ }).unwrap_or(TIMESPEC_MAX); + + let r = libc::pthread_cond_timedwait(self.inner.get(), pthread_mutex::raw(mutex), &timeout); + assert!(r == libc::ETIMEDOUT || r == 0); + r == 0 + } + + // This implementation is modeled after libcxx's condition_variable + // https://github.com/llvm-mirror/libcxx/blob/release_35/src/condition_variable.cpp#L46 + // https://github.com/llvm-mirror/libcxx/blob/release_35/include/__mutex_base#L367 + #[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "android", + target_os = "espidf" + ))] + pub unsafe fn wait_timeout(&self, mutex: &Mutex, mut dur: Duration) -> bool { + use crate::ptr; + use crate::time::Instant; + + // 1000 years + let max_dur = Duration::from_secs(1000 * 365 * 86400); + + if dur > max_dur { + // OSX implementation of `pthread_cond_timedwait` is buggy + // with super long durations. When duration is greater than + // 0x100_0000_0000_0000 seconds, `pthread_cond_timedwait` + // in macOS Sierra return error 316. + // + // This program demonstrates the issue: + // https://gist.github.com/stepancheg/198db4623a20aad2ad7cddb8fda4a63c + // + // To work around this issue, and possible bugs of other OSes, timeout + // is clamped to 1000 years, which is allowable per the API of `wait_timeout` + // because of spurious wakeups. + + dur = max_dur; + } + + // First, figure out what time it currently is, in both system and + // stable time. pthread_cond_timedwait uses system time, but we want to + // report timeout based on stable time. + let mut sys_now = libc::timeval { tv_sec: 0, tv_usec: 0 }; + let stable_now = Instant::now(); + let r = libc::gettimeofday(&mut sys_now, ptr::null_mut()); + debug_assert_eq!(r, 0); + + let nsec = dur.subsec_nanos() as libc::c_long + (sys_now.tv_usec * 1000) as libc::c_long; + let extra = (nsec / 1_000_000_000) as libc::time_t; + let nsec = nsec % 1_000_000_000; + let seconds = saturating_cast_to_time_t(dur.as_secs()); + + let timeout = sys_now + .tv_sec + .checked_add(extra) + .and_then(|s| s.checked_add(seconds)) + .map(|s| libc::timespec { tv_sec: s, tv_nsec: nsec }) + .unwrap_or(TIMESPEC_MAX); + + // And wait! + let r = libc::pthread_cond_timedwait(self.inner.get(), pthread_mutex::raw(mutex), &timeout); + debug_assert!(r == libc::ETIMEDOUT || r == 0); + + // ETIMEDOUT is not a totally reliable method of determining timeout due + // to clock shifts, so do the check ourselves + stable_now.elapsed() < dur + } + + #[inline] + #[cfg(not(target_os = "dragonfly"))] + pub unsafe fn destroy(&self) { + let r = libc::pthread_cond_destroy(self.inner.get()); + debug_assert_eq!(r, 0); + } + + #[inline] + #[cfg(target_os = "dragonfly")] + pub unsafe fn destroy(&self) { + let r = libc::pthread_cond_destroy(self.inner.get()); + // On DragonFly pthread_cond_destroy() returns EINVAL if called on + // a condvar that was just initialized with + // libc::PTHREAD_COND_INITIALIZER. Once it is used or + // pthread_cond_init() is called, this behaviour no longer occurs. + debug_assert!(r == 0 || r == libc::EINVAL); + } +} diff --git a/library/std/src/sys/unix/locks/pthread_mutex.rs b/library/std/src/sys/unix/locks/pthread_mutex.rs new file mode 100644 index 00000000000..09cfa2f50ec --- /dev/null +++ b/library/std/src/sys/unix/locks/pthread_mutex.rs @@ -0,0 +1,102 @@ +use crate::cell::UnsafeCell; +use crate::mem::MaybeUninit; +use crate::sys::cvt_nz; + +pub struct Mutex { + inner: UnsafeCell, +} + +pub type MovableMutex = Box; + +#[inline] +pub unsafe fn raw(m: &Mutex) -> *mut libc::pthread_mutex_t { + m.inner.get() +} + +unsafe impl Send for Mutex {} +unsafe impl Sync for Mutex {} + +#[allow(dead_code)] // sys isn't exported yet +impl Mutex { + pub const fn new() -> Mutex { + // Might be moved to a different address, so it is better to avoid + // initialization of potentially opaque OS data before it landed. + // Be very careful using this newly constructed `Mutex`, reentrant + // locking is undefined behavior until `init` is called! + Mutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) } + } + #[inline] + pub unsafe fn init(&mut self) { + // Issue #33770 + // + // A pthread mutex initialized with PTHREAD_MUTEX_INITIALIZER will have + // a type of PTHREAD_MUTEX_DEFAULT, which has undefined behavior if you + // try to re-lock it from the same thread when you already hold a lock + // (https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_init.html). + // This is the case even if PTHREAD_MUTEX_DEFAULT == PTHREAD_MUTEX_NORMAL + // (https://github.com/rust-lang/rust/issues/33770#issuecomment-220847521) -- in that + // case, `pthread_mutexattr_settype(PTHREAD_MUTEX_DEFAULT)` will of course be the same + // as setting it to `PTHREAD_MUTEX_NORMAL`, but not setting any mode will result in + // a Mutex where re-locking is UB. + // + // In practice, glibc takes advantage of this undefined behavior to + // implement hardware lock elision, which uses hardware transactional + // memory to avoid acquiring the lock. While a transaction is in + // progress, the lock appears to be unlocked. This isn't a problem for + // other threads since the transactional memory will abort if a conflict + // is detected, however no abort is generated when re-locking from the + // same thread. + // + // Since locking the same mutex twice will result in two aliasing &mut + // references, we instead create the mutex with type + // PTHREAD_MUTEX_NORMAL which is guaranteed to deadlock if we try to + // re-lock it from the same thread, thus avoiding undefined behavior. + let mut attr = MaybeUninit::::uninit(); + cvt_nz(libc::pthread_mutexattr_init(attr.as_mut_ptr())).unwrap(); + let attr = PthreadMutexAttr(&mut attr); + cvt_nz(libc::pthread_mutexattr_settype(attr.0.as_mut_ptr(), libc::PTHREAD_MUTEX_NORMAL)) + .unwrap(); + cvt_nz(libc::pthread_mutex_init(self.inner.get(), attr.0.as_ptr())).unwrap(); + } + #[inline] + pub unsafe fn lock(&self) { + let r = libc::pthread_mutex_lock(self.inner.get()); + debug_assert_eq!(r, 0); + } + #[inline] + pub unsafe fn unlock(&self) { + let r = libc::pthread_mutex_unlock(self.inner.get()); + debug_assert_eq!(r, 0); + } + #[inline] + pub unsafe fn try_lock(&self) -> bool { + libc::pthread_mutex_trylock(self.inner.get()) == 0 + } + #[inline] + #[cfg(not(target_os = "dragonfly"))] + pub unsafe fn destroy(&self) { + let r = libc::pthread_mutex_destroy(self.inner.get()); + debug_assert_eq!(r, 0); + } + #[inline] + #[cfg(target_os = "dragonfly")] + pub unsafe fn destroy(&self) { + let r = libc::pthread_mutex_destroy(self.inner.get()); + // On DragonFly pthread_mutex_destroy() returns EINVAL if called on a + // mutex that was just initialized with libc::PTHREAD_MUTEX_INITIALIZER. + // Once it is used (locked/unlocked) or pthread_mutex_init() is called, + // this behaviour no longer occurs. + debug_assert!(r == 0 || r == libc::EINVAL); + } +} + +pub(super) struct PthreadMutexAttr<'a>(pub &'a mut MaybeUninit); + +impl Drop for PthreadMutexAttr<'_> { + fn drop(&mut self) { + unsafe { + let result = libc::pthread_mutexattr_destroy(self.0.as_mut_ptr()); + debug_assert_eq!(result, 0); + } + } +} diff --git a/library/std/src/sys/unix/locks/pthread_remutex.rs b/library/std/src/sys/unix/locks/pthread_remutex.rs new file mode 100644 index 00000000000..b006181ee3a --- /dev/null +++ b/library/std/src/sys/unix/locks/pthread_remutex.rs @@ -0,0 +1,46 @@ +use super::pthread_mutex::PthreadMutexAttr; +use crate::cell::UnsafeCell; +use crate::mem::MaybeUninit; +use crate::sys::cvt_nz; + +pub struct ReentrantMutex { + inner: UnsafeCell, +} + +unsafe impl Send for ReentrantMutex {} +unsafe impl Sync for ReentrantMutex {} + +impl ReentrantMutex { + pub const unsafe fn uninitialized() -> ReentrantMutex { + ReentrantMutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) } + } + + pub unsafe fn init(&self) { + let mut attr = MaybeUninit::::uninit(); + cvt_nz(libc::pthread_mutexattr_init(attr.as_mut_ptr())).unwrap(); + let attr = PthreadMutexAttr(&mut attr); + cvt_nz(libc::pthread_mutexattr_settype(attr.0.as_mut_ptr(), libc::PTHREAD_MUTEX_RECURSIVE)) + .unwrap(); + cvt_nz(libc::pthread_mutex_init(self.inner.get(), attr.0.as_ptr())).unwrap(); + } + + pub unsafe fn lock(&self) { + let result = libc::pthread_mutex_lock(self.inner.get()); + debug_assert_eq!(result, 0); + } + + #[inline] + pub unsafe fn try_lock(&self) -> bool { + libc::pthread_mutex_trylock(self.inner.get()) == 0 + } + + pub unsafe fn unlock(&self) { + let result = libc::pthread_mutex_unlock(self.inner.get()); + debug_assert_eq!(result, 0); + } + + pub unsafe fn destroy(&self) { + let result = libc::pthread_mutex_destroy(self.inner.get()); + debug_assert_eq!(result, 0); + } +} diff --git a/library/std/src/sys/unix/locks/pthread_rwlock.rs b/library/std/src/sys/unix/locks/pthread_rwlock.rs new file mode 100644 index 00000000000..1318c5b8e3a --- /dev/null +++ b/library/std/src/sys/unix/locks/pthread_rwlock.rs @@ -0,0 +1,143 @@ +use crate::cell::UnsafeCell; +use crate::sync::atomic::{AtomicUsize, Ordering}; + +pub struct RWLock { + inner: UnsafeCell, + write_locked: UnsafeCell, // guarded by the `inner` RwLock + num_readers: AtomicUsize, +} + +pub type MovableRWLock = Box; + +unsafe impl Send for RWLock {} +unsafe impl Sync for RWLock {} + +impl RWLock { + pub const fn new() -> RWLock { + RWLock { + inner: UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER), + write_locked: UnsafeCell::new(false), + num_readers: AtomicUsize::new(0), + } + } + #[inline] + pub unsafe fn read(&self) { + let r = libc::pthread_rwlock_rdlock(self.inner.get()); + + // According to POSIX, when a thread tries to acquire this read lock + // while it already holds the write lock + // (or vice versa, or tries to acquire the write lock twice), + // "the call shall either deadlock or return [EDEADLK]" + // (https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_wrlock.html, + // https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_rdlock.html). + // So, in principle, all we have to do here is check `r == 0` to be sure we properly + // got the lock. + // + // However, (at least) glibc before version 2.25 does not conform to this spec, + // and can return `r == 0` even when this thread already holds the write lock. + // We thus check for this situation ourselves and panic when detecting that a thread + // got the write lock more than once, or got a read and a write lock. + if r == libc::EAGAIN { + panic!("rwlock maximum reader count exceeded"); + } else if r == libc::EDEADLK || (r == 0 && *self.write_locked.get()) { + // Above, we make sure to only access `write_locked` when `r == 0` to avoid + // data races. + if r == 0 { + // `pthread_rwlock_rdlock` succeeded when it should not have. + self.raw_unlock(); + } + panic!("rwlock read lock would result in deadlock"); + } else { + // POSIX does not make guarantees about all the errors that may be returned. + // See issue #94705 for more details. + assert_eq!(r, 0, "unexpected error during rwlock read lock: {:?}", r); + self.num_readers.fetch_add(1, Ordering::Relaxed); + } + } + #[inline] + pub unsafe fn try_read(&self) -> bool { + let r = libc::pthread_rwlock_tryrdlock(self.inner.get()); + if r == 0 { + if *self.write_locked.get() { + // `pthread_rwlock_tryrdlock` succeeded when it should not have. + self.raw_unlock(); + false + } else { + self.num_readers.fetch_add(1, Ordering::Relaxed); + true + } + } else { + false + } + } + #[inline] + pub unsafe fn write(&self) { + let r = libc::pthread_rwlock_wrlock(self.inner.get()); + // See comments above for why we check for EDEADLK and write_locked. For the same reason, + // we also need to check that there are no readers (tracked in `num_readers`). + if r == libc::EDEADLK + || (r == 0 && *self.write_locked.get()) + || self.num_readers.load(Ordering::Relaxed) != 0 + { + // Above, we make sure to only access `write_locked` when `r == 0` to avoid + // data races. + if r == 0 { + // `pthread_rwlock_wrlock` succeeded when it should not have. + self.raw_unlock(); + } + panic!("rwlock write lock would result in deadlock"); + } else { + // According to POSIX, for a properly initialized rwlock this can only + // return EDEADLK or 0. We rely on that. + debug_assert_eq!(r, 0); + } + *self.write_locked.get() = true; + } + #[inline] + pub unsafe fn try_write(&self) -> bool { + let r = libc::pthread_rwlock_trywrlock(self.inner.get()); + if r == 0 { + if *self.write_locked.get() || self.num_readers.load(Ordering::Relaxed) != 0 { + // `pthread_rwlock_trywrlock` succeeded when it should not have. + self.raw_unlock(); + false + } else { + *self.write_locked.get() = true; + true + } + } else { + false + } + } + #[inline] + unsafe fn raw_unlock(&self) { + let r = libc::pthread_rwlock_unlock(self.inner.get()); + debug_assert_eq!(r, 0); + } + #[inline] + pub unsafe fn read_unlock(&self) { + debug_assert!(!*self.write_locked.get()); + self.num_readers.fetch_sub(1, Ordering::Relaxed); + self.raw_unlock(); + } + #[inline] + pub unsafe fn write_unlock(&self) { + debug_assert_eq!(self.num_readers.load(Ordering::Relaxed), 0); + debug_assert!(*self.write_locked.get()); + *self.write_locked.get() = false; + self.raw_unlock(); + } + #[inline] + pub unsafe fn destroy(&self) { + let r = libc::pthread_rwlock_destroy(self.inner.get()); + // On DragonFly pthread_rwlock_destroy() returns EINVAL if called on a + // rwlock that was just initialized with + // libc::PTHREAD_RWLOCK_INITIALIZER. Once it is used (locked/unlocked) + // or pthread_rwlock_init() is called, this behaviour no longer occurs. + if cfg!(target_os = "dragonfly") { + debug_assert!(r == 0 || r == libc::EINVAL); + } else { + debug_assert_eq!(r, 0); + } + } +} diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs index 605cc499b3c..d5250bde75e 100644 --- a/library/std/src/sys/unix/mod.rs +++ b/library/std/src/sys/unix/mod.rs @@ -14,7 +14,6 @@ pub mod android; pub mod args; #[path = "../unix/cmath.rs"] pub mod cmath; -pub mod condvar; pub mod env; pub mod fd; pub mod fs; @@ -24,8 +23,8 @@ pub mod io; pub mod kernel_copy; #[cfg(target_os = "l4re")] mod l4re; +pub mod locks; pub mod memchr; -pub mod mutex; #[cfg(not(target_os = "l4re"))] pub mod net; #[cfg(target_os = "l4re")] @@ -36,7 +35,6 @@ pub mod path; pub mod pipe; pub mod process; pub mod rand; -pub mod rwlock; pub mod stack_overflow; pub mod stdio; pub mod thread; diff --git a/library/std/src/sys/unix/mutex.rs b/library/std/src/sys/unix/mutex.rs deleted file mode 100644 index 89c55eb859d..00000000000 --- a/library/std/src/sys/unix/mutex.rs +++ /dev/null @@ -1,144 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::mem::MaybeUninit; -use crate::sys::cvt_nz; - -pub struct Mutex { - inner: UnsafeCell, -} - -pub type MovableMutex = Box; - -#[inline] -pub unsafe fn raw(m: &Mutex) -> *mut libc::pthread_mutex_t { - m.inner.get() -} - -unsafe impl Send for Mutex {} -unsafe impl Sync for Mutex {} - -#[allow(dead_code)] // sys isn't exported yet -impl Mutex { - pub const fn new() -> Mutex { - // Might be moved to a different address, so it is better to avoid - // initialization of potentially opaque OS data before it landed. - // Be very careful using this newly constructed `Mutex`, reentrant - // locking is undefined behavior until `init` is called! - Mutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) } - } - #[inline] - pub unsafe fn init(&mut self) { - // Issue #33770 - // - // A pthread mutex initialized with PTHREAD_MUTEX_INITIALIZER will have - // a type of PTHREAD_MUTEX_DEFAULT, which has undefined behavior if you - // try to re-lock it from the same thread when you already hold a lock - // (https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_init.html). - // This is the case even if PTHREAD_MUTEX_DEFAULT == PTHREAD_MUTEX_NORMAL - // (https://github.com/rust-lang/rust/issues/33770#issuecomment-220847521) -- in that - // case, `pthread_mutexattr_settype(PTHREAD_MUTEX_DEFAULT)` will of course be the same - // as setting it to `PTHREAD_MUTEX_NORMAL`, but not setting any mode will result in - // a Mutex where re-locking is UB. - // - // In practice, glibc takes advantage of this undefined behavior to - // implement hardware lock elision, which uses hardware transactional - // memory to avoid acquiring the lock. While a transaction is in - // progress, the lock appears to be unlocked. This isn't a problem for - // other threads since the transactional memory will abort if a conflict - // is detected, however no abort is generated when re-locking from the - // same thread. - // - // Since locking the same mutex twice will result in two aliasing &mut - // references, we instead create the mutex with type - // PTHREAD_MUTEX_NORMAL which is guaranteed to deadlock if we try to - // re-lock it from the same thread, thus avoiding undefined behavior. - let mut attr = MaybeUninit::::uninit(); - cvt_nz(libc::pthread_mutexattr_init(attr.as_mut_ptr())).unwrap(); - let attr = PthreadMutexAttr(&mut attr); - cvt_nz(libc::pthread_mutexattr_settype(attr.0.as_mut_ptr(), libc::PTHREAD_MUTEX_NORMAL)) - .unwrap(); - cvt_nz(libc::pthread_mutex_init(self.inner.get(), attr.0.as_ptr())).unwrap(); - } - #[inline] - pub unsafe fn lock(&self) { - let r = libc::pthread_mutex_lock(self.inner.get()); - debug_assert_eq!(r, 0); - } - #[inline] - pub unsafe fn unlock(&self) { - let r = libc::pthread_mutex_unlock(self.inner.get()); - debug_assert_eq!(r, 0); - } - #[inline] - pub unsafe fn try_lock(&self) -> bool { - libc::pthread_mutex_trylock(self.inner.get()) == 0 - } - #[inline] - #[cfg(not(target_os = "dragonfly"))] - pub unsafe fn destroy(&self) { - let r = libc::pthread_mutex_destroy(self.inner.get()); - debug_assert_eq!(r, 0); - } - #[inline] - #[cfg(target_os = "dragonfly")] - pub unsafe fn destroy(&self) { - let r = libc::pthread_mutex_destroy(self.inner.get()); - // On DragonFly pthread_mutex_destroy() returns EINVAL if called on a - // mutex that was just initialized with libc::PTHREAD_MUTEX_INITIALIZER. - // Once it is used (locked/unlocked) or pthread_mutex_init() is called, - // this behaviour no longer occurs. - debug_assert!(r == 0 || r == libc::EINVAL); - } -} - -pub struct ReentrantMutex { - inner: UnsafeCell, -} - -unsafe impl Send for ReentrantMutex {} -unsafe impl Sync for ReentrantMutex {} - -impl ReentrantMutex { - pub const unsafe fn uninitialized() -> ReentrantMutex { - ReentrantMutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) } - } - - pub unsafe fn init(&self) { - let mut attr = MaybeUninit::::uninit(); - cvt_nz(libc::pthread_mutexattr_init(attr.as_mut_ptr())).unwrap(); - let attr = PthreadMutexAttr(&mut attr); - cvt_nz(libc::pthread_mutexattr_settype(attr.0.as_mut_ptr(), libc::PTHREAD_MUTEX_RECURSIVE)) - .unwrap(); - cvt_nz(libc::pthread_mutex_init(self.inner.get(), attr.0.as_ptr())).unwrap(); - } - - pub unsafe fn lock(&self) { - let result = libc::pthread_mutex_lock(self.inner.get()); - debug_assert_eq!(result, 0); - } - - #[inline] - pub unsafe fn try_lock(&self) -> bool { - libc::pthread_mutex_trylock(self.inner.get()) == 0 - } - - pub unsafe fn unlock(&self) { - let result = libc::pthread_mutex_unlock(self.inner.get()); - debug_assert_eq!(result, 0); - } - - pub unsafe fn destroy(&self) { - let result = libc::pthread_mutex_destroy(self.inner.get()); - debug_assert_eq!(result, 0); - } -} - -struct PthreadMutexAttr<'a>(&'a mut MaybeUninit); - -impl Drop for PthreadMutexAttr<'_> { - fn drop(&mut self) { - unsafe { - let result = libc::pthread_mutexattr_destroy(self.0.as_mut_ptr()); - debug_assert_eq!(result, 0); - } - } -} diff --git a/library/std/src/sys/unix/rwlock.rs b/library/std/src/sys/unix/rwlock.rs deleted file mode 100644 index 1318c5b8e3a..00000000000 --- a/library/std/src/sys/unix/rwlock.rs +++ /dev/null @@ -1,143 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::sync::atomic::{AtomicUsize, Ordering}; - -pub struct RWLock { - inner: UnsafeCell, - write_locked: UnsafeCell, // guarded by the `inner` RwLock - num_readers: AtomicUsize, -} - -pub type MovableRWLock = Box; - -unsafe impl Send for RWLock {} -unsafe impl Sync for RWLock {} - -impl RWLock { - pub const fn new() -> RWLock { - RWLock { - inner: UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER), - write_locked: UnsafeCell::new(false), - num_readers: AtomicUsize::new(0), - } - } - #[inline] - pub unsafe fn read(&self) { - let r = libc::pthread_rwlock_rdlock(self.inner.get()); - - // According to POSIX, when a thread tries to acquire this read lock - // while it already holds the write lock - // (or vice versa, or tries to acquire the write lock twice), - // "the call shall either deadlock or return [EDEADLK]" - // (https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_wrlock.html, - // https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_rdlock.html). - // So, in principle, all we have to do here is check `r == 0` to be sure we properly - // got the lock. - // - // However, (at least) glibc before version 2.25 does not conform to this spec, - // and can return `r == 0` even when this thread already holds the write lock. - // We thus check for this situation ourselves and panic when detecting that a thread - // got the write lock more than once, or got a read and a write lock. - if r == libc::EAGAIN { - panic!("rwlock maximum reader count exceeded"); - } else if r == libc::EDEADLK || (r == 0 && *self.write_locked.get()) { - // Above, we make sure to only access `write_locked` when `r == 0` to avoid - // data races. - if r == 0 { - // `pthread_rwlock_rdlock` succeeded when it should not have. - self.raw_unlock(); - } - panic!("rwlock read lock would result in deadlock"); - } else { - // POSIX does not make guarantees about all the errors that may be returned. - // See issue #94705 for more details. - assert_eq!(r, 0, "unexpected error during rwlock read lock: {:?}", r); - self.num_readers.fetch_add(1, Ordering::Relaxed); - } - } - #[inline] - pub unsafe fn try_read(&self) -> bool { - let r = libc::pthread_rwlock_tryrdlock(self.inner.get()); - if r == 0 { - if *self.write_locked.get() { - // `pthread_rwlock_tryrdlock` succeeded when it should not have. - self.raw_unlock(); - false - } else { - self.num_readers.fetch_add(1, Ordering::Relaxed); - true - } - } else { - false - } - } - #[inline] - pub unsafe fn write(&self) { - let r = libc::pthread_rwlock_wrlock(self.inner.get()); - // See comments above for why we check for EDEADLK and write_locked. For the same reason, - // we also need to check that there are no readers (tracked in `num_readers`). - if r == libc::EDEADLK - || (r == 0 && *self.write_locked.get()) - || self.num_readers.load(Ordering::Relaxed) != 0 - { - // Above, we make sure to only access `write_locked` when `r == 0` to avoid - // data races. - if r == 0 { - // `pthread_rwlock_wrlock` succeeded when it should not have. - self.raw_unlock(); - } - panic!("rwlock write lock would result in deadlock"); - } else { - // According to POSIX, for a properly initialized rwlock this can only - // return EDEADLK or 0. We rely on that. - debug_assert_eq!(r, 0); - } - *self.write_locked.get() = true; - } - #[inline] - pub unsafe fn try_write(&self) -> bool { - let r = libc::pthread_rwlock_trywrlock(self.inner.get()); - if r == 0 { - if *self.write_locked.get() || self.num_readers.load(Ordering::Relaxed) != 0 { - // `pthread_rwlock_trywrlock` succeeded when it should not have. - self.raw_unlock(); - false - } else { - *self.write_locked.get() = true; - true - } - } else { - false - } - } - #[inline] - unsafe fn raw_unlock(&self) { - let r = libc::pthread_rwlock_unlock(self.inner.get()); - debug_assert_eq!(r, 0); - } - #[inline] - pub unsafe fn read_unlock(&self) { - debug_assert!(!*self.write_locked.get()); - self.num_readers.fetch_sub(1, Ordering::Relaxed); - self.raw_unlock(); - } - #[inline] - pub unsafe fn write_unlock(&self) { - debug_assert_eq!(self.num_readers.load(Ordering::Relaxed), 0); - debug_assert!(*self.write_locked.get()); - *self.write_locked.get() = false; - self.raw_unlock(); - } - #[inline] - pub unsafe fn destroy(&self) { - let r = libc::pthread_rwlock_destroy(self.inner.get()); - // On DragonFly pthread_rwlock_destroy() returns EINVAL if called on a - // rwlock that was just initialized with - // libc::PTHREAD_RWLOCK_INITIALIZER. Once it is used (locked/unlocked) - // or pthread_rwlock_init() is called, this behaviour no longer occurs. - if cfg!(target_os = "dragonfly") { - debug_assert!(r == 0 || r == libc::EINVAL); - } else { - debug_assert_eq!(r, 0); - } - } -} diff --git a/library/std/src/sys_common/condvar.rs b/library/std/src/sys_common/condvar.rs index 2c02e1cd33c..67d4b126209 100644 --- a/library/std/src/sys_common/condvar.rs +++ b/library/std/src/sys_common/condvar.rs @@ -1,11 +1,10 @@ -use crate::sys::condvar as imp; -use crate::sys::mutex as mutex_imp; +use crate::sys::locks as imp; use crate::sys_common::mutex::MovableMutex; use crate::time::Duration; mod check; -type CondvarCheck = ::Check; +type CondvarCheck = ::Check; /// An OS-based condition variable. pub struct Condvar { diff --git a/library/std/src/sys_common/condvar/check.rs b/library/std/src/sys_common/condvar/check.rs index 1578a2de60c..47aff060d6f 100644 --- a/library/std/src/sys_common/condvar/check.rs +++ b/library/std/src/sys_common/condvar/check.rs @@ -1,5 +1,5 @@ use crate::sync::atomic::{AtomicUsize, Ordering}; -use crate::sys::mutex as mutex_imp; +use crate::sys::locks as imp; use crate::sys_common::mutex::MovableMutex; pub trait CondvarCheck { @@ -8,7 +8,7 @@ pub trait CondvarCheck { /// For boxed mutexes, a `Condvar` will check it's only ever used with the same /// mutex, based on its (stable) address. -impl CondvarCheck for Box { +impl CondvarCheck for Box { type Check = SameMutexCheck; } @@ -22,7 +22,7 @@ impl SameMutexCheck { Self { addr: AtomicUsize::new(0) } } pub fn verify(&self, mutex: &MovableMutex) { - let addr = mutex.raw() as *const mutex_imp::Mutex as usize; + let addr = mutex.raw() as *const imp::Mutex as usize; match self.addr.compare_exchange(0, addr, Ordering::SeqCst, Ordering::SeqCst) { Ok(_) => {} // Stored the address Err(n) if n == addr => {} // Lost a race to store the same address @@ -33,7 +33,7 @@ impl SameMutexCheck { /// Unboxed mutexes may move, so `Condvar` can not require its address to stay /// constant. -impl CondvarCheck for mutex_imp::Mutex { +impl CondvarCheck for imp::Mutex { type Check = NoCheck; } diff --git a/library/std/src/sys_common/mutex.rs b/library/std/src/sys_common/mutex.rs index f3e7efb955a..12a09c98605 100644 --- a/library/std/src/sys_common/mutex.rs +++ b/library/std/src/sys_common/mutex.rs @@ -1,4 +1,4 @@ -use crate::sys::mutex as imp; +use crate::sys::locks as imp; /// An OS-based mutual exclusion lock, meant for use in static variables. /// diff --git a/library/std/src/sys_common/remutex.rs b/library/std/src/sys_common/remutex.rs index 475bfca9b6d..801c9c28dd3 100644 --- a/library/std/src/sys_common/remutex.rs +++ b/library/std/src/sys_common/remutex.rs @@ -5,7 +5,7 @@ use crate::marker::PhantomPinned; use crate::ops::Deref; use crate::panic::{RefUnwindSafe, UnwindSafe}; use crate::pin::Pin; -use crate::sys::mutex as sys; +use crate::sys::locks as sys; /// A re-entrant mutual exclusion /// diff --git a/library/std/src/sys_common/rwlock.rs b/library/std/src/sys_common/rwlock.rs index 07ec20f4dc6..eaee6312701 100644 --- a/library/std/src/sys_common/rwlock.rs +++ b/library/std/src/sys_common/rwlock.rs @@ -1,4 +1,4 @@ -use crate::sys::rwlock as imp; +use crate::sys::locks as imp; /// An OS-based reader-writer lock, meant for use in static variables. /// -- cgit 1.4.1-3-g733a5 From 733153f2e550d46fe6f794c969df91368580e0b8 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Mon, 21 Mar 2022 15:45:51 +0100 Subject: Move std::sys::{mutex, condvar, rwlock} to std::sys::locks. --- library/std/src/sys/hermit/condvar.rs | 2 +- library/std/src/sys/hermit/mod.rs | 13 +++- library/std/src/sys/hermit/rwlock.rs | 3 +- library/std/src/sys/itron/condvar.rs | 2 +- library/std/src/sys/sgx/condvar.rs | 2 +- library/std/src/sys/sgx/mod.rs | 13 +++- library/std/src/sys/solid/mod.rs | 11 ++- library/std/src/sys/unsupported/condvar.rs | 32 -------- library/std/src/sys/unsupported/locks/condvar.rs | 32 ++++++++ library/std/src/sys/unsupported/locks/mod.rs | 6 ++ library/std/src/sys/unsupported/locks/mutex.rs | 61 +++++++++++++++ library/std/src/sys/unsupported/locks/rwlock.rs | 68 +++++++++++++++++ library/std/src/sys/unsupported/mod.rs | 4 +- library/std/src/sys/unsupported/mutex.rs | 61 --------------- library/std/src/sys/unsupported/rwlock.rs | 68 ----------------- library/std/src/sys/wasi/mod.rs | 8 +- library/std/src/sys/wasm/atomics/condvar.rs | 2 +- library/std/src/sys/wasm/atomics/rwlock.rs | 3 +- library/std/src/sys/wasm/mod.rs | 19 ++--- library/std/src/sys/windows/condvar.rs | 58 -------------- library/std/src/sys/windows/locks/condvar.rs | 58 ++++++++++++++ library/std/src/sys/windows/locks/mod.rs | 6 ++ library/std/src/sys/windows/locks/mutex.rs | 96 ++++++++++++++++++++++++ library/std/src/sys/windows/locks/rwlock.rs | 46 ++++++++++++ library/std/src/sys/windows/mod.rs | 4 +- library/std/src/sys/windows/mutex.rs | 96 ------------------------ library/std/src/sys/windows/rwlock.rs | 46 ------------ 27 files changed, 422 insertions(+), 398 deletions(-) delete mode 100644 library/std/src/sys/unsupported/condvar.rs create mode 100644 library/std/src/sys/unsupported/locks/condvar.rs create mode 100644 library/std/src/sys/unsupported/locks/mod.rs create mode 100644 library/std/src/sys/unsupported/locks/mutex.rs create mode 100644 library/std/src/sys/unsupported/locks/rwlock.rs delete mode 100644 library/std/src/sys/unsupported/mutex.rs delete mode 100644 library/std/src/sys/unsupported/rwlock.rs delete mode 100644 library/std/src/sys/windows/condvar.rs create mode 100644 library/std/src/sys/windows/locks/condvar.rs create mode 100644 library/std/src/sys/windows/locks/mod.rs create mode 100644 library/std/src/sys/windows/locks/mutex.rs create mode 100644 library/std/src/sys/windows/locks/rwlock.rs delete mode 100644 library/std/src/sys/windows/mutex.rs delete mode 100644 library/std/src/sys/windows/rwlock.rs (limited to 'library') diff --git a/library/std/src/sys/hermit/condvar.rs b/library/std/src/sys/hermit/condvar.rs index b62f21a9dac..f6083530005 100644 --- a/library/std/src/sys/hermit/condvar.rs +++ b/library/std/src/sys/hermit/condvar.rs @@ -2,7 +2,7 @@ use crate::ffi::c_void; use crate::ptr; use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst}; use crate::sys::hermit::abi; -use crate::sys::mutex::Mutex; +use crate::sys::locks::Mutex; use crate::time::Duration; // The implementation is inspired by Andrew D. Birrell's paper diff --git a/library/std/src/sys/hermit/mod.rs b/library/std/src/sys/hermit/mod.rs index b798c97448b..08eca423802 100644 --- a/library/std/src/sys/hermit/mod.rs +++ b/library/std/src/sys/hermit/mod.rs @@ -22,14 +22,12 @@ pub mod alloc; pub mod args; #[path = "../unix/cmath.rs"] pub mod cmath; -pub mod condvar; pub mod env; pub mod fd; pub mod fs; #[path = "../unsupported/io.rs"] pub mod io; pub mod memchr; -pub mod mutex; pub mod net; pub mod os; #[path = "../unix/os_str.rs"] @@ -40,7 +38,6 @@ pub mod path; pub mod pipe; #[path = "../unsupported/process.rs"] pub mod process; -pub mod rwlock; pub mod stdio; pub mod thread; pub mod thread_local_dtor; @@ -48,6 +45,16 @@ pub mod thread_local_dtor; pub mod thread_local_key; pub mod time; +mod condvar; +mod mutex; +mod rwlock; + +pub mod locks { + pub use super::condvar::*; + pub use super::mutex::*; + pub use super::rwlock::*; +} + use crate::io::ErrorKind; #[allow(unused_extern_crates)] diff --git a/library/std/src/sys/hermit/rwlock.rs b/library/std/src/sys/hermit/rwlock.rs index 64eaa2fc482..1cca809764c 100644 --- a/library/std/src/sys/hermit/rwlock.rs +++ b/library/std/src/sys/hermit/rwlock.rs @@ -1,6 +1,5 @@ use crate::cell::UnsafeCell; -use crate::sys::condvar::Condvar; -use crate::sys::mutex::Mutex; +use crate::sys::locks::{Condvar, Mutex}; pub struct RWLock { lock: Mutex, diff --git a/library/std/src/sys/itron/condvar.rs b/library/std/src/sys/itron/condvar.rs index 2992a6a5429..ed26c528027 100644 --- a/library/std/src/sys/itron/condvar.rs +++ b/library/std/src/sys/itron/condvar.rs @@ -1,6 +1,6 @@ //! POSIX conditional variable implementation based on user-space wait queues. use super::{abi, error::expect_success_aborting, spin::SpinMutex, task, time::with_tmos_strong}; -use crate::{mem::replace, ptr::NonNull, sys::mutex::Mutex, time::Duration}; +use crate::{mem::replace, ptr::NonNull, sys::locks::Mutex, time::Duration}; // The implementation is inspired by the queue-based implementation shown in // Andrew D. Birrell's paper "Implementing Condition Variables with Semaphores" diff --git a/library/std/src/sys/sgx/condvar.rs b/library/std/src/sys/sgx/condvar.rs index 55ac052d975..c9736880b08 100644 --- a/library/std/src/sys/sgx/condvar.rs +++ b/library/std/src/sys/sgx/condvar.rs @@ -1,4 +1,4 @@ -use crate::sys::mutex::Mutex; +use crate::sys::locks::Mutex; use crate::time::Duration; use super::waitqueue::{SpinMutex, WaitQueue, WaitVariable}; diff --git a/library/std/src/sys/sgx/mod.rs b/library/std/src/sys/sgx/mod.rs index 158c92e7a77..1333edb9881 100644 --- a/library/std/src/sys/sgx/mod.rs +++ b/library/std/src/sys/sgx/mod.rs @@ -15,7 +15,6 @@ pub mod alloc; pub mod args; #[path = "../unix/cmath.rs"] pub mod cmath; -pub mod condvar; pub mod env; pub mod fd; #[path = "../unsupported/fs.rs"] @@ -23,7 +22,6 @@ pub mod fs; #[path = "../unsupported/io.rs"] pub mod io; pub mod memchr; -pub mod mutex; pub mod net; pub mod os; #[path = "../unix/os_str.rs"] @@ -33,12 +31,21 @@ pub mod path; pub mod pipe; #[path = "../unsupported/process.rs"] pub mod process; -pub mod rwlock; pub mod stdio; pub mod thread; pub mod thread_local_key; pub mod time; +mod condvar; +mod mutex; +mod rwlock; + +pub mod locks { + pub use super::condvar::*; + pub use super::mutex::*; + pub use super::rwlock::*; +} + // SAFETY: must be called only once during runtime initialization. // NOTE: this is not guaranteed to run, for example when Rust code is called externally. pub unsafe fn init(argc: isize, argv: *const *const u8) { diff --git a/library/std/src/sys/solid/mod.rs b/library/std/src/sys/solid/mod.rs index 049460755d6..492b1a55475 100644 --- a/library/std/src/sys/solid/mod.rs +++ b/library/std/src/sys/solid/mod.rs @@ -37,14 +37,21 @@ pub mod path; pub mod pipe; #[path = "../unsupported/process.rs"] pub mod process; -pub mod rwlock; pub mod stdio; -pub use self::itron::{condvar, mutex, thread}; +pub use self::itron::thread; pub mod memchr; pub mod thread_local_dtor; pub mod thread_local_key; pub mod time; +mod rwlock; + +pub mod locks { + pub use super::itron::condvar::*; + pub use super::itron::mutex::*; + pub use super::rwlock::*; +} + // SAFETY: must be called only once during runtime initialization. // NOTE: this is not guaranteed to run, for example when Rust code is called externally. pub unsafe fn init(_argc: isize, _argv: *const *const u8) {} diff --git a/library/std/src/sys/unsupported/condvar.rs b/library/std/src/sys/unsupported/condvar.rs deleted file mode 100644 index 35d12a69c8a..00000000000 --- a/library/std/src/sys/unsupported/condvar.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::sys::mutex::Mutex; -use crate::time::Duration; - -pub struct Condvar {} - -pub type MovableCondvar = Condvar; - -impl Condvar { - pub const fn new() -> Condvar { - Condvar {} - } - - #[inline] - pub unsafe fn init(&mut self) {} - - #[inline] - pub unsafe fn notify_one(&self) {} - - #[inline] - pub unsafe fn notify_all(&self) {} - - pub unsafe fn wait(&self, _mutex: &Mutex) { - panic!("condvar wait not supported") - } - - pub unsafe fn wait_timeout(&self, _mutex: &Mutex, _dur: Duration) -> bool { - panic!("condvar wait not supported"); - } - - #[inline] - pub unsafe fn destroy(&self) {} -} diff --git a/library/std/src/sys/unsupported/locks/condvar.rs b/library/std/src/sys/unsupported/locks/condvar.rs new file mode 100644 index 00000000000..8dbe03bad9b --- /dev/null +++ b/library/std/src/sys/unsupported/locks/condvar.rs @@ -0,0 +1,32 @@ +use crate::sys::locks::Mutex; +use crate::time::Duration; + +pub struct Condvar {} + +pub type MovableCondvar = Condvar; + +impl Condvar { + pub const fn new() -> Condvar { + Condvar {} + } + + #[inline] + pub unsafe fn init(&mut self) {} + + #[inline] + pub unsafe fn notify_one(&self) {} + + #[inline] + pub unsafe fn notify_all(&self) {} + + pub unsafe fn wait(&self, _mutex: &Mutex) { + panic!("condvar wait not supported") + } + + pub unsafe fn wait_timeout(&self, _mutex: &Mutex, _dur: Duration) -> bool { + panic!("condvar wait not supported"); + } + + #[inline] + pub unsafe fn destroy(&self) {} +} diff --git a/library/std/src/sys/unsupported/locks/mod.rs b/library/std/src/sys/unsupported/locks/mod.rs new file mode 100644 index 00000000000..5634f106339 --- /dev/null +++ b/library/std/src/sys/unsupported/locks/mod.rs @@ -0,0 +1,6 @@ +mod condvar; +mod mutex; +mod rwlock; +pub use condvar::{Condvar, MovableCondvar}; +pub use mutex::{MovableMutex, Mutex, ReentrantMutex}; +pub use rwlock::{MovableRWLock, RWLock}; diff --git a/library/std/src/sys/unsupported/locks/mutex.rs b/library/std/src/sys/unsupported/locks/mutex.rs new file mode 100644 index 00000000000..b3203c16c50 --- /dev/null +++ b/library/std/src/sys/unsupported/locks/mutex.rs @@ -0,0 +1,61 @@ +use crate::cell::Cell; + +pub struct Mutex { + // This platform has no threads, so we can use a Cell here. + locked: Cell, +} + +pub type MovableMutex = Mutex; + +unsafe impl Send for Mutex {} +unsafe impl Sync for Mutex {} // no threads on this platform + +impl Mutex { + pub const fn new() -> Mutex { + Mutex { locked: Cell::new(false) } + } + + #[inline] + pub unsafe fn init(&mut self) {} + + #[inline] + pub unsafe fn lock(&self) { + assert_eq!(self.locked.replace(true), false, "cannot recursively acquire mutex"); + } + + #[inline] + pub unsafe fn unlock(&self) { + self.locked.set(false); + } + + #[inline] + pub unsafe fn try_lock(&self) -> bool { + self.locked.replace(true) == false + } + + #[inline] + pub unsafe fn destroy(&self) {} +} + +// All empty stubs because this platform does not yet support threads, so lock +// acquisition always succeeds. +pub struct ReentrantMutex {} + +impl ReentrantMutex { + pub const unsafe fn uninitialized() -> ReentrantMutex { + ReentrantMutex {} + } + + pub unsafe fn init(&self) {} + + pub unsafe fn lock(&self) {} + + #[inline] + pub unsafe fn try_lock(&self) -> bool { + true + } + + pub unsafe fn unlock(&self) {} + + pub unsafe fn destroy(&self) {} +} diff --git a/library/std/src/sys/unsupported/locks/rwlock.rs b/library/std/src/sys/unsupported/locks/rwlock.rs new file mode 100644 index 00000000000..8438adeb5b5 --- /dev/null +++ b/library/std/src/sys/unsupported/locks/rwlock.rs @@ -0,0 +1,68 @@ +use crate::cell::Cell; + +pub struct RWLock { + // This platform has no threads, so we can use a Cell here. + mode: Cell, +} + +pub type MovableRWLock = RWLock; + +unsafe impl Send for RWLock {} +unsafe impl Sync for RWLock {} // no threads on this platform + +impl RWLock { + pub const fn new() -> RWLock { + RWLock { mode: Cell::new(0) } + } + + #[inline] + pub unsafe fn read(&self) { + let m = self.mode.get(); + if m >= 0 { + self.mode.set(m + 1); + } else { + rtabort!("rwlock locked for writing"); + } + } + + #[inline] + pub unsafe fn try_read(&self) -> bool { + let m = self.mode.get(); + if m >= 0 { + self.mode.set(m + 1); + true + } else { + false + } + } + + #[inline] + pub unsafe fn write(&self) { + if self.mode.replace(-1) != 0 { + rtabort!("rwlock locked for reading") + } + } + + #[inline] + pub unsafe fn try_write(&self) -> bool { + if self.mode.get() == 0 { + self.mode.set(-1); + true + } else { + false + } + } + + #[inline] + pub unsafe fn read_unlock(&self) { + self.mode.set(self.mode.get() - 1); + } + + #[inline] + pub unsafe fn write_unlock(&self) { + assert_eq!(self.mode.replace(0), -1); + } + + #[inline] + pub unsafe fn destroy(&self) {} +} diff --git a/library/std/src/sys/unsupported/mod.rs b/library/std/src/sys/unsupported/mod.rs index a1276193bda..7bf6d40b76d 100644 --- a/library/std/src/sys/unsupported/mod.rs +++ b/library/std/src/sys/unsupported/mod.rs @@ -4,11 +4,10 @@ pub mod alloc; pub mod args; #[path = "../unix/cmath.rs"] pub mod cmath; -pub mod condvar; pub mod env; pub mod fs; pub mod io; -pub mod mutex; +pub mod locks; pub mod net; pub mod os; #[path = "../unix/os_str.rs"] @@ -17,7 +16,6 @@ pub mod os_str; pub mod path; pub mod pipe; pub mod process; -pub mod rwlock; pub mod stdio; pub mod thread; #[cfg(target_thread_local)] diff --git a/library/std/src/sys/unsupported/mutex.rs b/library/std/src/sys/unsupported/mutex.rs deleted file mode 100644 index b3203c16c50..00000000000 --- a/library/std/src/sys/unsupported/mutex.rs +++ /dev/null @@ -1,61 +0,0 @@ -use crate::cell::Cell; - -pub struct Mutex { - // This platform has no threads, so we can use a Cell here. - locked: Cell, -} - -pub type MovableMutex = Mutex; - -unsafe impl Send for Mutex {} -unsafe impl Sync for Mutex {} // no threads on this platform - -impl Mutex { - pub const fn new() -> Mutex { - Mutex { locked: Cell::new(false) } - } - - #[inline] - pub unsafe fn init(&mut self) {} - - #[inline] - pub unsafe fn lock(&self) { - assert_eq!(self.locked.replace(true), false, "cannot recursively acquire mutex"); - } - - #[inline] - pub unsafe fn unlock(&self) { - self.locked.set(false); - } - - #[inline] - pub unsafe fn try_lock(&self) -> bool { - self.locked.replace(true) == false - } - - #[inline] - pub unsafe fn destroy(&self) {} -} - -// All empty stubs because this platform does not yet support threads, so lock -// acquisition always succeeds. -pub struct ReentrantMutex {} - -impl ReentrantMutex { - pub const unsafe fn uninitialized() -> ReentrantMutex { - ReentrantMutex {} - } - - pub unsafe fn init(&self) {} - - pub unsafe fn lock(&self) {} - - #[inline] - pub unsafe fn try_lock(&self) -> bool { - true - } - - pub unsafe fn unlock(&self) {} - - pub unsafe fn destroy(&self) {} -} diff --git a/library/std/src/sys/unsupported/rwlock.rs b/library/std/src/sys/unsupported/rwlock.rs deleted file mode 100644 index 8438adeb5b5..00000000000 --- a/library/std/src/sys/unsupported/rwlock.rs +++ /dev/null @@ -1,68 +0,0 @@ -use crate::cell::Cell; - -pub struct RWLock { - // This platform has no threads, so we can use a Cell here. - mode: Cell, -} - -pub type MovableRWLock = RWLock; - -unsafe impl Send for RWLock {} -unsafe impl Sync for RWLock {} // no threads on this platform - -impl RWLock { - pub const fn new() -> RWLock { - RWLock { mode: Cell::new(0) } - } - - #[inline] - pub unsafe fn read(&self) { - let m = self.mode.get(); - if m >= 0 { - self.mode.set(m + 1); - } else { - rtabort!("rwlock locked for writing"); - } - } - - #[inline] - pub unsafe fn try_read(&self) -> bool { - let m = self.mode.get(); - if m >= 0 { - self.mode.set(m + 1); - true - } else { - false - } - } - - #[inline] - pub unsafe fn write(&self) { - if self.mode.replace(-1) != 0 { - rtabort!("rwlock locked for reading") - } - } - - #[inline] - pub unsafe fn try_write(&self) -> bool { - if self.mode.get() == 0 { - self.mode.set(-1); - true - } else { - false - } - } - - #[inline] - pub unsafe fn read_unlock(&self) { - self.mode.set(self.mode.get() - 1); - } - - #[inline] - pub unsafe fn write_unlock(&self) { - assert_eq!(self.mode.replace(0), -1); - } - - #[inline] - pub unsafe fn destroy(&self) {} -} diff --git a/library/std/src/sys/wasi/mod.rs b/library/std/src/sys/wasi/mod.rs index f878941939c..683a07a34dc 100644 --- a/library/std/src/sys/wasi/mod.rs +++ b/library/std/src/sys/wasi/mod.rs @@ -22,14 +22,12 @@ pub mod alloc; pub mod args; #[path = "../unix/cmath.rs"] pub mod cmath; -#[path = "../unsupported/condvar.rs"] -pub mod condvar; pub mod env; pub mod fd; pub mod fs; pub mod io; -#[path = "../unsupported/mutex.rs"] -pub mod mutex; +#[path = "../unsupported/locks/mod.rs"] +pub mod locks; pub mod net; pub mod os; #[path = "../unix/os_str.rs"] @@ -40,8 +38,6 @@ pub mod path; pub mod pipe; #[path = "../unsupported/process.rs"] pub mod process; -#[path = "../unsupported/rwlock.rs"] -pub mod rwlock; pub mod stdio; pub mod thread; #[path = "../unsupported/thread_local_dtor.rs"] diff --git a/library/std/src/sys/wasm/atomics/condvar.rs b/library/std/src/sys/wasm/atomics/condvar.rs index 0c1c076cc91..f06c07c5409 100644 --- a/library/std/src/sys/wasm/atomics/condvar.rs +++ b/library/std/src/sys/wasm/atomics/condvar.rs @@ -2,7 +2,7 @@ use crate::arch::wasm32; use crate::cmp; use crate::mem; use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst}; -use crate::sys::mutex::Mutex; +use crate::sys::locks::Mutex; use crate::time::Duration; pub struct Condvar { diff --git a/library/std/src/sys/wasm/atomics/rwlock.rs b/library/std/src/sys/wasm/atomics/rwlock.rs index 64eaa2fc482..1cca809764c 100644 --- a/library/std/src/sys/wasm/atomics/rwlock.rs +++ b/library/std/src/sys/wasm/atomics/rwlock.rs @@ -1,6 +1,5 @@ use crate::cell::UnsafeCell; -use crate::sys::condvar::Condvar; -use crate::sys::mutex::Mutex; +use crate::sys::locks::{Condvar, Mutex}; pub struct RWLock { lock: Mutex, diff --git a/library/std/src/sys/wasm/mod.rs b/library/std/src/sys/wasm/mod.rs index c81d653a5e3..9f6700caf14 100644 --- a/library/std/src/sys/wasm/mod.rs +++ b/library/std/src/sys/wasm/mod.rs @@ -50,22 +50,23 @@ pub mod time; cfg_if::cfg_if! { if #[cfg(target_feature = "atomics")] { #[path = "atomics/condvar.rs"] - pub mod condvar; + mod condvar; #[path = "atomics/mutex.rs"] - pub mod mutex; + mod mutex; #[path = "atomics/rwlock.rs"] - pub mod rwlock; + mod rwlock; + pub mod locks { + pub use super::condvar::*; + pub use super::mutex::*; + pub use super::rwlock::*; + } #[path = "atomics/futex.rs"] pub mod futex; #[path = "atomics/thread.rs"] pub mod thread; } else { - #[path = "../unsupported/condvar.rs"] - pub mod condvar; - #[path = "../unsupported/mutex.rs"] - pub mod mutex; - #[path = "../unsupported/rwlock.rs"] - pub mod rwlock; + #[path = "../unsupported/locks/mod.rs"] + pub mod locks; #[path = "../unsupported/thread.rs"] pub mod thread; } diff --git a/library/std/src/sys/windows/condvar.rs b/library/std/src/sys/windows/condvar.rs deleted file mode 100644 index 44547a5c51a..00000000000 --- a/library/std/src/sys/windows/condvar.rs +++ /dev/null @@ -1,58 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::sys::c; -use crate::sys::mutex::{self, Mutex}; -use crate::sys::os; -use crate::time::Duration; - -pub struct Condvar { - inner: UnsafeCell, -} - -pub type MovableCondvar = Condvar; - -unsafe impl Send for Condvar {} -unsafe impl Sync for Condvar {} - -impl Condvar { - pub const fn new() -> Condvar { - Condvar { inner: UnsafeCell::new(c::CONDITION_VARIABLE_INIT) } - } - - #[inline] - pub unsafe fn init(&mut self) {} - - #[inline] - pub unsafe fn wait(&self, mutex: &Mutex) { - let r = c::SleepConditionVariableSRW(self.inner.get(), mutex::raw(mutex), c::INFINITE, 0); - debug_assert!(r != 0); - } - - pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { - let r = c::SleepConditionVariableSRW( - self.inner.get(), - mutex::raw(mutex), - super::dur2timeout(dur), - 0, - ); - if r == 0 { - debug_assert_eq!(os::errno() as usize, c::ERROR_TIMEOUT as usize); - false - } else { - true - } - } - - #[inline] - pub unsafe fn notify_one(&self) { - c::WakeConditionVariable(self.inner.get()) - } - - #[inline] - pub unsafe fn notify_all(&self) { - c::WakeAllConditionVariable(self.inner.get()) - } - - pub unsafe fn destroy(&self) { - // ... - } -} diff --git a/library/std/src/sys/windows/locks/condvar.rs b/library/std/src/sys/windows/locks/condvar.rs new file mode 100644 index 00000000000..dfd8cfdceee --- /dev/null +++ b/library/std/src/sys/windows/locks/condvar.rs @@ -0,0 +1,58 @@ +use crate::cell::UnsafeCell; +use crate::sys::c; +use crate::sys::locks::{mutex, Mutex}; +use crate::sys::os; +use crate::time::Duration; + +pub struct Condvar { + inner: UnsafeCell, +} + +pub type MovableCondvar = Condvar; + +unsafe impl Send for Condvar {} +unsafe impl Sync for Condvar {} + +impl Condvar { + pub const fn new() -> Condvar { + Condvar { inner: UnsafeCell::new(c::CONDITION_VARIABLE_INIT) } + } + + #[inline] + pub unsafe fn init(&mut self) {} + + #[inline] + pub unsafe fn wait(&self, mutex: &Mutex) { + let r = c::SleepConditionVariableSRW(self.inner.get(), mutex::raw(mutex), c::INFINITE, 0); + debug_assert!(r != 0); + } + + pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { + let r = c::SleepConditionVariableSRW( + self.inner.get(), + mutex::raw(mutex), + crate::sys::windows::dur2timeout(dur), + 0, + ); + if r == 0 { + debug_assert_eq!(os::errno() as usize, c::ERROR_TIMEOUT as usize); + false + } else { + true + } + } + + #[inline] + pub unsafe fn notify_one(&self) { + c::WakeConditionVariable(self.inner.get()) + } + + #[inline] + pub unsafe fn notify_all(&self) { + c::WakeAllConditionVariable(self.inner.get()) + } + + pub unsafe fn destroy(&self) { + // ... + } +} diff --git a/library/std/src/sys/windows/locks/mod.rs b/library/std/src/sys/windows/locks/mod.rs new file mode 100644 index 00000000000..5634f106339 --- /dev/null +++ b/library/std/src/sys/windows/locks/mod.rs @@ -0,0 +1,6 @@ +mod condvar; +mod mutex; +mod rwlock; +pub use condvar::{Condvar, MovableCondvar}; +pub use mutex::{MovableMutex, Mutex, ReentrantMutex}; +pub use rwlock::{MovableRWLock, RWLock}; diff --git a/library/std/src/sys/windows/locks/mutex.rs b/library/std/src/sys/windows/locks/mutex.rs new file mode 100644 index 00000000000..56f91ebe582 --- /dev/null +++ b/library/std/src/sys/windows/locks/mutex.rs @@ -0,0 +1,96 @@ +//! System Mutexes +//! +//! The Windows implementation of mutexes is a little odd and it might 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. + +use crate::cell::UnsafeCell; +use crate::mem::MaybeUninit; +use crate::sys::c; + +pub struct Mutex { + srwlock: UnsafeCell, +} + +// Windows SRW Locks are movable (while not borrowed). +pub type MovableMutex = Mutex; + +unsafe impl Send for Mutex {} +unsafe impl Sync for Mutex {} + +#[inline] +pub unsafe fn raw(m: &Mutex) -> c::PSRWLOCK { + m.srwlock.get() +} + +impl Mutex { + pub const fn new() -> Mutex { + Mutex { srwlock: UnsafeCell::new(c::SRWLOCK_INIT) } + } + #[inline] + pub unsafe fn init(&mut self) {} + + #[inline] + pub unsafe fn lock(&self) { + c::AcquireSRWLockExclusive(raw(self)); + } + + #[inline] + pub unsafe fn try_lock(&self) -> bool { + c::TryAcquireSRWLockExclusive(raw(self)) != 0 + } + + #[inline] + pub unsafe fn unlock(&self) { + c::ReleaseSRWLockExclusive(raw(self)); + } + + #[inline] + pub unsafe fn destroy(&self) { + // SRWLock does not need to be destroyed. + } +} + +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())); + } +} diff --git a/library/std/src/sys/windows/locks/rwlock.rs b/library/std/src/sys/windows/locks/rwlock.rs new file mode 100644 index 00000000000..b7a5b1e7acc --- /dev/null +++ b/library/std/src/sys/windows/locks/rwlock.rs @@ -0,0 +1,46 @@ +use crate::cell::UnsafeCell; +use crate::sys::c; + +pub struct RWLock { + inner: UnsafeCell, +} + +pub type MovableRWLock = RWLock; + +unsafe impl Send for RWLock {} +unsafe impl Sync for RWLock {} + +impl RWLock { + pub const fn new() -> RWLock { + RWLock { inner: UnsafeCell::new(c::SRWLOCK_INIT) } + } + #[inline] + pub unsafe fn read(&self) { + c::AcquireSRWLockShared(self.inner.get()) + } + #[inline] + pub unsafe fn try_read(&self) -> bool { + c::TryAcquireSRWLockShared(self.inner.get()) != 0 + } + #[inline] + pub unsafe fn write(&self) { + c::AcquireSRWLockExclusive(self.inner.get()) + } + #[inline] + pub unsafe fn try_write(&self) -> bool { + c::TryAcquireSRWLockExclusive(self.inner.get()) != 0 + } + #[inline] + pub unsafe fn read_unlock(&self) { + c::ReleaseSRWLockShared(self.inner.get()) + } + #[inline] + pub unsafe fn write_unlock(&self) { + c::ReleaseSRWLockExclusive(self.inner.get()) + } + + #[inline] + pub unsafe fn destroy(&self) { + // ... + } +} diff --git a/library/std/src/sys/windows/mod.rs b/library/std/src/sys/windows/mod.rs index 6097e628768..62814eaaa56 100644 --- a/library/std/src/sys/windows/mod.rs +++ b/library/std/src/sys/windows/mod.rs @@ -16,13 +16,12 @@ pub mod alloc; pub mod args; pub mod c; pub mod cmath; -pub mod condvar; pub mod env; pub mod fs; pub mod handle; pub mod io; +pub mod locks; pub mod memchr; -pub mod mutex; pub mod net; pub mod os; pub mod os_str; @@ -30,7 +29,6 @@ pub mod path; pub mod pipe; pub mod process; pub mod rand; -pub mod rwlock; pub mod thread; pub mod thread_local_dtor; pub mod thread_local_key; diff --git a/library/std/src/sys/windows/mutex.rs b/library/std/src/sys/windows/mutex.rs deleted file mode 100644 index 56f91ebe582..00000000000 --- a/library/std/src/sys/windows/mutex.rs +++ /dev/null @@ -1,96 +0,0 @@ -//! System Mutexes -//! -//! The Windows implementation of mutexes is a little odd and it might 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. - -use crate::cell::UnsafeCell; -use crate::mem::MaybeUninit; -use crate::sys::c; - -pub struct Mutex { - srwlock: UnsafeCell, -} - -// Windows SRW Locks are movable (while not borrowed). -pub type MovableMutex = Mutex; - -unsafe impl Send for Mutex {} -unsafe impl Sync for Mutex {} - -#[inline] -pub unsafe fn raw(m: &Mutex) -> c::PSRWLOCK { - m.srwlock.get() -} - -impl Mutex { - pub const fn new() -> Mutex { - Mutex { srwlock: UnsafeCell::new(c::SRWLOCK_INIT) } - } - #[inline] - pub unsafe fn init(&mut self) {} - - #[inline] - pub unsafe fn lock(&self) { - c::AcquireSRWLockExclusive(raw(self)); - } - - #[inline] - pub unsafe fn try_lock(&self) -> bool { - c::TryAcquireSRWLockExclusive(raw(self)) != 0 - } - - #[inline] - pub unsafe fn unlock(&self) { - c::ReleaseSRWLockExclusive(raw(self)); - } - - #[inline] - pub unsafe fn destroy(&self) { - // SRWLock does not need to be destroyed. - } -} - -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())); - } -} diff --git a/library/std/src/sys/windows/rwlock.rs b/library/std/src/sys/windows/rwlock.rs deleted file mode 100644 index b7a5b1e7acc..00000000000 --- a/library/std/src/sys/windows/rwlock.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::sys::c; - -pub struct RWLock { - inner: UnsafeCell, -} - -pub type MovableRWLock = RWLock; - -unsafe impl Send for RWLock {} -unsafe impl Sync for RWLock {} - -impl RWLock { - pub const fn new() -> RWLock { - RWLock { inner: UnsafeCell::new(c::SRWLOCK_INIT) } - } - #[inline] - pub unsafe fn read(&self) { - c::AcquireSRWLockShared(self.inner.get()) - } - #[inline] - pub unsafe fn try_read(&self) -> bool { - c::TryAcquireSRWLockShared(self.inner.get()) != 0 - } - #[inline] - pub unsafe fn write(&self) { - c::AcquireSRWLockExclusive(self.inner.get()) - } - #[inline] - pub unsafe fn try_write(&self) -> bool { - c::TryAcquireSRWLockExclusive(self.inner.get()) != 0 - } - #[inline] - pub unsafe fn read_unlock(&self) { - c::ReleaseSRWLockShared(self.inner.get()) - } - #[inline] - pub unsafe fn write_unlock(&self) { - c::ReleaseSRWLockExclusive(self.inner.get()) - } - - #[inline] - pub unsafe fn destroy(&self) { - // ... - } -} -- cgit 1.4.1-3-g733a5