about summary refs log tree commit diff
path: root/src/libstd/io
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2015-10-31 09:41:21 -0700
committerAlex Crichton <alex@alexcrichton.com>2015-10-31 09:41:21 -0700
commit858865478358c627f0485f3aef17ffb8bcfee612 (patch)
tree4669291950a1dfd73e2025557125fce4f6c1f73f /src/libstd/io
parent1a2eaffb63aefba666f55abd992c5e2900654f06 (diff)
downloadrust-858865478358c627f0485f3aef17ffb8bcfee612.tar.gz
rust-858865478358c627f0485f3aef17ffb8bcfee612.zip
std: Prevent print panics when using TLS
Currently if a print happens while a thread is being torn down it may cause a
panic if the LOCAL_STDOUT TLS slot has been destroyed by that point. This adds a
guard to check and prints to the process stdout if that's the case (as we do for
if the slot is already borrowed).

Closes #29488
Diffstat (limited to 'src/libstd/io')
-rw-r--r--src/libstd/io/stdio.rs34
1 files changed, 26 insertions, 8 deletions
diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs
index 31b881bebf0..d6a9778ced2 100644
--- a/src/libstd/io/stdio.rs
+++ b/src/libstd/io/stdio.rs
@@ -16,11 +16,12 @@ use cmp;
 use fmt;
 use io::lazy::Lazy;
 use io::{self, BufReader, LineWriter};
+use libc;
 use sync::{Arc, Mutex, MutexGuard};
 use sys::stdio;
 use sys_common::io::{read_to_end_uninitialized};
 use sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard};
-use libc;
+use thread::LocalKeyState;
 
 /// Stdout used by print! and println! macros
 thread_local! {
@@ -576,14 +577,31 @@ pub fn set_print(sink: Box<Write + Send>) -> Option<Box<Write + Send>> {
            issue = "0")]
 #[doc(hidden)]
 pub fn _print(args: fmt::Arguments) {
-    let result = LOCAL_STDOUT.with(|s| {
-        if s.borrow_state() == BorrowState::Unused {
-            if let Some(w) = s.borrow_mut().as_mut() {
-                return w.write_fmt(args);
-            }
+    // As an implementation of the `println!` macro, we want to try our best to
+    // not panic wherever possible and get the output somewhere. There are
+    // currently two possible vectors for panics we take care of here:
+    //
+    // 1. If the TLS key for the local stdout has been destroyed, accessing it
+    //    would cause a panic. Note that we just lump in the uninitialized case
+    //    here for convenience, we're not trying to avoid a panic.
+    // 2. If the local stdout is currently in use (e.g. we're in the middle of
+    //    already printing) then accessing again would cause a panic.
+    //
+    // If, however, the actual I/O causes an error, we do indeed panic.
+    let result = match LOCAL_STDOUT.state() {
+        LocalKeyState::Uninitialized |
+        LocalKeyState::Destroyed => stdout().write_fmt(args),
+        LocalKeyState::Valid => {
+            LOCAL_STDOUT.with(|s| {
+                if s.borrow_state() == BorrowState::Unused {
+                    if let Some(w) = s.borrow_mut().as_mut() {
+                        return w.write_fmt(args);
+                    }
+                }
+                stdout().write_fmt(args)
+            })
         }
-        stdout().write_fmt(args)
-    });
+    };
     if let Err(e) = result {
         panic!("failed printing to stdout: {}", e);
     }