std: Prevent print panics when using TLS #29491

Merged
merged 1 commit into from Nov 6, 2015

Projects

None yet

6 participants

@alexcrichton
Owner

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

@alexcrichton alexcrichton 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
8588654
@brson brson was assigned by rust-highfive Oct 31, 2015
Collaborator

r? @brson

(rust_highfive has picked a reviewer for you, use r? to override)

@nagisa nagisa commented on the diff Oct 31, 2015
src/libstd/io/stdio.rs
- 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 |
nagisa
nagisa Oct 31, 2015 Contributor

LOCAL_STDOUT.with would initialize data, so I think this branch should go together with the next arm.

EDIT: this is also fine, because if stdout ends up actually changed from the global default, it would also get initialised.

alexcrichton
alexcrichton Oct 31, 2015 Owner

Yeah this branch could actually go either way, but I figured that we may as well avoid initializing TLS if we're not gonna use it anyway (e.g. as you also mentioned it'd just go to the same place regardless)

@nagisa nagisa commented on the diff Oct 31, 2015
src/libstd/io/stdio.rs
+ // 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 {
nagisa
nagisa Oct 31, 2015 Contributor

IMHO this should block waiting (using e.g. a spinlock) rather than falling back to global stdout.

nagisa
nagisa Oct 31, 2015 Contributor

Ah, never mind me, this is fine, since there’s rarely a case where LOCAL_STDOUT happens to not be global stdout and two levels of locks isn’t really good.

alexcrichton
alexcrichton Oct 31, 2015 Owner

Yeah and currently the only way that this can be in use is if the same thread is already printing (e.g. somewhere up on the stack someone called println!), so we can't actually wait for the resource to become available here because it never will.

This is akin to calling println! in a Display implementation.

Contributor
nagisa commented Oct 31, 2015

LGTM. Would r+.

Member

Do we still use LOCAL_STDOUT? Can we kill it instead?

Owner

@sfackler currently it's used to still capture the stdout of tests, but that's the only use that I know of.

Owner

ping r? @brson

Contributor
brson commented Nov 5, 2015

@bors r+

Contributor
bors commented Nov 5, 2015

📌 Commit 8588654 has been approved by brson

Contributor
bors commented Nov 6, 2015

⌛️ Testing commit 8588654 with merge 7512808...

@bors bors added a commit that referenced this pull request Nov 6, 2015
@bors bors Auto merge of #29491 - alexcrichton:avoid-stdio-tls, r=brson
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
7512808
@bors bors merged commit 8588654 into rust-lang:master Nov 6, 2015

2 checks passed

continuous-integration/travis-ci/pr The Travis CI build passed
Details
homu Test successful
Details
@alexcrichton alexcrichton deleted the alexcrichton:avoid-stdio-tls branch Jan 21, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment