-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFC: 'thread lifetime #1705
RFC: 'thread lifetime #1705
Conversation
See rust-lang/rust#17954 (comment) for a simpler solution. |
I remember @alexcrichton and @aturon and I discussing precisely this question when working on the Anyway, certainly we could make special rules for Are there other use cases for (I am not that concerned about |
Uh, that's right...
There a no other (at least to might knowledge) where it is strictly required, but there are quite a few where it is handy (pinging @tomaka). |
In any case, the current API is kinda hacky (e.g. it relies on closures, devirtualization, and so on). |
Sorry. I have had several problems with multithreading in general, but nothing comes to my mind that would be solved with |
The API was unsound due to allowing leakage of a reference to another thread. We solve this in a manner similar to libstd, by using a `with` method, which temporarily gives access through a reference by taking a closure. Related to [RFC 1705](rust-lang/rfcs#1705).
Actually, since LLVM now supports TLS emulation, |
The API was unsound due to allowing leakage of a reference to another thread. We solve this in a manner similar to libstd, by using a `with` method, which temporarily gives access through a reference by taking a closure. Related to [RFC 1705](rust-lang/rfcs#1705).
The problems I remember with The bit about In the presence of thread locals, a sendable Rust |
They aren't equal:
Hmm, that's a good point. |
With today's Rust they are and that causes unsafety, not because of data races, but because of thread termination.
You must also stop bad things when they are done in a rather indirect way: #![feature(thread_local)]
fn call_scoped<F, T>(f: F) -> T
where F: FnOnce() -> T + Send
{
// this function can be implemented using e.g. the crossbeam API, and
// we want to allow it.
}
fn set_bomb<'b>(bomb: &mut Option<&'b u32>)
where 'thread: 'b
{
// this function looks legal.
#[thread_local]
static LOCAL: u32 = 0;
*bomb = Some(&LOCAL);
}
fn detonate_bomb<'b>(bomb: &mut Option<&'b u32>, set_bomb: fn(&mut Option<&'b u32>))
{
// look at me! no `'thread` in sight!
call_scoped(|| set_bomb(bomb));
}
fn blow_up() {
// no `Send` in sight here.
let mut bomb = None;
detonate_bomb(&mut bomb, set_bomb);
println!("{:?}", bomb);
}
fn main() {
blow_up()
} |
@arielb1 I believe that in my alternative (function-local borrows), this line: *bomb = Some(&LOCAL); would not, in fact, pass the borrow-checker, because |
Sure. That would also end up with us not needing fn with_thread_locals<T, F: for<'a> FnOnce(ThreadLocals<'a>) -> T>(f: F) -> T { /* <snip> */ } |
@arielb1 Yeah, it's intended to make |
Yeah, thinking more deeply about the idea of I think that the way to think about Certainly @eddyb's notion of "current fn" seems much simpler -- and, thinking back to what @aturon and @alexcrichton and I were banging on a while back, I remember that a special lifetime |
The current fn approach is not ideal, it makes it imossible for library-level TLS vars. |
@ticki You can always create your own handle type which is not |
Yes, I agree it's not ideal -- but it is workable. =) I think you can imagine something like When checking a fn body, we create a skolemized lifetime (let's call it XXX -- this got posted before I was finished thinking it through ;) I realize now that while this scheme makes sense, it would not cause an error @arielb1's example, I don't think. But then, is that a problem? In particular, using this scheme, one could allow a pointer to a thread-local slot to escape to another thread -- but is that necessarily an issue? It seems like it's only a problem if the type of that thread-local variable is a |
Ah, never mind, I see the error in my thinking. I think the key part of @arielb1's example that is problematic is here: fn set_bomb<'b>(bomb: &mut Option<&'b u32>)
where 'thread: 'b
{
// this function looks legal.
#[thread_local]
static LOCAL: u32 = 0;
*bomb = Some(&LOCAL);
} In particular, we can't replace This isn't a fully thought out thought yet either though :) Certainly |
@nikomatsakis You can always just pass it down the stack in the "current fn" approach, it's as valid as any reference to a stack-local variable |
@eddyb yeah, true. It can cross threads, you just can't return it. But I guess you can return it with a wrapper and share that. I have to say that adding a bunch of machinery to cover this corner case doesn't feel very good, especially since I consider directly using |
@rfcbot fcp close I move we close this RFC. The motivation (supporting references to |
Team member @nikomatsakis has proposed to close this. The next step is review by the rest of the tagged teams: No concerns currently listed. Once these reviewers reach consensus, this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
🔔 This is now entering its final comment period, as per the review above. 🔔 |
Since this PR has been in FCP for 16 days and there are no new comments, I'm closing it. For justification see #1705 (comment) |
Add a
'thread
lifetime, which denotes a thread-bounded region.Rendered