]> git.lizzy.rs Git - rust.git/commitdiff
std: Don't assume thread::current() works on panic
authorAlex Crichton <alex@alexcrichton.com>
Wed, 15 Apr 2015 19:27:05 +0000 (12:27 -0700)
committerAlex Crichton <alex@alexcrichton.com>
Mon, 27 Apr 2015 23:15:36 +0000 (16:15 -0700)
Inspecting the current thread's info may not always work due to the TLS value
having been destroyed (or is actively being destroyed). The code for printing
a panic message assumed, however, that it could acquire the thread's name
through this method.

Instead this commit propagates the `Option` outwards to allow the
`std::panicking` module to handle the case where the current thread isn't
present.

While it solves the immediate issue of #24313, there is still another underlying
issue of panicking destructors in thread locals will abort the process.

Closes #24313

src/libstd/panicking.rs
src/libstd/sys/common/thread_info.rs
src/libstd/sys/unix/stack_overflow.rs
src/libstd/thread/mod.rs
src/test/run-pass/issue-24313.rs [new file with mode: 0644]

index 11b057d0094ae1d0674b22db405f03307eb35a47..7366524fd7ea82f1729f67578920b5e0ac2adc41 100644 (file)
@@ -17,7 +17,7 @@
 use cell::RefCell;
 use rt::{backtrace, unwind};
 use sys::stdio::Stderr;
-use thread;
+use sys_common::thread_info;
 
 thread_local! {
     pub static LOCAL_STDERR: RefCell<Option<Box<Write + Send>>> = {
@@ -34,8 +34,8 @@ pub fn on_panic(obj: &(Any+Send), file: &'static str, line: u32) {
         }
     };
     let mut err = Stderr::new();
-    let thread = thread::current();
-    let name = thread.name().unwrap_or("<unnamed>");
+    let thread = thread_info::current_thread();
+    let name = thread.as_ref().and_then(|t| t.name()).unwrap_or("<unnamed>");
     let prev = LOCAL_STDERR.with(|s| s.borrow_mut().take());
     match prev {
         Some(mut stderr) => {
index 22cb59433713043dbaf2a3409072e147e13901c5..ae55bae37aa884e3870e7b43315f745e425a927d 100644 (file)
@@ -25,10 +25,9 @@ struct ThreadInfo {
 thread_local! { static THREAD_INFO: RefCell<Option<ThreadInfo>> = RefCell::new(None) }
 
 impl ThreadInfo {
-    fn with<R, F>(f: F) -> R where F: FnOnce(&mut ThreadInfo) -> R {
+    fn with<R, F>(f: F) -> Option<R> where F: FnOnce(&mut ThreadInfo) -> R {
         if THREAD_INFO.state() == LocalKeyState::Destroyed {
-            panic!("Use of std::thread::current() is not possible after \
-                    the thread's local data has been destroyed");
+            return None
         }
 
         THREAD_INFO.with(move |c| {
@@ -38,16 +37,16 @@ fn with<R, F>(f: F) -> R where F: FnOnce(&mut ThreadInfo) -> R {
                     thread: NewThread::new(None),
                 })
             }
-            f(c.borrow_mut().as_mut().unwrap())
+            Some(f(c.borrow_mut().as_mut().unwrap()))
         })
     }
 }
 
-pub fn current_thread() -> Thread {
+pub fn current_thread() -> Option<Thread> {
     ThreadInfo::with(|info| info.thread.clone())
 }
 
-pub fn stack_guard() -> usize {
+pub fn stack_guard() -> Option<usize> {
     ThreadInfo::with(|info| info.stack_guard)
 }
 
index 6887095c53a7d1cdc24a5a7cf7d1acfe5b9a8baf..2bc280d12743958fbe5a1ff36b9c96960a27b223 100644 (file)
@@ -81,7 +81,7 @@ unsafe fn term(signum: libc::c_int) -> ! {
         // We're calling into functions with stack checks
         stack::record_sp_limit(0);
 
-        let guard = thread_info::stack_guard();
+        let guard = thread_info::stack_guard().unwrap_or(0);
         let addr = (*info).si_addr as usize;
 
         if guard == 0 || addr < guard - PAGE_SIZE || addr >= guard {
index 383726b3e8370ecce8b839ad169807b4f3c2f206..ce531fb13812c30db599fef9a42b10e8f3a8aad0 100644 (file)
@@ -407,7 +407,9 @@ pub fn scoped<'a, T, F>(f: F) -> JoinGuard<'a, T> where
 /// Gets a handle to the thread that invokes it.
 #[stable(feature = "rust1", since = "1.0.0")]
 pub fn current() -> Thread {
-    thread_info::current_thread()
+    thread_info::current_thread().expect("use of std::thread::current() is not \
+                                          possible after the thread's local \
+                                          data has been destroyed")
 }
 
 /// Cooperatively gives up a timeslice to the OS scheduler.
diff --git a/src/test/run-pass/issue-24313.rs b/src/test/run-pass/issue-24313.rs
new file mode 100644 (file)
index 0000000..9acfb04
--- /dev/null
@@ -0,0 +1,39 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::thread;
+use std::env;
+use std::process::Command;
+
+struct Handle(i32);
+
+impl Drop for Handle {
+    fn drop(&mut self) { panic!(); }
+}
+
+thread_local!(static HANDLE: Handle = Handle(0));
+
+fn main() {
+    let args = env::args().collect::<Vec<_>>();
+    if args.len() == 1 {
+        let out = Command::new(&args[0]).arg("test").output().unwrap();
+        let stderr = std::str::from_utf8(&out.stderr).unwrap();
+        assert!(stderr.contains("panicked at 'explicit panic'"),
+                "bad failure message:\n{}\n", stderr);
+    } else {
+        // TLS dtors are not always run on process exit
+        thread::spawn(|| {
+            HANDLE.with(|h| {
+                println!("{}", h.0);
+            });
+        }).join().unwrap();
+    }
+}
+