about summary refs log tree commit diff
path: root/library/std/src/sys_common
diff options
context:
space:
mode:
authorMara Bos <m-ou.se@m-ou.se>2022-06-03 17:04:14 +0200
committerMara Bos <m-ou.se@m-ou.se>2022-06-03 17:04:14 +0200
commit6a417d482899e13b1fbef5f5f9962f59e89e9e53 (patch)
tree6bf812e545a816dd9d361be00b6f9a3cfe200f36 /library/std/src/sys_common
parentac5aa1ded529cd8317b351ba952ff9cd78b1e172 (diff)
Lazily allocate+initialize locks.
Diffstat (limited to 'library/std/src/sys_common')
-rw-r--r--library/std/src/sys_common/condvar.rs4
-rw-r--r--library/std/src/sys_common/condvar/check.rs3
-rw-r--r--library/std/src/sys_common/lazy_box.rs77
-rw-r--r--library/std/src/sys_common/mod.rs1
-rw-r--r--library/std/src/sys_common/mutex.rs4
-rw-r--r--library/std/src/sys_common/rwlock.rs2
6 files changed, 83 insertions, 8 deletions
diff --git a/library/std/src/sys_common/condvar.rs b/library/std/src/sys_common/condvar.rs
index 4e55340d6aa..1def0518e0a 100644
--- a/library/std/src/sys_common/condvar.rs
+++ b/library/std/src/sys_common/condvar.rs
@@ -15,9 +15,7 @@ pub struct Condvar {
 impl Condvar {
     /// Creates a new condition variable for use.
     pub fn new() -> Self {
-        let mut c = imp::MovableCondvar::from(imp::Condvar::new());
-        unsafe { c.init() };
-        Self { inner: c, check: CondvarCheck::new() }
+        Self { inner: imp::MovableCondvar::new(), check: CondvarCheck::new() }
     }
 
     /// Signals one waiter on this condition variable to wake up.
diff --git a/library/std/src/sys_common/condvar/check.rs b/library/std/src/sys_common/condvar/check.rs
index d0d0d596518..ce8f3670487 100644
--- a/library/std/src/sys_common/condvar/check.rs
+++ b/library/std/src/sys_common/condvar/check.rs
@@ -1,6 +1,7 @@
 use crate::ptr;
 use crate::sync::atomic::{AtomicPtr, Ordering};
 use crate::sys::locks as imp;
+use crate::sys_common::lazy_box::{LazyBox, LazyInit};
 use crate::sys_common::mutex::MovableMutex;
 
 pub trait CondvarCheck {
@@ -9,7 +10,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<imp::Mutex> {
+impl<T: LazyInit> CondvarCheck for LazyBox<T> {
     type Check = SameMutexCheck;
 }
 
diff --git a/library/std/src/sys_common/lazy_box.rs b/library/std/src/sys_common/lazy_box.rs
new file mode 100644
index 00000000000..647c13d2437
--- /dev/null
+++ b/library/std/src/sys_common/lazy_box.rs
@@ -0,0 +1,77 @@
+#![allow(dead_code)] // Only used on some platforms.
+
+// This is used to wrap pthread {Mutex, Condvar, RwLock} in.
+
+use crate::marker::PhantomData;
+use crate::ops::{Deref, DerefMut};
+use crate::ptr::null_mut;
+use crate::sync::atomic::{
+    AtomicPtr,
+    Ordering::{AcqRel, Acquire},
+};
+
+pub(crate) struct LazyBox<T: LazyInit> {
+    ptr: AtomicPtr<T>,
+    _phantom: PhantomData<T>,
+}
+
+pub(crate) trait LazyInit {
+    /// This is called before the box is allocated, to provide the value to
+    /// move into the new box.
+    ///
+    /// It might be called more than once per LazyBox, as multiple threads
+    /// might race to initialize it concurrently, each constructing and initializing
+    /// their own box. (All but one of them will be destroyed right after.)
+    fn init() -> Box<Self>;
+}
+
+impl<T: LazyInit> LazyBox<T> {
+    #[inline]
+    pub const fn new() -> Self {
+        Self { ptr: AtomicPtr::new(null_mut()), _phantom: PhantomData }
+    }
+
+    #[inline]
+    fn get_pointer(&self) -> *mut T {
+        let ptr = self.ptr.load(Acquire);
+        if ptr.is_null() { self.initialize() } else { ptr }
+    }
+
+    #[cold]
+    fn initialize(&self) -> *mut T {
+        let new_ptr = Box::into_raw(T::init());
+        match self.ptr.compare_exchange(null_mut(), new_ptr, AcqRel, Acquire) {
+            Ok(_) => new_ptr,
+            Err(ptr) => {
+                // Lost the race to another thread.
+                // Drop the box we created, and use the one from the other thread instead.
+                drop(unsafe { Box::from_raw(new_ptr) });
+                ptr
+            }
+        }
+    }
+}
+
+impl<T: LazyInit> Deref for LazyBox<T> {
+    type Target = T;
+    #[inline]
+    fn deref(&self) -> &T {
+        unsafe { &*self.get_pointer() }
+    }
+}
+
+impl<T: LazyInit> DerefMut for LazyBox<T> {
+    #[inline]
+    fn deref_mut(&mut self) -> &mut T {
+        unsafe { &mut *self.get_pointer() }
+    }
+}
+
+impl<T: LazyInit> Drop for LazyBox<T> {
+    fn drop(&mut self) {
+        let ptr = *self.ptr.get_mut();
+        if !ptr.is_null() {
+            drop(unsafe { Box::from_raw(ptr) });
+        }
+    }
+}
diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs
index 804727fbc54..80f56bf7522 100644
--- a/library/std/src/sys_common/mod.rs
+++ b/library/std/src/sys_common/mod.rs
@@ -24,6 +24,7 @@ pub mod backtrace;
 pub mod condvar;
 pub mod fs;
 pub mod io;
+pub mod lazy_box;
 pub mod memchr;
 pub mod mutex;
 pub mod process;
diff --git a/library/std/src/sys_common/mutex.rs b/library/std/src/sys_common/mutex.rs
index c0a681246a4..36ea888d8de 100644
--- a/library/std/src/sys_common/mutex.rs
+++ b/library/std/src/sys_common/mutex.rs
@@ -61,9 +61,7 @@ unsafe impl Sync for MovableMutex {}
 impl MovableMutex {
     /// Creates a new mutex.
     pub fn new() -> Self {
-        let mut mutex = imp::MovableMutex::from(imp::Mutex::new());
-        unsafe { mutex.init() };
-        Self(mutex)
+        Self(imp::MovableMutex::new())
     }
 
     pub(super) fn raw(&self) -> &imp::Mutex {
diff --git a/library/std/src/sys_common/rwlock.rs b/library/std/src/sys_common/rwlock.rs
index 13a75705fec..abc9fd561f1 100644
--- a/library/std/src/sys_common/rwlock.rs
+++ b/library/std/src/sys_common/rwlock.rs
@@ -74,7 +74,7 @@ pub struct MovableRwLock(imp::MovableRwLock);
 impl MovableRwLock {
     /// Creates a new reader-writer lock for use.
     pub fn new() -> Self {
-        Self(imp::MovableRwLock::from(imp::RwLock::new()))
+        Self(imp::MovableRwLock::new())
     }
 
     /// Acquires shared access to the underlying lock, blocking the current