summaryrefslogtreecommitdiff
path: root/library/std/src/sys/cloudabi/rwlock.rs
diff options
context:
space:
mode:
Diffstat (limited to 'library/std/src/sys/cloudabi/rwlock.rs')
-rw-r--r--library/std/src/sys/cloudabi/rwlock.rs218
1 files changed, 218 insertions, 0 deletions
diff --git a/library/std/src/sys/cloudabi/rwlock.rs b/library/std/src/sys/cloudabi/rwlock.rs
new file mode 100644
index 00000000000..b8af5af1d70
--- /dev/null
+++ b/library/std/src/sys/cloudabi/rwlock.rs
@@ -0,0 +1,218 @@
+use crate::cell::UnsafeCell;
+use crate::mem;
+use crate::mem::MaybeUninit;
+use crate::sync::atomic::{AtomicU32, Ordering};
+use crate::sys::cloudabi::abi;
+
+extern "C" {
+ #[thread_local]
+ static __pthread_thread_id: abi::tid;
+}
+
+#[thread_local]
+static mut RDLOCKS_ACQUIRED: u32 = 0;
+
+pub struct RWLock {
+ lock: UnsafeCell<AtomicU32>,
+}
+
+pub unsafe fn raw(r: &RWLock) -> *mut AtomicU32 {
+ r.lock.get()
+}
+
+unsafe impl Send for RWLock {}
+unsafe impl Sync for RWLock {}
+
+const NEW: RWLock = RWLock { lock: UnsafeCell::new(AtomicU32::new(abi::LOCK_UNLOCKED.0)) };
+
+impl RWLock {
+ pub const fn new() -> RWLock {
+ NEW
+ }
+
+ pub unsafe fn try_read(&self) -> bool {
+ let lock = self.lock.get();
+ let mut old = abi::LOCK_UNLOCKED.0;
+ while let Err(cur) =
+ (*lock).compare_exchange_weak(old, old + 1, Ordering::Acquire, Ordering::Relaxed)
+ {
+ if (cur & abi::LOCK_WRLOCKED.0) != 0 {
+ // Another thread already has a write lock.
+ assert_ne!(
+ old & !abi::LOCK_KERNEL_MANAGED.0,
+ __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
+ "Attempted to acquire a read lock while holding a write lock"
+ );
+ return false;
+ } else if (old & abi::LOCK_KERNEL_MANAGED.0) != 0 && RDLOCKS_ACQUIRED == 0 {
+ // Lock has threads waiting for the lock. Only acquire
+ // the lock if we have already acquired read locks. In
+ // that case, it is justified to acquire this lock to
+ // prevent a deadlock.
+ return false;
+ }
+ old = cur;
+ }
+
+ RDLOCKS_ACQUIRED += 1;
+ true
+ }
+
+ pub unsafe fn read(&self) {
+ if !self.try_read() {
+ // Call into the kernel to acquire a read lock.
+ let lock = self.lock.get();
+ let subscription = abi::subscription {
+ type_: abi::eventtype::LOCK_RDLOCK,
+ union: abi::subscription_union {
+ lock: abi::subscription_lock {
+ lock: lock as *mut abi::lock,
+ lock_scope: abi::scope::PRIVATE,
+ },
+ },
+ ..mem::zeroed()
+ };
+ let mut event = MaybeUninit::<abi::event>::uninit();
+ let mut nevents = MaybeUninit::<usize>::uninit();
+ let ret = abi::poll(&subscription, event.as_mut_ptr(), 1, nevents.as_mut_ptr());
+ assert_eq!(ret, abi::errno::SUCCESS, "Failed to acquire read lock");
+ let event = event.assume_init();
+ assert_eq!(event.error, abi::errno::SUCCESS, "Failed to acquire read lock");
+
+ RDLOCKS_ACQUIRED += 1;
+ }
+ }
+
+ pub unsafe fn read_unlock(&self) {
+ // Perform a read unlock. We can do this in userspace, except when
+ // other threads are blocked and we are performing the last unlock.
+ // In that case, call into the kernel.
+ //
+ // Other threads may attempt to increment the read lock count,
+ // meaning that the call into the kernel could be spurious. To
+ // prevent this from happening, upgrade to a write lock first. This
+ // allows us to call into the kernel, having the guarantee that the
+ // lock value will not change in the meantime.
+ assert!(RDLOCKS_ACQUIRED > 0, "Bad lock count");
+ let mut old = 1;
+ loop {
+ let lock = self.lock.get();
+ if old == 1 | abi::LOCK_KERNEL_MANAGED.0 {
+ // Last read lock while threads are waiting. Attempt to upgrade
+ // to a write lock before calling into the kernel to unlock.
+ if let Err(cur) = (*lock).compare_exchange_weak(
+ old,
+ __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0 | abi::LOCK_KERNEL_MANAGED.0,
+ Ordering::Acquire,
+ Ordering::Relaxed,
+ ) {
+ old = cur;
+ } else {
+ // Call into the kernel to unlock.
+ let ret = abi::lock_unlock(lock as *mut abi::lock, abi::scope::PRIVATE);
+ assert_eq!(ret, abi::errno::SUCCESS, "Failed to write unlock a rwlock");
+ break;
+ }
+ } else {
+ // No threads waiting or not the last read lock. Just decrement
+ // the read lock count.
+ assert_ne!(old & !abi::LOCK_KERNEL_MANAGED.0, 0, "This rwlock is not locked");
+ assert_eq!(
+ old & abi::LOCK_WRLOCKED.0,
+ 0,
+ "Attempted to read-unlock a write-locked rwlock"
+ );
+ if let Err(cur) = (*lock).compare_exchange_weak(
+ old,
+ old - 1,
+ Ordering::Acquire,
+ Ordering::Relaxed,
+ ) {
+ old = cur;
+ } else {
+ break;
+ }
+ }
+ }
+
+ RDLOCKS_ACQUIRED -= 1;
+ }
+
+ pub unsafe fn try_write(&self) -> bool {
+ // Attempt to acquire the lock.
+ let lock = self.lock.get();
+ if let Err(old) = (*lock).compare_exchange(
+ abi::LOCK_UNLOCKED.0,
+ __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
+ Ordering::Acquire,
+ Ordering::Relaxed,
+ ) {
+ // Failure. Crash upon recursive acquisition.
+ assert_ne!(
+ old & !abi::LOCK_KERNEL_MANAGED.0,
+ __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
+ "Attempted to recursive write-lock a rwlock",
+ );
+ false
+ } else {
+ // Success.
+ true
+ }
+ }
+
+ pub unsafe fn write(&self) {
+ if !self.try_write() {
+ // Call into the kernel to acquire a write lock.
+ let lock = self.lock.get();
+ let subscription = abi::subscription {
+ type_: abi::eventtype::LOCK_WRLOCK,
+ union: abi::subscription_union {
+ lock: abi::subscription_lock {
+ lock: lock as *mut abi::lock,
+ lock_scope: abi::scope::PRIVATE,
+ },
+ },
+ ..mem::zeroed()
+ };
+ let mut event = MaybeUninit::<abi::event>::uninit();
+ let mut nevents = MaybeUninit::<usize>::uninit();
+ let ret = abi::poll(&subscription, event.as_mut_ptr(), 1, nevents.as_mut_ptr());
+ assert_eq!(ret, abi::errno::SUCCESS, "Failed to acquire write lock");
+ let event = event.assume_init();
+ assert_eq!(event.error, abi::errno::SUCCESS, "Failed to acquire write lock");
+ }
+ }
+
+ pub unsafe fn write_unlock(&self) {
+ let lock = self.lock.get();
+ assert_eq!(
+ (*lock).load(Ordering::Relaxed) & !abi::LOCK_KERNEL_MANAGED.0,
+ __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
+ "This rwlock is not write-locked by this thread"
+ );
+
+ if !(*lock)
+ .compare_exchange(
+ __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
+ abi::LOCK_UNLOCKED.0,
+ Ordering::Release,
+ Ordering::Relaxed,
+ )
+ .is_ok()
+ {
+ // Lock is managed by kernelspace. Call into the kernel
+ // to unblock waiting threads.
+ let ret = abi::lock_unlock(lock as *mut abi::lock, abi::scope::PRIVATE);
+ assert_eq!(ret, abi::errno::SUCCESS, "Failed to write unlock a rwlock");
+ }
+ }
+
+ pub unsafe fn destroy(&self) {
+ let lock = self.lock.get();
+ assert_eq!(
+ (*lock).load(Ordering::Relaxed),
+ abi::LOCK_UNLOCKED.0,
+ "Attempted to destroy locked rwlock"
+ );
+ }
+}