about summary refs log tree commit diff
path: root/src/libstd/sys_common
diff options
context:
space:
mode:
authorDylan DPC <dylan.dpc@gmail.com>2020-03-21 13:06:38 +0100
committerGitHub <noreply@github.com>2020-03-21 13:06:38 +0100
commit276b54e9c930c4ff015e1958ad1c640deffd29b2 (patch)
tree4c1eb2b3b0d3e5b53b4a4e1ada7042001f5ed089 /src/libstd/sys_common
parent8deeac153fcca97f6a5185b322f8d65d59fab5f4 (diff)
parent5edaa7eefd76d4996dcf85dfc1c1a3f737087257 (diff)
downloadrust-276b54e9c930c4ff015e1958ad1c640deffd29b2.tar.gz
rust-276b54e9c930c4ff015e1958ad1c640deffd29b2.zip
Rollup merge of #69955 - alexcrichton:stderr-infallible, r=sfackler
Fix abort-on-eprintln during process shutdown

This commit fixes an issue where if `eprintln!` is used in a TLS
destructor it can accidentally cause the process to abort. TLS
destructors are executed after `main` returns on the main thread, and at
this point we've also deinitialized global `Lazy` values like those
which store the `Stderr` and `Stdout` internals. This means that despite
handling TLS not being accessible in `eprintln!`, we will fail due to
not being able to call `stderr()`. This means that we'll double-panic
quickly because panicking also attempt to write to stderr.

The fix here is to reimplement the global stderr handle to avoid the
need for destruction. This avoids the need for `Lazy` as well as the
hidden panic inside of the `stderr` function.

Overall this should improve the robustness of printing errors and/or
panics in weird situations, since the `stderr` accessor should be
infallible in more situations.
Diffstat (limited to 'src/libstd/sys_common')
-rw-r--r--src/libstd/sys_common/remutex.rs114
1 files changed, 51 insertions, 63 deletions
diff --git a/src/libstd/sys_common/remutex.rs b/src/libstd/sys_common/remutex.rs
index a1ad44a3666..4f19bbc467f 100644
--- a/src/libstd/sys_common/remutex.rs
+++ b/src/libstd/sys_common/remutex.rs
@@ -3,7 +3,6 @@ use crate::marker;
 use crate::ops::Deref;
 use crate::panic::{RefUnwindSafe, UnwindSafe};
 use crate::sys::mutex as sys;
-use crate::sys_common::poison::{self, LockResult, TryLockError, TryLockResult};
 
 /// A re-entrant mutual exclusion
 ///
@@ -11,8 +10,7 @@ use crate::sys_common::poison::{self, LockResult, TryLockError, TryLockResult};
 /// available. The thread which has already locked the mutex can lock it
 /// multiple times without blocking, preventing a common source of deadlocks.
 pub struct ReentrantMutex<T> {
-    inner: Box<sys::ReentrantMutex>,
-    poison: poison::Flag,
+    inner: sys::ReentrantMutex,
     data: T,
 }
 
