Skip to content

Commit 67c18fd

Browse files
committed
Use Pin for the 'don't move' requirement of ReentrantMutex.
The code in io::stdio before this change misused the ReentrantMutexes, by calling init() on them and moving them afterwards. Now that ReentrantMutex requires Pin for init(), this mistake is no longer easy to make.
1 parent 8fe9096 commit 67c18fd

File tree

4 files changed

+70
-73
lines changed

4 files changed

+70
-73
lines changed

library/std/src/io/stdio.rs

+29-25
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::cell::{Cell, RefCell};
99
use crate::fmt;
1010
use crate::io::{self, BufReader, Initializer, IoSlice, IoSliceMut, LineWriter};
1111
use crate::lazy::SyncOnceCell;
12+
use crate::pin::Pin;
1213
use crate::sync::atomic::{AtomicBool, Ordering};
1314
use crate::sync::{Arc, Mutex, MutexGuard};
1415
use crate::sys::stdio;
@@ -490,7 +491,7 @@ pub struct Stdout {
490491
// FIXME: this should be LineWriter or BufWriter depending on the state of
491492
// stdout (tty or not). Note that if this is not line buffered it
492493
// should also flush-on-panic or some form of flush-on-abort.
493-
inner: &'static ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>,
494+
inner: Pin<&'static ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>>,
494495
}
495496

496497
/// A locked reference to the `Stdout` handle.
@@ -550,25 +551,29 @@ pub struct StdoutLock<'a> {
550551
pub fn stdout() -> Stdout {
551552
static INSTANCE: SyncOnceCell<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> =
552553
SyncOnceCell::new();
554+
555+
fn cleanup() {
556+
if let Some(instance) = INSTANCE.get() {
557+
// Flush the data and disable buffering during shutdown
558+
// by replacing the line writer by one with zero
559+
// buffering capacity.
560+
// We use try_lock() instead of lock(), because someone
561+
// might have leaked a StdoutLock, which would
562+
// otherwise cause a deadlock here.
563+
if let Some(lock) = Pin::static_ref(instance).try_lock() {
564+
*lock.borrow_mut() = LineWriter::with_capacity(0, stdout_raw());
565+
}
566+
}
567+
}
568+
553569
Stdout {
554-
inner: INSTANCE.get_or_init(|| unsafe {
555-
let _ = sys_common::at_exit(|| {
556-
if let Some(instance) = INSTANCE.get() {
557-
// Flush the data and disable buffering during shutdown
558-
// by replacing the line writer by one with zero
559-
// buffering capacity.
560-
// We use try_lock() instead of lock(), because someone
561-
// might have leaked a StdoutLock, which would
562-
// otherwise cause a deadlock here.
563-
if let Some(lock) = instance.try_lock() {
564-
*lock.borrow_mut() = LineWriter::with_capacity(0, stdout_raw());
565-
}
566-
}
567-
});
568-
let r = ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw())));
569-
r.init();
570-
r
571-
}),
570+
inner: Pin::static_ref(&INSTANCE).get_or_init_pin(
571+
|| unsafe {
572+
let _ = sys_common::at_exit(cleanup);
573+
ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw())))
574+
},
575+
|mutex| unsafe { mutex.init() },
576+
),
572577
}
573578
}
574579

@@ -700,7 +705,7 @@ impl fmt::Debug for StdoutLock<'_> {
700705
/// an error.
701706
#[stable(feature = "rust1", since = "1.0.0")]
702707
pub struct Stderr {
703-
inner: &'static ReentrantMutex<RefCell<StderrRaw>>,
708+
inner: Pin<&'static ReentrantMutex<RefCell<StderrRaw>>>,
704709
}
705710

706711
/// A locked reference to the `Stderr` handle.
@@ -762,11 +767,10 @@ pub fn stderr() -> Stderr {
762767
static INSTANCE: SyncOnceCell<ReentrantMutex<RefCell<StderrRaw>>> = SyncOnceCell::new();
763768

764769
Stderr {
765-
inner: INSTANCE.get_or_init(|| unsafe {
766-
let r = ReentrantMutex::new(RefCell::new(stderr_raw()));
767-
r.init();
768-
r
769-
}),
770+
inner: Pin::static_ref(&INSTANCE).get_or_init_pin(
771+
|| unsafe { ReentrantMutex::new(RefCell::new(stderr_raw())) },
772+
|mutex| unsafe { mutex.init() },
773+
),
770774
}
771775
}
772776

