Skip to content

Commit 6a417d4

Browse files
committed
Lazily allocate+initialize locks.
1 parent ac5aa1d commit 6a417d4

File tree

17 files changed

+145
-36
lines changed

17 files changed

+145
-36
lines changed

library/std/src/sys/sgx/condvar.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::sys::locks::Mutex;
2+
use crate::sys_common::lazy_box::{LazyBox, LazyInit};
23
use crate::time::Duration;
34

45
use super::waitqueue::{SpinMutex, WaitQueue, WaitVariable};
@@ -7,16 +8,19 @@ pub struct Condvar {
78
inner: SpinMutex<WaitVariable<()>>,
89
}
910

10-
pub type MovableCondvar = Box<Condvar>;
11+
pub(crate) type MovableCondvar = LazyBox<Condvar>;
12+
13+
impl LazyInit for Condvar {
14+
fn init() -> Box<Self> {
15+
Box::new(Self::new())
16+
}
17+
}
1118

1219
impl Condvar {
1320
pub const fn new() -> Condvar {
1421
Condvar { inner: SpinMutex::new(WaitVariable::new(())) }
1522
}
1623

17-
#[inline]
18-
pub unsafe fn init(&mut self) {}
19-
2024
#[inline]
2125
pub unsafe fn notify_one(&self) {
2226
let _ = WaitQueue::notify_one(self.inner.lock());

library/std/src/sys/sgx/mutex.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
use super::waitqueue::{try_lock_or_false, SpinMutex, WaitQueue, WaitVariable};
2+
use crate::sys_common::lazy_box::{LazyBox, LazyInit};
23

34
pub struct Mutex {
45
inner: SpinMutex<WaitVariable<bool>>,
56
}
67

78
// not movable: see UnsafeList implementation
8-
pub type MovableMutex = Box<Mutex>;
9+
pub(crate) type MovableMutex = LazyBox<Mutex>;
10+
11+
impl LazyInit for Mutex {
12+
fn init() -> Box<Self> {
13+
Box::new(Self::new())
14+
}
15+
}
916

1017
// Implementation according to “Operating Systems: Three Easy Pieces”, chapter 28
1118
impl Mutex {

library/std/src/sys/sgx/rwlock.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
mod tests;
33

44
use crate::num::NonZeroUsize;
5+
use crate::sys_common::lazy_box::{LazyBox, LazyInit};
56

67
use super::waitqueue::{
78
try_lock_or_false, NotifiedTcs, SpinMutex, SpinMutexGuard, WaitQueue, WaitVariable,
@@ -13,7 +14,13 @@ pub struct RwLock {
1314
writer: SpinMutex<WaitVariable<bool>>,
1415
}
1516

16-
pub type MovableRwLock = Box<RwLock>;
17+
pub(crate) type MovableRwLock = LazyBox<RwLock>;
18+
19+
impl LazyInit for RwLock {
20+
fn init() -> Box<Self> {
21+
Box::new(Self::new())
22+
}
23+
}
1724

1825
// Check at compile time that RwLock size matches C definition (see test_c_rwlock_initializer below)
1926
//

library/std/src/sys/unix/locks/futex.rs

-3
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,6 @@ impl Condvar {
115115
Self { futex: AtomicU32::new(0) }
116116
}
117117

118-
#[inline]
119-
pub unsafe fn init(&mut self) {}
120-
121118
// All the memory orderings here are `Relaxed`,
122119
// because synchronization is done by unlocking and locking the mutex.
123120

library/std/src/sys/unix/locks/mod.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ cfg_if::cfg_if! {
99
))] {
1010
mod futex;
1111
mod futex_rwlock;
12-
pub use futex::{Mutex, MovableMutex, Condvar, MovableCondvar};
13-
pub use futex_rwlock::{RwLock, MovableRwLock};
12+
pub(crate) use futex::{Mutex, MovableMutex, MovableCondvar};
13+
pub(crate) use futex_rwlock::{RwLock, MovableRwLock};
1414
} else {
1515
mod pthread_mutex;
1616
mod pthread_rwlock;
1717
mod pthread_condvar;
18-
pub use pthread_mutex::{Mutex, MovableMutex};
19-
pub use pthread_rwlock::{RwLock, MovableRwLock};
20-
pub use pthread_condvar::{Condvar, MovableCondvar};
18+
pub(crate) use pthread_mutex::{Mutex, MovableMutex};
19+
pub(crate) use pthread_rwlock::{RwLock, MovableRwLock};
20+
pub(crate) use pthread_condvar::MovableCondvar;
2121
}
2222
}

library/std/src/sys/unix/locks/pthread_condvar.rs

+13-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
use crate::cell::UnsafeCell;
22
use crate::sys::locks::{pthread_mutex, Mutex};
3+
use crate::sys_common::lazy_box::{LazyBox, LazyInit};
34
use crate::time::Duration;
45

56
pub struct Condvar {
67
inner: UnsafeCell<libc::pthread_cond_t>,
78
}
89

9-
pub type MovableCondvar = Box<Condvar>;
10+
pub(crate) type MovableCondvar = LazyBox<Condvar>;
1011

1112
unsafe impl Send for Condvar {}
1213
unsafe impl Sync for Condvar {}
@@ -18,6 +19,14 @@ fn saturating_cast_to_time_t(value: u64) -> libc::time_t {
1819
if value > <libc::time_t>::MAX as u64 { <libc::time_t>::MAX } else { value as libc::time_t }
1920
}
2021

22+
impl LazyInit for Condvar {
23+
fn init() -> Box<Self> {
24+
let mut condvar = Box::new(Self::new());
25+
unsafe { condvar.init() };
26+
condvar
27+
}
28+
}
29+
2130
impl Condvar {
2231
pub const fn new() -> Condvar {
2332
// Might be moved and address is changing it is better to avoid
@@ -32,14 +41,14 @@ impl Condvar {
3241
target_os = "android",
3342
target_os = "redox"
3443
))]
35-
pub unsafe fn init(&mut self) {}
44+
unsafe fn init(&mut self) {}
3645

3746
// NOTE: ESP-IDF's PTHREAD_COND_INITIALIZER support is not released yet
3847
// So on that platform, init() should always be called
3948
// Moreover, that platform does not have pthread_condattr_setclock support,
4049
// hence that initialization should be skipped as well
4150
#[cfg(target_os = "espidf")]
42-
pub unsafe fn init(&mut self) {
51+
unsafe fn init(&mut self) {
4352
let r = libc::pthread_cond_init(self.inner.get(), crate::ptr::null());
4453
assert_eq!(r, 0);
4554
}
@@ -52,7 +61,7 @@ impl Condvar {
5261
target_os = "redox",
5362
target_os = "espidf"
5463
)))]
55-
pub unsafe fn init(&mut self) {
64+
unsafe fn init(&mut self) {
5665
use crate::mem::MaybeUninit;
5766
let mut attr = MaybeUninit::<libc::pthread_condattr_t>::uninit();
5867
let r = libc::pthread_condattr_init(attr.as_mut_ptr());

library/std/src/sys/unix/locks/pthread_mutex.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
use crate::cell::UnsafeCell;
22
use crate::mem::MaybeUninit;
33
use crate::sys::cvt_nz;
4+
use crate::sys_common::lazy_box::{LazyBox, LazyInit};
45

56
pub struct Mutex {
67
inner: UnsafeCell<libc::pthread_mutex_t>,
78
}
89

9-
pub type MovableMutex = Box<Mutex>;
10+
pub(crate) type MovableMutex = LazyBox<Mutex>;
1011

1112
#[inline]
1213
pub unsafe fn raw(m: &Mutex) -> *mut libc::pthread_mutex_t {
@@ -16,6 +17,14 @@ pub unsafe fn raw(m: &Mutex) -> *mut libc::pthread_mutex_t {
1617
unsafe impl Send for Mutex {}
1718
unsafe impl Sync for Mutex {}
1819

20+
impl LazyInit for Mutex {
21+
fn init() -> Box<Self> {
22+
let mut mutex = Box::new(Self::new());
23+
unsafe { mutex.init() };
24+
mutex
25+
}
26+
}
27+
1928
impl Mutex {
2029
pub const fn new() -> Mutex {
2130
// Might be moved to a different address, so it is better to avoid

library/std/src/sys/unix/locks/pthread_rwlock.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,24 @@
11
use crate::cell::UnsafeCell;
22
use crate::sync::atomic::{AtomicUsize, Ordering};
3+
use crate::sys_common::lazy_box::{LazyBox, LazyInit};
34

45
pub struct RwLock {
56
inner: UnsafeCell<libc::pthread_rwlock_t>,
67
write_locked: UnsafeCell<bool>, // guarded by the `inner` RwLock
78
num_readers: AtomicUsize,
89
}
910

10-
pub type MovableRwLock = Box<RwLock>;
11+
pub(crate) type MovableRwLock = LazyBox<RwLock>;
1112

1213
unsafe impl Send for RwLock {}
1314
unsafe impl Sync for RwLock {}
1415

16+
impl LazyInit for RwLock {
17+
fn init() -> Box<Self> {
18+
Box::new(Self::new())
19+
}
20+
}
21+
1522
impl RwLock {
1623
pub const fn new() -> RwLock {
1724
RwLock {

library/std/src/sys/unsupported/locks/condvar.rs

-3
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,6 @@ impl Condvar {
1010
Condvar {}
1111
}
1212

13-
#[inline]
14-
pub unsafe fn init(&mut self) {}
15-
1613
#[inline]
1714
pub unsafe fn notify_one(&self) {}
1815

library/std/src/sys/wasm/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ cfg_if::cfg_if! {
5454
#![allow(unsafe_op_in_unsafe_fn)]
5555
mod futex;
5656
mod futex_rwlock;
57-
pub use futex::{Mutex, MovableMutex, Condvar, MovableCondvar};
58-
pub use futex_rwlock::{RwLock, MovableRwLock};
57+
pub(crate) use futex::{Mutex, MovableMutex, Condvar, MovableCondvar};
58+
pub(crate) use futex_rwlock::{RwLock, MovableRwLock};
5959
}
6060
#[path = "atomics/futex.rs"]
6161
pub mod futex;

library/std/src/sys/windows/locks/condvar.rs

-3
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@ impl Condvar {
1818
Condvar { inner: UnsafeCell::new(c::CONDITION_VARIABLE_INIT) }
1919
}
2020

21-
#[inline]
22-
pub unsafe fn init(&mut self) {}
23-
2421
#[inline]
2522
pub unsafe fn wait(&self, mutex: &Mutex) {
2623
let r = c::SleepConditionVariableSRW(self.inner.get(), mutex::raw(mutex), c::INFINITE, 0);

library/std/src/sys_common/condvar.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@ pub struct Condvar {
1515
impl Condvar {
1616
/// Creates a new condition variable for use.
1717
pub fn new() -> Self {
18-
let mut c = imp::MovableCondvar::from(imp::Condvar::new());
19-
unsafe { c.init() };
20-
Self { inner: c, check: CondvarCheck::new() }
18+
Self { inner: imp::MovableCondvar::new(), check: CondvarCheck::new() }
2119
}
2220

2321
/// Signals one waiter on this condition variable to wake up.

library/std/src/sys_common/condvar/check.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::ptr;
22
use crate::sync::atomic::{AtomicPtr, Ordering};
33
use crate::sys::locks as imp;
4+
use crate::sys_common::lazy_box::{LazyBox, LazyInit};
45
use crate::sys_common::mutex::MovableMutex;
56

67
pub trait CondvarCheck {
@@ -9,7 +10,7 @@ pub trait CondvarCheck {
910

1011
/// For boxed mutexes, a `Condvar` will check it's only ever used with the same
1112
/// mutex, based on its (stable) address.
12-
impl CondvarCheck for Box<imp::Mutex> {
13+
impl<T: LazyInit> CondvarCheck for LazyBox<T> {
1314
type Check = SameMutexCheck;
1415
}
1516

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#![allow(dead_code)] // Only used on some platforms.
2+
3+
// This is used to wrap pthread {Mutex, Condvar, RwLock} in.
4+
5+
use crate::marker::PhantomData;
6+
use crate::ops::{Deref, DerefMut};
7+
use crate::ptr::null_mut;
8+
use crate::sync::atomic::{
9+
AtomicPtr,
10+
Ordering::{AcqRel, Acquire},
11+
};
12+
13+
pub(crate) struct LazyBox<T: LazyInit> {
14+
ptr: AtomicPtr<T>,
15+
_phantom: PhantomData<T>,
16+
}
17+
18+
pub(crate) trait LazyInit {
19+
/// This is called before the box is allocated, to provide the value to
20+
/// move into the new box.
21+
///
22+
/// It might be called more than once per LazyBox, as multiple threads
23+
/// might race to initialize it concurrently, each constructing and initializing
24+
/// their own box. (All but one of them will be destroyed right after.)
25+
fn init() -> Box<Self>;
26+
}
27+
28+
impl<T: LazyInit> LazyBox<T> {
29+
#[inline]
30+
pub const fn new() -> Self {
31+
Self { ptr: AtomicPtr::new(null_mut()), _phantom: PhantomData }
32+
}
33+
34+
#[inline]
35+
fn get_pointer(&self) -> *mut T {
36+
let ptr = self.ptr.load(Acquire);
37+
if ptr.is_null() { self.initialize() } else { ptr }
38+
}
39+
40+
#[cold]
41+
fn initialize(&self) -> *mut T {
42+
let new_ptr = Box::into_raw(T::init());
43+
match self.ptr.compare_exchange(null_mut(), new_ptr, AcqRel, Acquire) {
44+
Ok(_) => new_ptr,
45+
Err(ptr) => {
46+
// Lost the race to another thread.
47+
// Drop the box we created, and use the one from the other thread instead.
48+
drop(unsafe { Box::from_raw(new_ptr) });
49+
ptr
50+
}
51+
}
52+
}
53+
}
54+
55+
impl<T: LazyInit> Deref for LazyBox<T> {
56+
type Target = T;
57+
#[inline]
58+
fn deref(&self) -> &T {
59+
unsafe { &*self.get_pointer() }
60+
}
61+
}
62+
63+
impl<T: LazyInit> DerefMut for LazyBox<T> {
64+
#[inline]
65+
fn deref_mut(&mut self) -> &mut T {
66+
unsafe { &mut *self.get_pointer() }
67+
}
68+
}
69+
70+
impl<T: LazyInit> Drop for LazyBox<T> {
71+
fn drop(&mut self) {
72+
let ptr = *self.ptr.get_mut();
73+
if !ptr.is_null() {
74+
drop(unsafe { Box::from_raw(ptr) });
75+
}
76+
}
77+
}

library/std/src/sys_common/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ pub mod backtrace;
2424
pub mod condvar;
2525
pub mod fs;
2626
pub mod io;
27+
pub mod lazy_box;
2728
pub mod memchr;
2829
pub mod mutex;
2930
pub mod process;

library/std/src/sys_common/mutex.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,7 @@ unsafe impl Sync for MovableMutex {}
6161
impl MovableMutex {
6262
/// Creates a new mutex.
6363
pub fn new() -> Self {
64-
let mut mutex = imp::MovableMutex::from(imp::Mutex::new());
65-
unsafe { mutex.init() };
66-
Self(mutex)
64+
Self(imp::MovableMutex::new())
6765
}
6866

6967
pub(super) fn raw(&self) -> &imp::Mutex {

library/std/src/sys_common/rwlock.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ pub struct MovableRwLock(imp::MovableRwLock);
7474
impl MovableRwLock {
7575
/// Creates a new reader-writer lock for use.
7676
pub fn new() -> Self {
77-
Self(imp::MovableRwLock::from(imp::RwLock::new()))
77+
Self(imp::MovableRwLock::new())
7878
}
7979

8080
/// Acquires shared access to the underlying lock, blocking the current

0 commit comments

Comments
 (0)