@@ -39,23 +37,30 @@ pub struct ReentrantMutexGuard<'a, T: 'a> {
     // funny underscores due to how Deref currently works (it disregards field
     // privacy).
     __lock: &'a ReentrantMutex<T>,
-    __poison: poison::Guard,
 }
 
 impl<T> !marker::Send for ReentrantMutexGuard<'_, T> {}
 
 impl<T> ReentrantMutex<T> {
     /// Creates a new reentrant mutex in an unlocked state.
-    pub fn new(t: T) -> ReentrantMutex<T> {
-        unsafe {
-            let mut mutex = ReentrantMutex {
-                inner: box sys::ReentrantMutex::uninitialized(),
-                poison: poison::Flag::new(),
-                data: t,
-            };
-            mutex.inner.init();
-            mutex
-        }
+    ///
+    /// # Unsafety
+    ///
+    /// This function is unsafe because it is required that `init` is called
+    /// once this mutex is in its final resting place, and only then are the
+    /// lock/unlock methods safe.
+    pub const unsafe fn new(t: T) -> ReentrantMutex<T> {
+        ReentrantMutex { inner: sys::ReentrantMutex::uninitialized(), data: t }
+    }
+
+    /// Initializes this mutex so it's ready for use.
+    ///
+    /// # Unsafety
+    ///
+    /// Unsafe to call more than once, and must be called after this will no
+    /// longer move in memory.
+    pub unsafe fn init(&self) {
+        self.inner.init();
     }
 
     /// Acquires a mutex, blocking the current thread until it is able to do so.
@@ -70,7 +75,7 @@ impl<T> ReentrantMutex<T> {
     /// If another user of this mutex panicked while holding the mutex, then
     /// this call will return failure if the mutex would otherwise be
     /// acquired.
-    pub fn lock(&self) -> LockResult<ReentrantMutexGuard<'_, T>> {
+    pub fn lock(&self) -> ReentrantMutexGuard<'_, T> {
         unsafe { self.inner.lock() }
         ReentrantMutexGuard::new(&self)
     }
@@ -87,12 +92,8 @@ impl<T> ReentrantMutex<T> {
     /// If another user of this mutex panicked while holding the mutex, then
     /// this call will return failure if the mutex would otherwise be
     /// acquired.
-    pub fn try_lock(&self) -> TryLockResult<ReentrantMutexGuard<'_, T>> {
-        if unsafe { self.inner.try_lock() } {
-            Ok(ReentrantMutexGuard::new(&self)?)
-        } else {
-            Err(TryLockError::WouldBlock)
-        }
+    pub fn try_lock(&self) -> Option<ReentrantMutexGuard<'_, T>> {
+        if unsafe { self.inner.try_lock() } { Some(ReentrantMutexGuard::new(&self)) } else { None }
     }
 }
 
@@ -108,11 +109,8 @@ 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() {
-            Ok(guard) => f.debug_struct("ReentrantMutex").field("data", &*guard).finish(),
-            Err(TryLockError::Poisoned(err)) => {
-                f.debug_struct("ReentrantMutex").field("data", &**err.get_ref()).finish()
-            }
-            Err(TryLockError::WouldBlock) => {
+            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 {
@@ -127,11 +125,8 @@ impl<T: fmt::Debug + 'static> fmt::Debug for ReentrantMutex<T> {
 }
 
 impl<'mutex, T> ReentrantMutexGuard<'mutex, T> {
-    fn new(lock: &'mutex ReentrantMutex<T>) -> LockResult<ReentrantMutexGuard<'mutex, T>> {
-        poison::map_result(lock.poison.borrow(), |guard| ReentrantMutexGuard {
-            __lock: lock,
-            __poison: guard,
-        })
+    fn new(lock: &'mutex ReentrantMutex<T>) -> ReentrantMutexGuard<'mutex, T> {
+        ReentrantMutexGuard { __lock: lock }
     }
 }
 
@@ -147,7 +142,6 @@ impl<T> Drop for ReentrantMutexGuard<'_, T> {
     #[inline]
     fn drop(&mut self) {
         unsafe {
-            self.__lock.poison.done(&self.__poison);
             self.__lock.inner.unlock();
         }
     }
@@ -162,13 +156,17 @@ mod tests {
 
     #[test]
     fn smoke() {
-        let m = ReentrantMutex::new(());
+        let m = unsafe {
+            let m = ReentrantMutex::new(());
+            m.init();
+            m
+        };
         {
-            let a = m.lock().unwrap();
+            let a = m.lock();
             {
-                let b = m.lock().unwrap();
+                let b = m.lock();
                 {
-                    let c = m.lock().unwrap();
+                    let c = m.lock();
                     assert_eq!(*c, ());
                 }
                 assert_eq!(*b, ());
@@ -179,15 +177,19 @@ mod tests {
 
     #[test]
     fn is_mutex() {
-        let m = Arc::new(ReentrantMutex::new(RefCell::new(0)));
+        let m = unsafe {
+            let m = Arc::new(ReentrantMutex::new(RefCell::new(0)));
+            m.init();
+            m
+        };
         let m2 = m.clone();
-        let lock = m.lock().unwrap();
+        let lock = m.lock();
         let child = thread::spawn(move || {
-            let lock = m2.lock().unwrap();
+            let lock = m2.lock();
             assert_eq!(*lock.borrow(), 4950);
         });
         for i in 0..100 {
-            let lock = m.lock().unwrap();
+            let lock = m.lock();
             *lock.borrow_mut() += i;
         }
         drop(lock);
@@ -196,17 +198,21 @@ mod tests {
 
     #[test]
     fn trylock_works() {
-        let m = Arc::new(ReentrantMutex::new(()));
+        let m = unsafe {
+            let m = Arc::new(ReentrantMutex::new(()));
+            m.init();
+            m
+        };
         let m2 = m.clone();
-        let _lock = m.try_lock().unwrap();
-        let _lock2 = m.try_lock().unwrap();
+        let _lock = m.try_lock();
+        let _lock2 = m.try_lock();
         thread::spawn(move || {
             let lock = m2.try_lock();
-            assert!(lock.is_err());
+            assert!(lock.is_none());
         })
         .join()
         .unwrap();
-        let _lock3 = m.try_lock().unwrap();
+        let _lock3 = m.try_lock();
     }
 
     pub struct Answer<'a>(pub ReentrantMutexGuard<'a, RefCell<u32>>);
@@ -215,22 +221,4 @@ mod tests {
             *self.0.borrow_mut() = 42;
         }
     }
-
-    #[test]
-    fn poison_works() {
-        let m = Arc::new(ReentrantMutex::new(RefCell::new(0)));
-        let mc = m.clone();
-        let result = thread::spawn(move || {
-            let lock = mc.lock().unwrap();
-            *lock.borrow_mut() = 1;
-            let lock2 = mc.lock().unwrap();
-            *lock.borrow_mut() = 2;
-            let _answer = Answer(lock2);
-            panic!("What the answer to my lifetimes dilemma is?");
-        })
-        .join();
-        assert!(result.is_err());
-        let r = m.lock().err().unwrap().into_inner();
-        assert_eq!(*r.borrow(), 42);
-    }
 }