library/std/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@
266266
#![feature(format_args_nl)]
267267
#![feature(gen_future)]
268268
#![feature(generator_trait)]
269+
#![feature(get_mut_unchecked)]
269270
#![feature(global_asm)]
270271
#![feature(hashmap_internals)]
271272
#![feature(int_error_internals)]
@@ -293,6 +294,7 @@
293294
#![feature(panic_info_message)]
294295
#![feature(panic_internals)]
295296
#![feature(panic_unwind)]
297+
#![feature(pin_static_ref)]
296298
#![feature(prelude_import)]
297299
#![feature(ptr_internals)]
298300
#![feature(raw)]

library/std/src/sys_common/remutex.rs

+19-33
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
#[cfg(all(test, not(target_os = "emscripten")))]
22
mod tests;
33

4-
use crate::fmt;
4+
use crate::marker::PhantomPinned;
55
use crate::ops::Deref;
66
use crate::panic::{RefUnwindSafe, UnwindSafe};
7+
use crate::pin::Pin;
78
use crate::sys::mutex as sys;
89

910
/// A re-entrant mutual exclusion
@@ -14,6 +15,7 @@ use crate::sys::mutex as sys;
1415
pub struct ReentrantMutex<T> {
1516
inner: sys::ReentrantMutex,
1617
data: T,
18+
_pinned: PhantomPinned,
1719
}
1820

1921
unsafe impl<T: Send> Send for ReentrantMutex<T> {}
@@ -36,7 +38,7 @@ impl<T> RefUnwindSafe for ReentrantMutex<T> {}
3638
/// guarded data.
3739
#[must_use = "if unused the ReentrantMutex will immediately unlock"]
3840
pub struct ReentrantMutexGuard<'a, T: 'a> {
39-
lock: &'a ReentrantMutex<T>,
41+
lock: Pin<&'a ReentrantMutex<T>>,
4042
}
4143

4244
impl<T> !Send for ReentrantMutexGuard<'_, T> {}
@@ -50,7 +52,11 @@ impl<T> ReentrantMutex<T> {
5052
/// once this mutex is in its final resting place, and only then are the
5153
/// lock/unlock methods safe.
5254
pub const unsafe fn new(t: T) -> ReentrantMutex<T> {
53-
ReentrantMutex { inner: sys::ReentrantMutex::uninitialized(), data: t }
55+
ReentrantMutex {
56+
inner: sys::ReentrantMutex::uninitialized(),
57+
data: t,
58+
_pinned: PhantomPinned,
59+
}
5460
}
5561

5662
/// Initializes this mutex so it's ready for use.
@@ -59,8 +65,8 @@ impl<T> ReentrantMutex<T> {
5965
///
6066
/// Unsafe to call more than once, and must be called after this will no
6167
/// longer move in memory.
62-
pub unsafe fn init(&self) {
63-
self.inner.init();
68+
pub unsafe fn init(self: Pin<&mut Self>) {
69+
self.get_unchecked_mut().inner.init()
6470
}
6571

6672
/// Acquires a mutex, blocking the current thread until it is able to do so.
@@ -75,9 +81,9 @@ impl<T> ReentrantMutex<T> {
7581
/// If another user of this mutex panicked while holding the mutex, then
7682
/// this call will return failure if the mutex would otherwise be
7783
/// acquired.
78-
pub fn lock(&self) -> ReentrantMutexGuard<'_, T> {
84+
pub fn lock(self: Pin<&Self>) -> ReentrantMutexGuard<'_, T> {
7985
unsafe { self.inner.lock() }
80-
ReentrantMutexGuard::new(&self)
86+
ReentrantMutexGuard { lock: self }
8187
}
8288

8389
/// Attempts to acquire this lock.
@@ -92,8 +98,12 @@ impl<T> ReentrantMutex<T> {
9298
/// If another user of this mutex panicked while holding the mutex, then
9399
/// this call will return failure if the mutex would otherwise be
94100
/// acquired.
95-
pub fn try_lock(&self) -> Option<ReentrantMutexGuard<'_, T>> {
96-
if unsafe { self.inner.try_lock() } { Some(ReentrantMutexGuard::new(&self)) } else { None }
101+
pub fn try_lock(self: Pin<&Self>) -> Option<ReentrantMutexGuard<'_, T>> {
102+
if unsafe { self.inner.try_lock() } {
103+
Some(ReentrantMutexGuard { lock: self })
104+
} else {
105+
None
106+
}
97107
}
98108
}
99109

@@ -106,30 +116,6 @@ impl<T> Drop for ReentrantMutex<T> {
106116
}
107117
}
108118

109-
impl<T: fmt::Debug + 'static> fmt::Debug for ReentrantMutex<T> {
110-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111-
match self.try_lock() {
112-
Some(guard) => f.debug_struct("ReentrantMutex").field("data", &*guard).finish(),
113-
None => {
114-
struct LockedPlaceholder;
115-
impl fmt::Debug for LockedPlaceholder {
116-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117-
f.write_str("<locked>")
118-
}
119-
}
120-
121-
f.debug_struct("ReentrantMutex").field("data", &LockedPlaceholder).finish()
122-
}
123-
}
124-
}
125-
}
126-
127-
impl<'mutex, T> ReentrantMutexGuard<'mutex, T> {
128-
fn new(lock: &'mutex ReentrantMutex<T>) -> ReentrantMutexGuard<'mutex, T> {
129-
ReentrantMutexGuard { lock }
130-
}
131-
}
132-
133119
impl<T> Deref for ReentrantMutexGuard<'_, T> {
134120
type Target = T;
135121

library/std/src/sys_common/remutex/tests.rs

+20-15
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
1+
use crate::boxed::Box;
12
use crate::cell::RefCell;
3+
use crate::pin::Pin;
24
use crate::sync::Arc;
35
use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard};
46
use crate::thread;
57

68
#[test]
79
fn smoke() {
810
let m = unsafe {
9-
let m = ReentrantMutex::new(());
10-
m.init();
11+
let mut m = Box::pin(ReentrantMutex::new(()));
12+
m.as_mut().init();
1113
m
1214
};
15+
let m = m.as_ref();
1316
{
1417
let a = m.lock();
1518
{
@@ -27,18 +30,19 @@ fn smoke() {
2730
#[test]
2831
fn is_mutex() {
2932
let m = unsafe {
30-
let m = Arc::new(ReentrantMutex::new(RefCell::new(0)));
31-
m.init();
32-
m
33+
// FIXME: Simplify this if Arc gets a Arc::get_pin_mut.
34+
let mut m = Arc::new(ReentrantMutex::new(RefCell::new(0)));
35+
Pin::new_unchecked(Arc::get_mut_unchecked(&mut m)).init();
36+
Pin::new_unchecked(m)
3337
};
3438
let m2 = m.clone();
35-
let lock = m.lock();
39+
let lock = m.as_ref().lock();
3640
let child = thread::spawn(move || {
37-
let lock = m2.lock();
41+
let lock = m2.as_ref().lock();
3842
assert_eq!(*lock.borrow(), 4950);
3943
});
4044
for i in 0..100 {
41-
let lock = m.lock();
45+
let lock = m.as_ref().lock();
4246
*lock.borrow_mut() += i;
4347
}
4448
drop(lock);
@@ -48,20 +52,21 @@ fn is_mutex() {
4852
#[test]
4953
fn trylock_works() {
5054
let m = unsafe {
51-
let m = Arc::new(ReentrantMutex::new(()));
52-
m.init();
53-
m
55+
// FIXME: Simplify this if Arc gets a Arc::get_pin_mut.
56+
let mut m = Arc::new(ReentrantMutex::new(()));
57+
Pin::new_unchecked(Arc::get_mut_unchecked(&mut m)).init();
58+
Pin::new_unchecked(m)
5459
};
5560
let m2 = m.clone();
56-
let _lock = m.try_lock();
57-
let _lock2 = m.try_lock();
61+
let _lock = m.as_ref().try_lock();
62+
let _lock2 = m.as_ref().try_lock();
5863
thread::spawn(move || {
59-
let lock = m2.try_lock();
64+
let lock = m2.as_ref().try_lock();
6065
assert!(lock.is_none());
6166
})
6267
.join()
6368
.unwrap();
64-
let _lock3 = m.try_lock();
69+
let _lock3 = m.as_ref().try_lock();
6570
}
6671

6772
pub struct Answer<'a>(pub ReentrantMutexGuard<'a, RefCell<u32>>);

0 commit comments

Comments
 (0)