Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upstd::thread::LocalKeyState: Add state Initializing #43550
Conversation
rust-highfive
assigned
sfackler
Jul 29, 2017
This comment has been minimized.
This comment has been minimized.
|
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @sfackler (or someone else) soon. If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes. Please see the contribution instructions for more information. |
sfackler
added
the
T-libs
label
Jul 30, 2017
This comment has been minimized.
This comment has been minimized.
|
Thanks for the PR! Could the formatting related changes be removed from this PR for now? |
This comment has been minimized.
This comment has been minimized.
|
@alexcrichton I'm not sure how to do that other than to remove them manually - is there a standard |
carols10cents
added
the
S-waiting-on-author
label
Jul 31, 2017
This comment has been minimized.
This comment has been minimized.
|
@joshlf can you disable auto-rustfmt in your editor, redo the changes, and resubmit them? |
This comment has been minimized.
This comment has been minimized.
|
@alexcrichton Done. |
This comment has been minimized.
This comment has been minimized.
|
I think it'd be good to modify |
This comment has been minimized.
This comment has been minimized.
|
OK, should be ready to go. Removing the WIP tag. |
joshlf
changed the title
[WIP] std::thread::LocalKeyState: Add state Initializing
std::thread::LocalKeyState: Add state Initializing
Aug 4, 2017
This comment has been minimized.
This comment has been minimized.
|
This comment has been minimized.
This comment has been minimized.
|
@eddyb Can you take a quick look at this and let me know if you think anything here conflicts with your PR? I don't think it does, but you know your PR better than I do, and I suspect yours will get merged first so I'll end up rebasing on top of it. I suspect that all I'll need to do is mark some functions and methods |
This comment has been minimized.
This comment has been minimized.
|
This is a lot of code. But I think all you have to check for if: if you take |
This comment has been minimized.
This comment has been minimized.
|
OK sounds good; thanks! |
joshlf
force-pushed the
joshlf:local-key-state
branch
2 times, most recently
from
1ca7c0a
to
06834a9
Aug 8, 2017
This comment has been minimized.
This comment has been minimized.
|
Looks like one of the CI tests failed due to network errors. Does anybody have the ability to tell Travis to retry? Looks like I don't.
|
This comment has been minimized.
This comment has been minimized.
|
|
This comment has been minimized.
This comment has been minimized.
|
Just do a trivial |
joshlf
force-pushed the
joshlf:local-key-state
branch
from
546984e
to
bc6800a
Aug 16, 2017
This comment has been minimized.
This comment has been minimized.
|
@eddyb Do you have any advice for how to get the lifetimes to work in this PR (in particular, in the The current version of the code won't compile because |
This comment has been minimized.
This comment has been minimized.
|
@joshlf The one function I had to deal with had the same problem, btw - look at how I changed |
This comment has been minimized.
This comment has been minimized.
|
|
This comment has been minimized.
This comment has been minimized.
|
@joshlf Btw you should use |
This comment has been minimized.
This comment has been minimized.
|
@eddyb Do you know if there's a way to |
This comment has been minimized.
This comment has been minimized.
|
@joshlf You can run it now and it should kill merge commits. |
joshlf
force-pushed the
joshlf:local-key-state
branch
3 times, most recently
from
19d4391
to
a96d10d
Aug 16, 2017
This comment has been minimized.
This comment has been minimized.
|
Hey @alexcrichton , can I ask you a question about some code that unsafe extern fn destroy_value<T: 'static>(ptr: *mut u8) {
// The OS TLS ensures that this key contains a NULL value when this
// destructor starts to run. We set it back to a sentinel value of 1 to
// ensure that any future calls to `get` for this thread will return
// `None`.
//
// Note that to prevent an infinite loop we reset it back to null right
// before we return from the destructor ourselves.
let ptr = Box::from_raw(ptr as *mut Value<T>);
let key = ptr.key;
key.os.set(1 as *mut u8);
drop(ptr);
key.os.set(ptr::null_mut());
}When you set the pointer back to null before returning, doesn't that make it so that a future access of the key after the destructor has run will cause the key to be re-initialized because a null pointer looks like an uninitialized key rather than a destroyed one? This could happen if the destructor of another TLS key accesses this key, and that destructor is called after this key's destructor. |
This comment has been minimized.
This comment has been minimized.
|
@joshlf It's tri-state, IIRC they go like this (conceptually, the actual types might differ):
|
This comment has been minimized.
This comment has been minimized.
|
@eddyb I believe you're right, but my concern is what happens after the destruction has finished (after which, as the comment that I quoted says, the pointer is set back from 1 to 0). The implementation of pub unsafe fn get(&'static self) -> Option<&'static UnsafeCell<Option<T>>> {
let ptr = self.os.get() as *mut Value<T>;
if !ptr.is_null() {
if ptr as usize == 1 {
return None
}
return Some(&(*ptr).value);
}
// If the lookup returned null, we haven't initialized our own
// local copy, so do that now.
let ptr: Box<Value<T>> = box Value {
key: self,
value: UnsafeCell::new(None),
};
let ptr = Box::into_raw(ptr);
self.os.set(ptr as *mut u8);
Some(&(*ptr).value)
} |
joshlf
force-pushed the
joshlf:local-key-state
branch
from
6eed19e
to
7635f72
Sep 6, 2017
This comment has been minimized.
This comment has been minimized.
Indeed! We don't have much of an option here, though, unless we track this data elsewhere. If we leave it as 1 then the OS will continue to call the destructor function rather than returning. |
This comment has been minimized.
This comment has been minimized.
|
OK, in that case I think I'll clarify that in the documentation, and add a TODO in case somebody comes along and figures out how to fix it. EDIT: I see now that this is already documented in the doc comment on |
joshlf
force-pushed the
joshlf:local-key-state
branch
5 times, most recently
from
1a14f08
to
1dbbfd6
Sep 6, 2017
This comment has been minimized.
This comment has been minimized.
|
OK, it's ready for review. Just to recap:
|
This comment has been minimized.
This comment has been minimized.
|
|
alexcrichton
reviewed
Sep 7, 2017
| if let &LocalKeyValue::Valid(ref inner) = &*slot.get() { | ||
| // Do this in a separate if branch (rather than just part of the | ||
| // match statement in the else block) to increase the performance | ||
| // of the fast path. |
This comment has been minimized.
This comment has been minimized.
alexcrichton
Sep 7, 2017
Member
Does this measurably help the codegen?
I'd naively expect not, butI'm curious if you have benchmarks.
This comment has been minimized.
This comment has been minimized.
joshlf
Sep 7, 2017
Author
Contributor
So I'm actually not sure. I tried benchmarking it, but I'm not confident that my xargo command is testing what I think it is because I ran it with a version of the code that had a compiler error, and it still ran properly, so I'm not sure what artifact xargo is picking up to link as libstd. Here's what I'm running:
RUST_BACKTRACE=1 RUSTFLAGS='-C opt-level=3' XARGO_RUST_SRC=/path/to/rust/src/ xargo run --release
Given this command, it looks like there's not much of a difference, but I'm skeptical for the reasons mentioned. If you want to give it a try yourself (since I assume you're better at this whole build pipeline thing), it would be give me more confidence.
alexcrichton
reviewed
Sep 7, 2017
|
|
||
| // Register a destructor for the value. register_dtor should be called | ||
| // after the key has been transitioned into the Valid state. | ||
| register_dtor: unsafe fn(), |
This comment has been minimized.
This comment has been minimized.
alexcrichton
Sep 7, 2017
Member
Couldn't this still happen on the first call to get? If initialization fails that means we'll just run the destructor on a noop value, right?
This comment has been minimized.
This comment has been minimized.
joshlf
Sep 7, 2017
Author
Contributor
I wanted to avoid adding an extra branch in the hot path, and that would require branching in get to see whether the destructor had been registered already.
alexcrichton
reviewed
Sep 7, 2017
| // Set inner to Destroyed before we drop tmp so that a | ||
| // recursive call to get (called from the destructor) will | ||
| // be able to detect that the value is already being dropped. | ||
| ptr::write((*ptr).inner.get(), __LocalKeyValue::Destroyed); |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
alexcrichton
reviewed
Sep 7, 2017
| let ptr = self.os.get() as *mut Value<T>; | ||
| if !ptr.is_null() { | ||
| if ptr as usize == 1 { | ||
| return None | ||
| // The destructor was already called (and set self.os to 1). | ||
| return &*(&self.dummy_destroyed as *const _); |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
joshlf
Sep 7, 2017
Author
Contributor
Oh, honestly I think I wasn't sure what to do there so I just copied what mod fast does, but of course that doesn't make sense because mod os uses a truly static value. I'll change it back.
joshlf
force-pushed the
joshlf:local-key-state
branch
from
1dbbfd6
to
523f8a5
Sep 7, 2017
joshlf
force-pushed the
joshlf:local-key-state
branch
from
523f8a5
to
fe1894a
Sep 7, 2017
This comment has been minimized.
This comment has been minimized.
|
Hm ok so looking into the perf impact here, I'm getting more and more wary about this change... Right now the Today, however, calls to In other words, I think that fundamentally adding an "initializing" state is going to come at a perf hit to all thread locals. With that in mind, I wonder if there are perhaps other methods to tackle this for your use case? I've always imagined that |
This comment has been minimized.
This comment has been minimized.
Can't pub fn with<F, R>(&'static self, f: F) -> R
where F: FnOnce(&T) -> R {
self.try_with(f).expect("cannot access a TLS value during or \
after it is destroyed")
}
I already decided to give up on making this work for allocation (see this comment). However, this is still useful for other recursive initialization dependencies. |
This comment has been minimized.
This comment has been minimized.
|
I commented awhile ago with this example which generates this IR for a In general yes,
In that case I'm somewhat inclined to close this? This PR isn't needed for you use case, is technically a regression in behavior, and is currently a regression in performance. |
This comment has been minimized.
This comment has been minimized.
|
OK, fair enough. I'll open a PR to clarify that TLS constructors cannot depend recursively on the values that they construct, but other than the case of allocators (and perhaps some weird dependency injection scenarios), this should only arise within a given crate, in which case it's more reasonable to ask authors to just architect their stuff differently. |
This comment has been minimized.
This comment has been minimized.
|
PR added: #44396 |
This comment has been minimized.
This comment has been minimized.
|
Ok, closing in favor of #44396 in that case |
joshlf commentedJul 29, 2017
Closes #43491.
I know that the issue hasn't been discussed or approved yet, but I figured I'd make this PR to provide something concrete for the discussion.
Open question: This is my first serious PR, and I'm not sure what to do about my
rustfmtautomatically re-formatting existing code that I didn't change. Somebody's guidance here would be appreciated.