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.
This commit is contained in:
parent
8fe90966e1
commit
67c18fdec5
@ -9,6 +9,7 @@ use crate::cell::{Cell, RefCell};
|
|||||||
use crate::fmt;
|
use crate::fmt;
|
||||||
use crate::io::{self, BufReader, Initializer, IoSlice, IoSliceMut, LineWriter};
|
use crate::io::{self, BufReader, Initializer, IoSlice, IoSliceMut, LineWriter};
|
||||||
use crate::lazy::SyncOnceCell;
|
use crate::lazy::SyncOnceCell;
|
||||||
|
use crate::pin::Pin;
|
||||||
use crate::sync::atomic::{AtomicBool, Ordering};
|
use crate::sync::atomic::{AtomicBool, Ordering};
|
||||||
use crate::sync::{Arc, Mutex, MutexGuard};
|
use crate::sync::{Arc, Mutex, MutexGuard};
|
||||||
use crate::sys::stdio;
|
use crate::sys::stdio;
|
||||||
@ -490,7 +491,7 @@ pub struct Stdout {
|
|||||||
// FIXME: this should be LineWriter or BufWriter depending on the state of
|
// FIXME: this should be LineWriter or BufWriter depending on the state of
|
||||||
// stdout (tty or not). Note that if this is not line buffered it
|
// stdout (tty or not). Note that if this is not line buffered it
|
||||||
// should also flush-on-panic or some form of flush-on-abort.
|
// should also flush-on-panic or some form of flush-on-abort.
|
||||||
inner: &'static ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>,
|
inner: Pin<&'static ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A locked reference to the `Stdout` handle.
|
/// A locked reference to the `Stdout` handle.
|
||||||
@ -550,9 +551,8 @@ pub struct StdoutLock<'a> {
|
|||||||
pub fn stdout() -> Stdout {
|
pub fn stdout() -> Stdout {
|
||||||
static INSTANCE: SyncOnceCell<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> =
|
static INSTANCE: SyncOnceCell<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> =
|
||||||
SyncOnceCell::new();
|
SyncOnceCell::new();
|
||||||
Stdout {
|
|
||||||
inner: INSTANCE.get_or_init(|| unsafe {
|
fn cleanup() {
|
||||||
let _ = sys_common::at_exit(|| {
|
|
||||||
if let Some(instance) = INSTANCE.get() {
|
if let Some(instance) = INSTANCE.get() {
|
||||||
// Flush the data and disable buffering during shutdown
|
// Flush the data and disable buffering during shutdown
|
||||||
// by replacing the line writer by one with zero
|
// by replacing the line writer by one with zero
|
||||||
@ -560,15 +560,20 @@ pub fn stdout() -> Stdout {
|
|||||||
// We use try_lock() instead of lock(), because someone
|
// We use try_lock() instead of lock(), because someone
|
||||||
// might have leaked a StdoutLock, which would
|
// might have leaked a StdoutLock, which would
|
||||||
// otherwise cause a deadlock here.
|
// otherwise cause a deadlock here.
|
||||||
if let Some(lock) = instance.try_lock() {
|
if let Some(lock) = Pin::static_ref(instance).try_lock() {
|
||||||
*lock.borrow_mut() = LineWriter::with_capacity(0, stdout_raw());
|
*lock.borrow_mut() = LineWriter::with_capacity(0, stdout_raw());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
let r = ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw())));
|
|
||||||
r.init();
|
Stdout {
|
||||||
r
|
inner: Pin::static_ref(&INSTANCE).get_or_init_pin(
|
||||||
}),
|
|| unsafe {
|
||||||
|
let _ = sys_common::at_exit(cleanup);
|
||||||
|
ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw())))
|
||||||
|
},
|
||||||
|
|mutex| unsafe { mutex.init() },
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -700,7 +705,7 @@ impl fmt::Debug for StdoutLock<'_> {
|
|||||||
/// an error.
|
/// an error.
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
pub struct Stderr {
|
pub struct Stderr {
|
||||||
inner: &'static ReentrantMutex<RefCell<StderrRaw>>,
|
inner: Pin<&'static ReentrantMutex<RefCell<StderrRaw>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A locked reference to the `Stderr` handle.
|
/// A locked reference to the `Stderr` handle.
|
||||||
@ -762,11 +767,10 @@ pub fn stderr() -> Stderr {
|
|||||||
static INSTANCE: SyncOnceCell<ReentrantMutex<RefCell<StderrRaw>>> = SyncOnceCell::new();
|
static INSTANCE: SyncOnceCell<ReentrantMutex<RefCell<StderrRaw>>> = SyncOnceCell::new();
|
||||||
|
|
||||||
Stderr {
|
Stderr {
|
||||||
inner: INSTANCE.get_or_init(|| unsafe {
|
inner: Pin::static_ref(&INSTANCE).get_or_init_pin(
|
||||||
let r = ReentrantMutex::new(RefCell::new(stderr_raw()));
|
|| unsafe { ReentrantMutex::new(RefCell::new(stderr_raw())) },
|
||||||
r.init();
|
|mutex| unsafe { mutex.init() },
|
||||||
r
|
),
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,6 +266,7 @@
|
|||||||
#![feature(format_args_nl)]
|
#![feature(format_args_nl)]
|
||||||
#![feature(gen_future)]
|
#![feature(gen_future)]
|
||||||
#![feature(generator_trait)]
|
#![feature(generator_trait)]
|
||||||
|
#![feature(get_mut_unchecked)]
|
||||||
#![feature(global_asm)]
|
#![feature(global_asm)]
|
||||||
#![feature(hashmap_internals)]
|
#![feature(hashmap_internals)]
|
||||||
#![feature(int_error_internals)]
|
#![feature(int_error_internals)]
|
||||||
@ -293,6 +294,7 @@
|
|||||||
#![feature(panic_info_message)]
|
#![feature(panic_info_message)]
|
||||||
#![feature(panic_internals)]
|
#![feature(panic_internals)]
|
||||||
#![feature(panic_unwind)]
|
#![feature(panic_unwind)]
|
||||||
|
#![feature(pin_static_ref)]
|
||||||
#![feature(prelude_import)]
|
#![feature(prelude_import)]
|
||||||
#![feature(ptr_internals)]
|
#![feature(ptr_internals)]
|
||||||
#![feature(raw)]
|
#![feature(raw)]
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
#[cfg(all(test, not(target_os = "emscripten")))]
|
#[cfg(all(test, not(target_os = "emscripten")))]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
use crate::fmt;
|
use crate::marker::PhantomPinned;
|
||||||
use crate::ops::Deref;
|
use crate::ops::Deref;
|
||||||
use crate::panic::{RefUnwindSafe, UnwindSafe};
|
use crate::panic::{RefUnwindSafe, UnwindSafe};
|
||||||
|
use crate::pin::Pin;
|
||||||
use crate::sys::mutex as sys;
|
use crate::sys::mutex as sys;
|
||||||
|
|
||||||
/// A re-entrant mutual exclusion
|
/// A re-entrant mutual exclusion
|
||||||
@ -14,6 +15,7 @@ use crate::sys::mutex as sys;
|
|||||||
pub struct ReentrantMutex<T> {
|
pub struct ReentrantMutex<T> {
|
||||||
inner: sys::ReentrantMutex,
|
inner: sys::ReentrantMutex,
|
||||||
data: T,
|
data: T,
|
||||||
|
_pinned: PhantomPinned,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<T: Send> Send for ReentrantMutex<T> {}
|
unsafe impl<T: Send> Send for ReentrantMutex<T> {}
|
||||||
@ -36,7 +38,7 @@ impl<T> RefUnwindSafe for ReentrantMutex<T> {}
|
|||||||
/// guarded data.
|
/// guarded data.
|
||||||
#[must_use = "if unused the ReentrantMutex will immediately unlock"]
|
#[must_use = "if unused the ReentrantMutex will immediately unlock"]
|
||||||
pub struct ReentrantMutexGuard<'a, T: 'a> {
|
pub struct ReentrantMutexGuard<'a, T: 'a> {
|
||||||
lock: &'a ReentrantMutex<T>,
|
lock: Pin<&'a ReentrantMutex<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> !Send for ReentrantMutexGuard<'_, T> {}
|
impl<T> !Send for ReentrantMutexGuard<'_, T> {}
|
||||||
@ -50,7 +52,11 @@ impl<T> ReentrantMutex<T> {
|
|||||||
/// once this mutex is in its final resting place, and only then are the
|
/// once this mutex is in its final resting place, and only then are the
|
||||||
/// lock/unlock methods safe.
|
/// lock/unlock methods safe.
|
||||||
pub const unsafe fn new(t: T) -> ReentrantMutex<T> {
|
pub const unsafe fn new(t: T) -> ReentrantMutex<T> {
|
||||||
ReentrantMutex { inner: sys::ReentrantMutex::uninitialized(), data: t }
|
ReentrantMutex {
|
||||||
|
inner: sys::ReentrantMutex::uninitialized(),
|
||||||
|
data: t,
|
||||||
|
_pinned: PhantomPinned,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initializes this mutex so it's ready for use.
|
/// Initializes this mutex so it's ready for use.
|
||||||
@ -59,8 +65,8 @@ impl<T> ReentrantMutex<T> {
|
|||||||
///
|
///
|
||||||
/// Unsafe to call more than once, and must be called after this will no
|
/// Unsafe to call more than once, and must be called after this will no
|
||||||
/// longer move in memory.
|
/// longer move in memory.
|
||||||
pub unsafe fn init(&self) {
|
pub unsafe fn init(self: Pin<&mut Self>) {
|
||||||
self.inner.init();
|
self.get_unchecked_mut().inner.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Acquires a mutex, blocking the current thread until it is able to do so.
|
/// Acquires a mutex, blocking the current thread until it is able to do so.
|
||||||
@ -75,9 +81,9 @@ impl<T> ReentrantMutex<T> {
|
|||||||
/// If another user of this mutex panicked while holding the mutex, then
|
/// If another user of this mutex panicked while holding the mutex, then
|
||||||
/// this call will return failure if the mutex would otherwise be
|
/// this call will return failure if the mutex would otherwise be
|
||||||
/// acquired.
|
/// acquired.
|
||||||
pub fn lock(&self) -> ReentrantMutexGuard<'_, T> {
|
pub fn lock(self: Pin<&Self>) -> ReentrantMutexGuard<'_, T> {
|
||||||
unsafe { self.inner.lock() }
|
unsafe { self.inner.lock() }
|
||||||
ReentrantMutexGuard::new(&self)
|
ReentrantMutexGuard { lock: self }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to acquire this lock.
|
/// Attempts to acquire this lock.
|
||||||
@ -92,8 +98,12 @@ impl<T> ReentrantMutex<T> {
|
|||||||
/// If another user of this mutex panicked while holding the mutex, then
|
/// If another user of this mutex panicked while holding the mutex, then
|
||||||
/// this call will return failure if the mutex would otherwise be
|
/// this call will return failure if the mutex would otherwise be
|
||||||
/// acquired.
|
/// acquired.
|
||||||
pub fn try_lock(&self) -> Option<ReentrantMutexGuard<'_, T>> {
|
pub fn try_lock(self: Pin<&Self>) -> Option<ReentrantMutexGuard<'_, T>> {
|
||||||
if unsafe { self.inner.try_lock() } { Some(ReentrantMutexGuard::new(&self)) } else { None }
|
if unsafe { self.inner.try_lock() } {
|
||||||
|
Some(ReentrantMutexGuard { lock: self })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,30 +116,6 @@ impl<T> Drop for ReentrantMutex<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: fmt::Debug + 'static> fmt::Debug for ReentrantMutex<T> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match self.try_lock() {
|
|
||||||
Some(guard) => f.debug_struct("ReentrantMutex").field("data", &*guard).finish(),
|
|
||||||
None => {
|
|
||||||
struct LockedPlaceholder;
|
|
||||||
impl fmt::Debug for LockedPlaceholder {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.write_str("<locked>")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
f.debug_struct("ReentrantMutex").field("data", &LockedPlaceholder).finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'mutex, T> ReentrantMutexGuard<'mutex, T> {
|
|
||||||
fn new(lock: &'mutex ReentrantMutex<T>) -> ReentrantMutexGuard<'mutex, T> {
|
|
||||||
ReentrantMutexGuard { lock }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Deref for ReentrantMutexGuard<'_, T> {
|
impl<T> Deref for ReentrantMutexGuard<'_, T> {
|
||||||
type Target = T;
|
type Target = T;
|
||||||
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
use crate::boxed::Box;
|
||||||
use crate::cell::RefCell;
|
use crate::cell::RefCell;
|
||||||
|
use crate::pin::Pin;
|
||||||
use crate::sync::Arc;
|
use crate::sync::Arc;
|
||||||
use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard};
|
use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard};
|
||||||
use crate::thread;
|
use crate::thread;
|
||||||
@ -6,10 +8,11 @@ use crate::thread;
|
|||||||
#[test]
|
#[test]
|
||||||
fn smoke() {
|
fn smoke() {
|
||||||
let m = unsafe {
|
let m = unsafe {
|
||||||
let m = ReentrantMutex::new(());
|
let mut m = Box::pin(ReentrantMutex::new(()));
|
||||||
m.init();
|
m.as_mut().init();
|
||||||
m
|
m
|
||||||
};
|
};
|
||||||
|
let m = m.as_ref();
|
||||||
{
|
{
|
||||||
let a = m.lock();
|
let a = m.lock();
|
||||||
{
|
{
|
||||||
@ -27,18 +30,19 @@ fn smoke() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn is_mutex() {
|
fn is_mutex() {
|
||||||
let m = unsafe {
|
let m = unsafe {
|
||||||
let m = Arc::new(ReentrantMutex::new(RefCell::new(0)));
|
// FIXME: Simplify this if Arc gets a Arc::get_pin_mut.
|
||||||
m.init();
|
let mut m = Arc::new(ReentrantMutex::new(RefCell::new(0)));
|
||||||
m
|
Pin::new_unchecked(Arc::get_mut_unchecked(&mut m)).init();
|
||||||
|
Pin::new_unchecked(m)
|
||||||
};
|
};
|
||||||
let m2 = m.clone();
|
let m2 = m.clone();
|
||||||
let lock = m.lock();
|
let lock = m.as_ref().lock();
|
||||||
let child = thread::spawn(move || {
|
let child = thread::spawn(move || {
|
||||||
let lock = m2.lock();
|
let lock = m2.as_ref().lock();
|
||||||
assert_eq!(*lock.borrow(), 4950);
|
assert_eq!(*lock.borrow(), 4950);
|
||||||
});
|
});
|
||||||
for i in 0..100 {
|
for i in 0..100 {
|
||||||
let lock = m.lock();
|
let lock = m.as_ref().lock();
|
||||||
*lock.borrow_mut() += i;
|
*lock.borrow_mut() += i;
|
||||||
}
|
}
|
||||||
drop(lock);
|
drop(lock);
|
||||||
@ -48,20 +52,21 @@ fn is_mutex() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn trylock_works() {
|
fn trylock_works() {
|
||||||
let m = unsafe {
|
let m = unsafe {
|
||||||
let m = Arc::new(ReentrantMutex::new(()));
|
// FIXME: Simplify this if Arc gets a Arc::get_pin_mut.
|
||||||
m.init();
|
let mut m = Arc::new(ReentrantMutex::new(()));
|
||||||
m
|
Pin::new_unchecked(Arc::get_mut_unchecked(&mut m)).init();
|
||||||
|
Pin::new_unchecked(m)
|
||||||
};
|
};
|
||||||
let m2 = m.clone();
|
let m2 = m.clone();
|
||||||
let _lock = m.try_lock();
|
let _lock = m.as_ref().try_lock();
|
||||||
let _lock2 = m.try_lock();
|
let _lock2 = m.as_ref().try_lock();
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let lock = m2.try_lock();
|
let lock = m2.as_ref().try_lock();
|
||||||
assert!(lock.is_none());
|
assert!(lock.is_none());
|
||||||
})
|
})
|
||||||
.join()
|
.join()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let _lock3 = m.try_lock();
|
let _lock3 = m.as_ref().try_lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Answer<'a>(pub ReentrantMutexGuard<'a, RefCell<u32>>);
|
pub struct Answer<'a>(pub ReentrantMutexGuard<'a, RefCell<u32>>);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user