diff options
| author | Mara Bos <m-ou.se@m-ou.se> | 2022-06-03 17:04:14 +0200 |
|---|---|---|
| committer | Mara Bos <m-ou.se@m-ou.se> | 2022-06-03 17:04:14 +0200 |
| commit | 6a417d482899e13b1fbef5f5f9962f59e89e9e53 (patch) | |
| tree | 6bf812e545a816dd9d361be00b6f9a3cfe200f36 /library/std/src/sys_common | |
| parent | ac5aa1ded529cd8317b351ba952ff9cd78b1e172 (diff) | |
Lazily allocate+initialize locks.
Diffstat (limited to 'library/std/src/sys_common')
| -rw-r--r-- | library/std/src/sys_common/condvar.rs | 4 | ||||
| -rw-r--r-- | library/std/src/sys_common/condvar/check.rs | 3 | ||||
| -rw-r--r-- | library/std/src/sys_common/lazy_box.rs | 77 | ||||
| -rw-r--r-- | library/std/src/sys_common/mod.rs | 1 | ||||
| -rw-r--r-- | library/std/src/sys_common/mutex.rs | 4 | ||||
| -rw-r--r-- | library/std/src/sys_common/rwlock.rs | 2 |
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 |
