diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2015-10-31 09:41:21 -0700 |
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2015-10-31 09:41:21 -0700 |
| commit | 858865478358c627f0485f3aef17ffb8bcfee612 (patch) | |
| tree | 4669291950a1dfd73e2025557125fce4f6c1f73f /src/libstd/io | |
| parent | 1a2eaffb63aefba666f55abd992c5e2900654f06 (diff) | |
| download | rust-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.rs | 34 |
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); } |
