std: Prevent print panics when using TLS #29491

Merged
merged 1 commit into from Nov 6, 2015

Conversation

Projects
None yet
6 participants
Owner

alexcrichton commented Oct 31, 2015

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 was assigned by rust-highfive Oct 31, 2015

Collaborator

rust-highfive commented Oct 31, 2015

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

sfackler commented Oct 31, 2015

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

Owner

alexcrichton commented Oct 31, 2015

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

Owner

alexcrichton commented Nov 5, 2015

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 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