RFC: Add a thread local storage module, std::tls #461

Merged
merged 3 commits into from Nov 21, 2014

Conversation

Projects
None yet
7 participants
@alexcrichton
Member

alexcrichton commented Nov 11, 2014

Introduce a new thread local storage module to the standard library, std::tls,
providing:

  • Scoped TLS, a non-owning variant of TLS for any value.
  • Owning TLS, an owning, dynamically initialized, dynamically destructed
    variant, similar to std::local_data today.

Rendered

RFC: Add a thread local storage module, std::tls
Introduce a new thread local storage module to the standard library, `std::tls`,
providing:

* Scoped TLS, a non-owning variant of TLS for any value.
* Owning TLS, an owning, dynamically initialized, dynamically destructed
  variant, similar to `std::local_data` today.
+lost when writing Unix-only code.
+
+Destructor support for Windows will be provided through a custom implementation
+of tracking known destructors for TLS keys.

This comment has been minimized.

@bill-myers

bill-myers Nov 12, 2014

On Windows, you can set a callback for when a thread exits, and you can then iterate over all TLS keys and destroy them if they are set.

See https://github.com/ChromiumWebApps/chromium/blob/master/base/threading/thread_local_storage_win.cc#L42 for how to do it without using DllMain.

Iterating over all possible TLS keys is quadratic instead of linear if they are sparsely used, but it should be possible to write code that directly accesses the Windows TEB to avoid this if desired.

@bill-myers

bill-myers Nov 12, 2014

On Windows, you can set a callback for when a thread exits, and you can then iterate over all TLS keys and destroy them if they are set.

See https://github.com/ChromiumWebApps/chromium/blob/master/base/threading/thread_local_storage_win.cc#L42 for how to do it without using DllMain.

Iterating over all possible TLS keys is quadratic instead of linear if they are sparsely used, but it should be possible to write code that directly accesses the Windows TEB to avoid this if desired.

This comment has been minimized.

@alexcrichton

alexcrichton Nov 12, 2014

Member

Indeed! I ran across that same trick when whipping up the sample implementation. I definitely agree that the implementation can be improved as well!

@alexcrichton

alexcrichton Nov 12, 2014

Member

Indeed! I ran across that same trick when whipping up the sample implementation. I definitely agree that the implementation can be improved as well!

@aturon

This comment has been minimized.

Show comment
Hide comment
@aturon

aturon Nov 13, 2014

Member

cc @thestinger, would be good to get your feedback on this proposal.

Member

aturon commented Nov 13, 2014

cc @thestinger, would be good to get your feedback on this proposal.

+
+### Destructor support
+
+The major difference between Unix and Windows TLS support is that Unix supports

This comment has been minimized.

@thestinger

thestinger Nov 13, 2014

It's also possible to use only #[thread_local] with no secondary pthread_key_create / synchronization by using C++11 destructor support. On Linux, glibc defines a fn __cxa_thread_atexit_impl(dtor: unsafe extern "C" fn(ptr: *mut c_void), ptr: *mut c_void, dso_symbol: *mut i8) where dso_symbol can just be retrived by defining static mut __dso_handle: i8. On OS X, there's a fn _tlv_atexit(dtor: unsafe extern "C" fn(ptr: *mut c_void), ptr: *mut c_void); function. Rust could use the weak symbol trick to call these when available and fall back to a crappier implementation on top of dynamic TLS.

@thestinger

thestinger Nov 13, 2014

It's also possible to use only #[thread_local] with no secondary pthread_key_create / synchronization by using C++11 destructor support. On Linux, glibc defines a fn __cxa_thread_atexit_impl(dtor: unsafe extern "C" fn(ptr: *mut c_void), ptr: *mut c_void, dso_symbol: *mut i8) where dso_symbol can just be retrived by defining static mut __dso_handle: i8. On OS X, there's a fn _tlv_atexit(dtor: unsafe extern "C" fn(ptr: *mut c_void), ptr: *mut c_void); function. Rust could use the weak symbol trick to call these when available and fall back to a crappier implementation on top of dynamic TLS.

This comment has been minimized.

@alexcrichton

alexcrichton Nov 14, 2014

Member

Indeed! If you take a look at the sample implementation you'll see that it does precisely that!

@alexcrichton

alexcrichton Nov 14, 2014

Member

Indeed! If you take a look at the sample implementation you'll see that it does precisely that!

+
+Some other alternatives might include:
+
+* A 0-cost abstraction over `#[thread_local]` and OS-based TLS which does not

This comment has been minimized.

@thestinger

thestinger Nov 13, 2014

See the point about _tlv_atexit, etc. above. It's also only truly zero cost if there's the ability to define variables without the branch for dynamic initialization / destruction which is why the Cell version existed.

@thestinger

thestinger Nov 13, 2014

See the point about _tlv_atexit, etc. above. It's also only truly zero cost if there's the ability to define variables without the branch for dynamic initialization / destruction which is why the Cell version existed.

This comment has been minimized.

@alexcrichton

alexcrichton Nov 14, 2014

Member

Note that the sample implementation does indeed use the destructor registration functions, and it does actually have a statically initialized variant, I just felt that it was confusing to expose so many variants of TLS. I'll benchmark this though so we can get a good handle on how big of a hit this is.

@alexcrichton

alexcrichton Nov 14, 2014

Member

Note that the sample implementation does indeed use the destructor registration functions, and it does actually have a statically initialized variant, I just felt that it was confusing to expose so many variants of TLS. I'll benchmark this though so we can get a good handle on how big of a hit this is.

This comment has been minimized.

@alexcrichton

alexcrichton Nov 14, 2014

Member

Ah one other thing which led me to start out with dynamic-only is the general lack of statically initialized values in the standard library. As you've found out, you can't statically initialize a Cell or a RefCell for example, and leaking an UnsafeCell as an api is pretty unfortunate. This means today for statically initialized TLS with internal mutability that we've got one of two options:

  1. Provided Cell/RefCell variants baked in (reimplementing Cell/RefCell) as you did with macros.
  2. Force usage of UnsafeCell which allows for static initialization.

I'm not a super fan of either of these options, and would prefer to hold out for something like const fn or generic constants so we have a better story for statically initialized data. In I found the usage around something statically initialized to be pretty uncomfortable unless we provided a reimplementation of Cell/RefCell (which I'd rather not do for composability reasons), so I opted to have only-dynamic for now.

I do think we may be able to add a static variant later without much pain, but I'd just prefer to see some more well-supported statically initialized values before that time.

@alexcrichton

alexcrichton Nov 14, 2014

Member

Ah one other thing which led me to start out with dynamic-only is the general lack of statically initialized values in the standard library. As you've found out, you can't statically initialize a Cell or a RefCell for example, and leaking an UnsafeCell as an api is pretty unfortunate. This means today for statically initialized TLS with internal mutability that we've got one of two options:

  1. Provided Cell/RefCell variants baked in (reimplementing Cell/RefCell) as you did with macros.
  2. Force usage of UnsafeCell which allows for static initialization.

I'm not a super fan of either of these options, and would prefer to hold out for something like const fn or generic constants so we have a better story for statically initialized data. In I found the usage around something statically initialized to be pretty uncomfortable unless we provided a reimplementation of Cell/RefCell (which I'd rather not do for composability reasons), so I opted to have only-dynamic for now.

I do think we may be able to add a static variant later without much pain, but I'd just prefer to see some more well-supported statically initialized values before that time.

+ values must be pointer-sized, implying that the rust value must itself be
+ boxed (whereas `#[thread_local]` can support any type of any size).
+
+* A variant of the `tls!` macro could be used where dynamic initialization is

This comment has been minimized.

@thestinger

thestinger Nov 13, 2014

Ah, that's right here.

@thestinger

thestinger Nov 13, 2014

Ah, that's right here.

text/0000-tls-overhaul.md
+* There is no variant of TLS for statically initialized data. Currently the
+ `std::tls` module requires dynamic initialization, which means a slight
+ penalty is paid on each access (a check to see if it's already initialized).
+* The specification of destructors on owned TLS values is still somewhat shaky

This comment has been minimized.

@thestinger

thestinger Nov 13, 2014

Using the C++11 destructor support would be much more robust. It doesn't have weird limitations like PTHREAD_DESTRUCTOR_ITERATIONS, and a fallback implementation when the platform doesn't properly support C++11 (nearly all do) just needs to be safe rather than perfect.

@thestinger

thestinger Nov 13, 2014

Using the C++11 destructor support would be much more robust. It doesn't have weird limitations like PTHREAD_DESTRUCTOR_ITERATIONS, and a fallback implementation when the platform doesn't properly support C++11 (nearly all do) just needs to be safe rather than perfect.

This comment has been minimized.

@alexcrichton

alexcrichton Nov 14, 2014

Member

I'm actually not super familiar with the semantics of C++11 destructors with respect to thread_local, do you know if there's some documentation that you can point me at? The sample implementation actually does this already where it favors the oddly-named destructor registration functions over an OS-based implementation, but the OS-based implementation is provided as a fallback.

@alexcrichton

alexcrichton Nov 14, 2014

Member

I'm actually not super familiar with the semantics of C++11 destructors with respect to thread_local, do you know if there's some documentation that you can point me at? The sample implementation actually does this already where it favors the oddly-named destructor registration functions over an OS-based implementation, but the OS-based implementation is provided as a fallback.

This comment has been minimized.

@thestinger

thestinger Nov 14, 2014

If there are 100 TLS variables and each one has a destructor accessing the next for the first time, I think it will lead to leaks with the old POSIX TLS because it only cycles N times (4 on Linux IIRC). AFAIK, that problem was solved for C++11 TLS by just giving it guaranteed sensible semantics (run until completion).

@thestinger

thestinger Nov 14, 2014

If there are 100 TLS variables and each one has a destructor accessing the next for the first time, I think it will lead to leaks with the old POSIX TLS because it only cycles N times (4 on Linux IIRC). AFAIK, that problem was solved for C++11 TLS by just giving it guaranteed sensible semantics (run until completion).

This comment has been minimized.

@alexcrichton

alexcrichton Nov 14, 2014

Member

Do you know of documentation for the C++11 destructor semantics in thread_local? I'll reiterate that we do use the destructor registration functions that it uses when available, I'd like to copy the semantics to the fallback implementation (OS TLS) as much as possible, however.

@alexcrichton

alexcrichton Nov 14, 2014

Member

Do you know of documentation for the C++11 destructor semantics in thread_local? I'll reiterate that we do use the destructor registration functions that it uses when available, I'd like to copy the semantics to the fallback implementation (OS TLS) as much as possible, however.

text/0000-tls-overhaul.md
+
+* Leaking TLS keys on Windows is certainly not ideal (see the description
+ above).
+* There is no variant of TLS for statically initialized data. Currently the

This comment has been minimized.

@thestinger

thestinger Nov 13, 2014

A branch + pointer offset is significantly worse than just a pointer offset, even if it's marked as likely to succeed for LLVM.

@thestinger

thestinger Nov 13, 2014

A branch + pointer offset is significantly worse than just a pointer offset, even if it's marked as likely to succeed for LLVM.

This comment has been minimized.

@alexcrichton

alexcrichton Nov 14, 2014

Member

I definitely agree that there's a performance loss, but after some benchmarking, "significantly worse" may be overstating it a bit, I found the hit to be ~10%:

test dynamic      ... bench:      1725 ns/iter (+/- 71)
test local_data   ... bench:      6739 ns/iter (+/- 33)
test os           ... bench:      9787 ns/iter (+/- 215)
test scoped       ... bench:      1529 ns/iter (+/- 10)
test statik       ... bench:      1544 ns/iter (+/- 16)
test thread_local ... bench:      1545 ns/iter (+/- 20)

benchmarks

@alexcrichton

alexcrichton Nov 14, 2014

Member

I definitely agree that there's a performance loss, but after some benchmarking, "significantly worse" may be overstating it a bit, I found the hit to be ~10%:

test dynamic      ... bench:      1725 ns/iter (+/- 71)
test local_data   ... bench:      6739 ns/iter (+/- 33)
test os           ... bench:      9787 ns/iter (+/- 215)
test scoped       ... bench:      1529 ns/iter (+/- 10)
test statik       ... bench:      1544 ns/iter (+/- 16)
test thread_local ... bench:      1545 ns/iter (+/- 20)

benchmarks

This comment has been minimized.

@thestinger

thestinger Nov 14, 2014

It's far more than 10%. You're not really measuring anything by doing naive micro-benchmarks of branches.

@thestinger

thestinger Nov 14, 2014

It's far more than 10%. You're not really measuring anything by doing naive micro-benchmarks of branches.

This comment has been minimized.

@alexcrichton

alexcrichton Nov 14, 2014

Member

Do you have some representative examples I could measure? I'd love to get a handle on what sort of impact this has.

@alexcrichton

alexcrichton Nov 14, 2014

Member

Do you have some representative examples I could measure? I'd love to get a handle on what sort of impact this has.

This comment has been minimized.

@thestinger

thestinger Nov 14, 2014

The call to black_box is far more expensive than a TLS access, and turning off optimizations won't yield useful numbers without a lot of effort put into crafting a real benchmark. The cost of accessing TLS in the executable is comparable to the cost of accessing a global variable or data on the stack.

@thestinger

thestinger Nov 14, 2014

The call to black_box is far more expensive than a TLS access, and turning off optimizations won't yield useful numbers without a lot of effort put into crafting a real benchmark. The cost of accessing TLS in the executable is comparable to the cost of accessing a global variable or data on the stack.

This comment has been minimized.

@thestinger

thestinger Nov 14, 2014

Static TLS access is just a single offset instruction with a constant offset so IMO the only way you're going to get a sane benchmark is generating the assembly for a fetch, increment and set of an integer in TLS (inside a library pub fn where it can't optimize out) and then copy-pasting it 100000 times. Using a loop will be measuring the cost of looping at least as much as the cost of the pointer offset vs. pointer offset + branch inside the loop.

@thestinger

thestinger Nov 14, 2014

Static TLS access is just a single offset instruction with a constant offset so IMO the only way you're going to get a sane benchmark is generating the assembly for a fetch, increment and set of an integer in TLS (inside a library pub fn where it can't optimize out) and then copy-pasting it 100000 times. Using a loop will be measuring the cost of looping at least as much as the cost of the pointer offset vs. pointer offset + branch inside the loop.

This comment has been minimized.

@alexcrichton

alexcrichton Nov 14, 2014

Member

Ah I'm sorry, I should have clarified. The benchmarks were run with cargo bench --features thread-local which enabled the #[thread_local] usage for the various macros, as well as building everything with optimizations. Upon removing the calls to black_box the statik and thread_local benchmarks are completely optimized away (verified by inspecting the disassembly):

running 6 tests
test dynamic      ... bench:       529 ns/iter (+/- 251)
test local_data   ... bench:      5957 ns/iter (+/- 4)
test os           ... bench:      9789 ns/iter (+/- 12)
test scoped       ... bench:      1528 ns/iter (+/- 6)
test statik       ... bench:         1 ns/iter (+/- 0)
test thread_local ... bench:         1 ns/iter (+/- 0)

test result: ok. 0 passed; 0 failed; 0 ignored; 6 measured

I didn't find this very useful, so I added black_box to prevent this from happening. I didn't intend for it to hinder optimizations. Do you know of a way that I could measure without hindering optimizations?

@alexcrichton

alexcrichton Nov 14, 2014

Member

Ah I'm sorry, I should have clarified. The benchmarks were run with cargo bench --features thread-local which enabled the #[thread_local] usage for the various macros, as well as building everything with optimizations. Upon removing the calls to black_box the statik and thread_local benchmarks are completely optimized away (verified by inspecting the disassembly):

running 6 tests
test dynamic      ... bench:       529 ns/iter (+/- 251)
test local_data   ... bench:      5957 ns/iter (+/- 4)
test os           ... bench:      9789 ns/iter (+/- 12)
test scoped       ... bench:      1528 ns/iter (+/- 6)
test statik       ... bench:         1 ns/iter (+/- 0)
test thread_local ... bench:         1 ns/iter (+/- 0)

test result: ok. 0 passed; 0 failed; 0 ignored; 6 measured

I didn't find this very useful, so I added black_box to prevent this from happening. I didn't intend for it to hinder optimizations. Do you know of a way that I could measure without hindering optimizations?

This comment has been minimized.

@thestinger

thestinger Nov 14, 2014

You could measure the time taken by a no-op loop calling black_box on a local variable, and subtract that from the time taken by the various benchmarks. AFAICT, with optimizations enabled LLVM is just going to optimize out the repeated TLS offsets to a single reused offset (not that pointer arithmetic is expensive) so it's just going to be measuring the cost of incrementing memory at that location. I wouldn't expect it to be more expensive than a global variable even without the TLS optimizations though - both are dynamic offsets in position independent code.

@thestinger

thestinger Nov 14, 2014

You could measure the time taken by a no-op loop calling black_box on a local variable, and subtract that from the time taken by the various benchmarks. AFAICT, with optimizations enabled LLVM is just going to optimize out the repeated TLS offsets to a single reused offset (not that pointer arithmetic is expensive) so it's just going to be measuring the cost of incrementing memory at that location. I wouldn't expect it to be more expensive than a global variable even without the TLS optimizations though - both are dynamic offsets in position independent code.

This comment has been minimized.

@alexcrichton

alexcrichton Nov 14, 2014

Member

I've added a few more benchmarks, and these are the results:

test dynamic         ... bench:      1573 ns/iter (+/- 64)
test global_variable ... bench:      1797 ns/iter (+/- 16)
test local_data      ... bench:      5246 ns/iter (+/- 14)
test local_variable  ... bench:      1531 ns/iter (+/- 2)
test noop            ... bench:       271 ns/iter (+/- 3)
test os              ... bench:      9787 ns/iter (+/- 55)
test scoped          ... bench:      1529 ns/iter (+/- 7)
test statik          ... bench:      1543 ns/iter (+/- 5)
test thread_local    ... bench:      1543 ns/iter (+/- 1)

While I don't dispute that a dynamically initialized variable has more instructions on the fast path than a statically initialized one, it seems that the impact is quite minor. These numbers make it look like a global variable is a tad bit slower! If the cost of measuring the benchmarking loop is significant in terms of measurements, then I would expect the conclusion to be that the unit being benchmarked is quite fast.

I'd also like to reiterate that I would like to support statically initialized TLS in terms of an API, but the ergonomics of doing so make it infeasible today in my personal opinion. Do note that it is entirely implemented in the sample implementation. API-wise, however providing two variants (dynamic/static) also seems somewhat overkill versus providing only one to worry about. I suspect with an extension to the macro syntax in the future (and ergonomic static initialization), we could tweak the macro to something like: tls!(static FOO: Cell<uint> := Cell::new(3)) where here the := means "statically initialized" and Cell::new(3) is evaluated at compile time.

I would also expect the number of candidates for a statically initialized TLS variable to be fairly small today. It's pretty rare to work with a data structure that can be statically initialized, so in practice if we provided 2 possibilities I would expect the dynamic variant's usage to far outweigh the static variant's usage. If, however, we see usage going in the other direction, we could certainly tweak the semantics!

@alexcrichton

alexcrichton Nov 14, 2014

Member

I've added a few more benchmarks, and these are the results:

test dynamic         ... bench:      1573 ns/iter (+/- 64)
test global_variable ... bench:      1797 ns/iter (+/- 16)
test local_data      ... bench:      5246 ns/iter (+/- 14)
test local_variable  ... bench:      1531 ns/iter (+/- 2)
test noop            ... bench:       271 ns/iter (+/- 3)
test os              ... bench:      9787 ns/iter (+/- 55)
test scoped          ... bench:      1529 ns/iter (+/- 7)
test statik          ... bench:      1543 ns/iter (+/- 5)
test thread_local    ... bench:      1543 ns/iter (+/- 1)

While I don't dispute that a dynamically initialized variable has more instructions on the fast path than a statically initialized one, it seems that the impact is quite minor. These numbers make it look like a global variable is a tad bit slower! If the cost of measuring the benchmarking loop is significant in terms of measurements, then I would expect the conclusion to be that the unit being benchmarked is quite fast.

I'd also like to reiterate that I would like to support statically initialized TLS in terms of an API, but the ergonomics of doing so make it infeasible today in my personal opinion. Do note that it is entirely implemented in the sample implementation. API-wise, however providing two variants (dynamic/static) also seems somewhat overkill versus providing only one to worry about. I suspect with an extension to the macro syntax in the future (and ergonomic static initialization), we could tweak the macro to something like: tls!(static FOO: Cell<uint> := Cell::new(3)) where here the := means "statically initialized" and Cell::new(3) is evaluated at compile time.

I would also expect the number of candidates for a statically initialized TLS variable to be fairly small today. It's pretty rare to work with a data structure that can be statically initialized, so in practice if we provided 2 possibilities I would expect the dynamic variant's usage to far outweigh the static variant's usage. If, however, we see usage going in the other direction, we could certainly tweak the semantics!

+
+# Unresolved questions
+
+* Are the questions around destructors vague enough to warrant the `get` method

This comment has been minimized.

@thestinger

thestinger Nov 13, 2014

For the pure #[thread_local] / C++11 destructor support case, it just needs to have a thread-local boolean tracking the initialization state. It can set it back to uninitialized before calling the destructor, and then further accesses will just reinitialize it. I think it's perfectly sane for it to infinite loop if that's the programmer has essentially asked it to do that by having dependency cycles between the destructors.

On platforms still lacking this support, it's still possible to make it memory safe but it does mean that accessing uninitialized TLS in destructors should be considered as a bug even if it's intended / would be sane with C++11 semantics because it may trigger memory leaks (PTHREAD_DESTRUCTOR_ITERATIONS, etc.).

@thestinger

thestinger Nov 13, 2014

For the pure #[thread_local] / C++11 destructor support case, it just needs to have a thread-local boolean tracking the initialization state. It can set it back to uninitialized before calling the destructor, and then further accesses will just reinitialize it. I think it's perfectly sane for it to infinite loop if that's the programmer has essentially asked it to do that by having dependency cycles between the destructors.

On platforms still lacking this support, it's still possible to make it memory safe but it does mean that accessing uninitialized TLS in destructors should be considered as a bug even if it's intended / would be sane with C++11 semantics because it may trigger memory leaks (PTHREAD_DESTRUCTOR_ITERATIONS, etc.).

This comment has been minimized.

@alexcrichton

alexcrichton Nov 14, 2014

Member

For now the sample implementation does have a boolean for this and it doesn't ever flip it back to false after it's been set. This means that once deinitialized the get function will always return None, preventing cycles. The problem arises for OS-based TLS because this was difficult to do for pthread-TLS, for example, leading to some of the more uncomfortable questions.

@alexcrichton

alexcrichton Nov 14, 2014

Member

For now the sample implementation does have a boolean for this and it doesn't ever flip it back to false after it's been set. This means that once deinitialized the get function will always return None, preventing cycles. The problem arises for OS-based TLS because this was difficult to do for pthread-TLS, for example, leading to some of the more uncomfortable questions.

This comment has been minimized.

@thestinger

thestinger Nov 14, 2014

I think it's wrong for the get function to return Option. This isn't a runtime error for the program to handle, it's a bug. The only sane thing to do with it is to unwrap it because a correct program will not have these cycles.

@thestinger

thestinger Nov 14, 2014

I think it's wrong for the get function to return Option. This isn't a runtime error for the program to handle, it's a bug. The only sane thing to do with it is to unwrap it because a correct program will not have these cycles.

This comment has been minimized.

@thestinger

thestinger Nov 14, 2014

The number of branches in the fast path isn't unimportant.

@thestinger

thestinger Nov 14, 2014

The number of branches in the fast path isn't unimportant.

This comment has been minimized.

@alexcrichton

alexcrichton Nov 14, 2014

Member

Would you be in favor of get calling panic! instead?

@alexcrichton

alexcrichton Nov 14, 2014

Member

Would you be in favor of get calling panic! instead?

This comment has been minimized.

@thestinger

thestinger Nov 14, 2014

Yes, and with a single integer / enum tracking the state so there can be one branch for the fast path. Special handling of destruction would essentially be free in that case because it would only be an extra cost for initialization (which by definition only happens once).

@thestinger

thestinger Nov 14, 2014

Yes, and with a single integer / enum tracking the state so there can be one branch for the fast path. Special handling of destruction would essentially be free in that case because it would only be an extra cost for initialization (which by definition only happens once).

@aturon aturon referenced this pull request in rust-lang/rust Nov 15, 2014

Merged

Finish runtime removal #18967

bors added a commit to rust-lang/rust that referenced this pull request Nov 17, 2014

auto merge of #18967 : aturon/rust/remove-runtime, r=alexcrichton
This PR completes the removal of the runtime system and green-threaded abstractions as part of implementing [RFC 230](rust-lang/rfcs#230).

Specifically:

* It removes the `Runtime` trait and `bookkeeping` module, welding the scheduling infrastructure directly to native threads, and allowing a Rust program to shut down immediately once its main thread has terminated even if others are still running.

* It removes `libgreen` and `libnative` entirely.

* It rewrites `sync::mutex` as a trivial layer on top of native mutexes. Eventually, the two modules will be merged.

* It hides the vast majority of `std::rt`.

This completes the basic task of removing the runtime system (I/O and scheduling) and components that depend on it. 

After this lands, a follow-up PR will pull the `rustrt` crate back into `std`, turn `std::task` into `std::thread` (with API changes to go along with it). Other changes, including new [TLS](rust-lang/rfcs#461) and synchronization are in the RFC or pre-RFC phase.

Closes #17325
Closes #18687

[breaking-change]

r? @alexcrichton

bors added a commit to rust-lang/rust that referenced this pull request Nov 18, 2014

auto merge of #18967 : aturon/rust/remove-runtime, r=alexcrichton
This PR completes the removal of the runtime system and green-threaded abstractions as part of implementing [RFC 230](rust-lang/rfcs#230).

Specifically:

* It removes the `Runtime` trait and `bookkeeping` module, welding the scheduling infrastructure directly to native threads, and allowing a Rust program to shut down immediately once its main thread has terminated even if others are still running.

* It removes `libgreen` and `libnative` entirely.

* It rewrites `sync::mutex` as a trivial layer on top of native mutexes. Eventually, the two modules will be merged.

* It hides the vast majority of `std::rt`.

This completes the basic task of removing the runtime system (I/O and scheduling) and components that depend on it. 

After this lands, a follow-up PR will pull the `rustrt` crate back into `std`, turn `std::task` into `std::thread` (with API changes to go along with it). Other changes, including new [TLS](rust-lang/rfcs#461) and synchronization are in the RFC or pre-RFC phase.

Closes #17325
Closes #18687

[breaking-change]

r? @alexcrichton
@brson

This comment has been minimized.

Show comment
Hide comment
@brson

brson Nov 18, 2014

Contributor

ISTM like scoped TLS may be the 'preferred' way to use TLS as it is more structured, harder to create error-prone access patterns, but this design gives traditional tls the better module name.

Contributor

brson commented Nov 18, 2014

ISTM like scoped TLS may be the 'preferred' way to use TLS as it is more structured, harder to create error-prone access patterns, but this design gives traditional tls the better module name.

@brson

This comment has been minimized.

Show comment
Hide comment
@brson

brson Nov 18, 2014

Contributor

This RFC doesn't seem to explain how it achieves acceptable performance compared to local_data and #[thread_local], which is one of the main concerns.

Contributor

brson commented Nov 18, 2014

This RFC doesn't seem to explain how it achieves acceptable performance compared to local_data and #[thread_local], which is one of the main concerns.

@brson

This comment has been minimized.

Show comment
Hide comment
@brson

brson Nov 18, 2014

I don't see in the description above what this is referring to.

I don't see in the description above what this is referring to.

This comment has been minimized.

Show comment
Hide comment
@alexcrichton

alexcrichton Nov 18, 2014

Owner

Oops, I good point. I removed this awhile ago but forgot to remove the drawback.

Owner

alexcrichton replied Nov 18, 2014

Oops, I good point. I removed this awhile ago but forgot to remove the drawback.

@alexcrichton

This comment has been minimized.

Show comment
Hide comment
@alexcrichton

alexcrichton Nov 18, 2014

Member

@brson that's a good point about naming, we could take no preference at all and perhaps move everything in to std::tls as well with DynamicKey and ScopedKey with dynamic_tls! and scoped_tls! macros. I think I'm somewhat biased towards "owned by default" (and so is the repo), but that's also probably because it's all we have today.

The sample implementation's benchmarking numbers are currently all on par with #[thread_local] (numbers are in some of the above comments), but I'll try to call it out specifically how this is trying to mirror the system primitives as much as possible

Today I also discovered an unsoundness with this API as proposed, specifically with the get method on owning TLS keys. The return Ref<T> satisfies the 'static bound, allowing you to store references to other TLS variables in TLS itself, perhaps causing undefined behavior during destructors. Which is to say that TLS variables can store stale references to one another:

#![feature(phase)]

#[phase(plugin, link)]
extern crate tls;

use std::cell::RefCell;

dynamic_tls!(static FOO: Box<int> = box 1)
dynamic_tls!(static BAR: RefCell<Option<tls::dynamic::Ref<Box<int>>>> = RefCell::new(None))

fn main() {
    *BAR.get().unwrap().borrow_mut() = Some(FOO.get().unwrap());
}

What this really wants to do is to store a &'thread T inside of Ref<T> to ensure it doesn't satisfy 'static and can't be stored in another global itself. I've been talking with @nikomatsakis on IRC about this and it may be possible, but it would prevent this from landing in the interim. For that reason I'm going to modify the get() api to with() so it has the same semantics as scoped tls.

Member

alexcrichton commented Nov 18, 2014

@brson that's a good point about naming, we could take no preference at all and perhaps move everything in to std::tls as well with DynamicKey and ScopedKey with dynamic_tls! and scoped_tls! macros. I think I'm somewhat biased towards "owned by default" (and so is the repo), but that's also probably because it's all we have today.

The sample implementation's benchmarking numbers are currently all on par with #[thread_local] (numbers are in some of the above comments), but I'll try to call it out specifically how this is trying to mirror the system primitives as much as possible

Today I also discovered an unsoundness with this API as proposed, specifically with the get method on owning TLS keys. The return Ref<T> satisfies the 'static bound, allowing you to store references to other TLS variables in TLS itself, perhaps causing undefined behavior during destructors. Which is to say that TLS variables can store stale references to one another:

#![feature(phase)]

#[phase(plugin, link)]
extern crate tls;

use std::cell::RefCell;

dynamic_tls!(static FOO: Box<int> = box 1)
dynamic_tls!(static BAR: RefCell<Option<tls::dynamic::Ref<Box<int>>>> = RefCell::new(None))

fn main() {
    *BAR.get().unwrap().borrow_mut() = Some(FOO.get().unwrap());
}

What this really wants to do is to store a &'thread T inside of Ref<T> to ensure it doesn't satisfy 'static and can't be stored in another global itself. I've been talking with @nikomatsakis on IRC about this and it may be possible, but it would prevent this from landing in the interim. For that reason I'm going to modify the get() api to with() so it has the same semantics as scoped tls.

bors added a commit to rust-lang/rust that referenced this pull request Nov 18, 2014

auto merge of #18967 : aturon/rust/remove-runtime, r=alexcrichton,ale…
…xcrichton

This PR completes the removal of the runtime system and green-threaded abstractions as part of implementing [RFC 230](rust-lang/rfcs#230).

Specifically:

* It removes the `Runtime` trait and `bookkeeping` module, welding the scheduling infrastructure directly to native threads, and allowing a Rust program to shut down immediately once its main thread has terminated even if others are still running.

* It removes `libgreen` and `libnative` entirely.

* It rewrites `sync::mutex` as a trivial layer on top of native mutexes. Eventually, the two modules will be merged.

* It hides the vast majority of `std::rt`.

This completes the basic task of removing the runtime system (I/O and scheduling) and components that depend on it. 

After this lands, a follow-up PR will pull the `rustrt` crate back into `std`, turn `std::task` into `std::thread` (with API changes to go along with it). Other changes, including new [TLS](rust-lang/rfcs#461) and synchronization are in the RFC or pre-RFC phase.

Closes #17325
Closes #18687

[breaking-change]

r? @alexcrichton

alexcrichton added a commit to alexcrichton/rust that referenced this pull request Nov 18, 2014

std: Add a new top-level tls module
This commit removes the `std::local_data` module in favor of a new `std::tls`
module providing thread local storage. The module provides two variants of TLS:
one which owns its contents and one which is based on scoped references. Each
implementation has pros and cons listed in the documentation.

Both flavors have accessors through a function called `with` which yield a
reference to a closure provided. Both flavors also panic if a reference cannot
be yielded and provide a function to test whether an access would panic or not.
This is an implementation of [RFC 461][rfc] and full details can be found in
that RFC.

This is a breaking change due to the removal of the `std::local_data` module.
All users can migrate to the new tls system like so:

    tls!(static FOO: Rc<RefCell<Option<T>>> = Rc::new(RefCell::new(None)))

The old `local_data` module inherently contained the `Rc<RefCell<Option<T>>>` as
an implementation detail which must now be explicitly stated by users.

[rfc]: rust-lang/rfcs#461
[breaking-change]

@alexcrichton alexcrichton referenced this pull request in rust-lang/rust Nov 18, 2014

Closed

std: Add a new top-level thread_local module #19094

alexcrichton added a commit to alexcrichton/rust that referenced this pull request Nov 18, 2014

std: Add a new top-level tls module
This commit removes the `std::local_data` module in favor of a new `std::tls`
module providing thread local storage. The module provides two variants of TLS:
one which owns its contents and one which is based on scoped references. Each
implementation has pros and cons listed in the documentation.

Both flavors have accessors through a function called `with` which yield a
reference to a closure provided. Both flavors also panic if a reference cannot
be yielded and provide a function to test whether an access would panic or not.
This is an implementation of [RFC 461][rfc] and full details can be found in
that RFC.

This is a breaking change due to the removal of the `std::local_data` module.
All users can migrate to the new tls system like so:

    tls!(static FOO: Rc<RefCell<Option<T>>> = Rc::new(RefCell::new(None)))

The old `local_data` module inherently contained the `Rc<RefCell<Option<T>>>` as
an implementation detail which must now be explicitly stated by users.

[rfc]: rust-lang/rfcs#461
[breaking-change]

bors added a commit to rust-lang/rust that referenced this pull request Nov 19, 2014

auto merge of #18967 : aturon/rust/remove-runtime, r=alexcrichton
This PR completes the removal of the runtime system and green-threaded abstractions as part of implementing [RFC 230](rust-lang/rfcs#230).

Specifically:

* It removes the `Runtime` trait and `bookkeeping` module, welding the scheduling infrastructure directly to native threads, and allowing a Rust program to shut down immediately once its main thread has terminated even if others are still running.

* It removes `libgreen` and `libnative` entirely.

* It rewrites `sync::mutex` as a trivial layer on top of native mutexes. Eventually, the two modules will be merged.

* It hides the vast majority of `std::rt`.

This completes the basic task of removing the runtime system (I/O and scheduling) and components that depend on it. 

After this lands, a follow-up PR will pull the `rustrt` crate back into `std`, turn `std::task` into `std::thread` (with API changes to go along with it). Other changes, including new [TLS](rust-lang/rfcs#461) and synchronization are in the RFC or pre-RFC phase.

Closes #17325
Closes #18687

[breaking-change]

r? @alexcrichton

alexcrichton added a commit to alexcrichton/rust that referenced this pull request Nov 19, 2014

std: Add a new top-level tls module
This commit removes the `std::local_data` module in favor of a new `std::tls`
module providing thread local storage. The module provides two variants of TLS:
one which owns its contents and one which is based on scoped references. Each
implementation has pros and cons listed in the documentation.

Both flavors have accessors through a function called `with` which yield a
reference to a closure provided. Both flavors also panic if a reference cannot
be yielded and provide a function to test whether an access would panic or not.
This is an implementation of [RFC 461][rfc] and full details can be found in
that RFC.

This is a breaking change due to the removal of the `std::local_data` module.
All users can migrate to the new tls system like so:

    tls!(static FOO: Rc<RefCell<Option<T>>> = Rc::new(RefCell::new(None)))

The old `local_data` module inherently contained the `Rc<RefCell<Option<T>>>` as
an implementation detail which must now be explicitly stated by users.

[rfc]: rust-lang/rfcs#461
[breaking-change]

alexcrichton added a commit to alexcrichton/rust that referenced this pull request Nov 19, 2014

std: Add a new top-level tls module
This commit removes the `std::local_data` module in favor of a new `std::tls`
module providing thread local storage. The module provides two variants of TLS:
one which owns its contents and one which is based on scoped references. Each
implementation has pros and cons listed in the documentation.

Both flavors have accessors through a function called `with` which yield a
reference to a closure provided. Both flavors also panic if a reference cannot
be yielded and provide a function to test whether an access would panic or not.
This is an implementation of [RFC 461][rfc] and full details can be found in
that RFC.

This is a breaking change due to the removal of the `std::local_data` module.
All users can migrate to the new tls system like so:

    tls!(static FOO: Rc<RefCell<Option<T>>> = Rc::new(RefCell::new(None)))

The old `local_data` module inherently contained the `Rc<RefCell<Option<T>>>` as
an implementation detail which must now be explicitly stated by users.

[rfc]: rust-lang/rfcs#461
[breaking-change]

bors added a commit to rust-lang/rust that referenced this pull request Nov 19, 2014

auto merge of #18967 : aturon/rust/remove-runtime, r=alexcrichton
This PR completes the removal of the runtime system and green-threaded abstractions as part of implementing [RFC 230](rust-lang/rfcs#230).

Specifically:

* It removes the `Runtime` trait and `bookkeeping` module, welding the scheduling infrastructure directly to native threads, and allowing a Rust program to shut down immediately once its main thread has terminated even if others are still running.

* It removes `libgreen` and `libnative` entirely.

* It rewrites `sync::mutex` as a trivial layer on top of native mutexes. Eventually, the two modules will be merged.

* It hides the vast majority of `std::rt`.

This completes the basic task of removing the runtime system (I/O and scheduling) and components that depend on it. 

After this lands, a follow-up PR will pull the `rustrt` crate back into `std`, turn `std::task` into `std::thread` (with API changes to go along with it). Other changes, including new [TLS](rust-lang/rfcs#461) and synchronization are in the RFC or pre-RFC phase.

Closes #17325
Closes #18687

[breaking-change]

r? @alexcrichton

bors added a commit to rust-lang/rust that referenced this pull request Nov 20, 2014

auto merge of #18967 : aturon/rust/remove-runtime, r=alexcrichton
This PR completes the removal of the runtime system and green-threaded abstractions as part of implementing [RFC 230](rust-lang/rfcs#230).

Specifically:

* It removes the `Runtime` trait and `bookkeeping` module, welding the scheduling infrastructure directly to native threads, and allowing a Rust program to shut down immediately once its main thread has terminated even if others are still running.

* It removes `libgreen` and `libnative` entirely.

* It rewrites `sync::mutex` as a trivial layer on top of native mutexes. Eventually, the two modules will be merged.

* It hides the vast majority of `std::rt`.

This completes the basic task of removing the runtime system (I/O and scheduling) and components that depend on it. 

After this lands, a follow-up PR will pull the `rustrt` crate back into `std`, turn `std::task` into `std::thread` (with API changes to go along with it). Other changes, including new [TLS](rust-lang/rfcs#461) and synchronization are in the RFC or pre-RFC phase.

Closes #17325
Closes #18687

[breaking-change]

r? @alexcrichton

bors added a commit to rust-lang/rust that referenced this pull request Nov 20, 2014

auto merge of #18967 : aturon/rust/remove-runtime, r=alexcrichton
This PR completes the removal of the runtime system and green-threaded abstractions as part of implementing [RFC 230](rust-lang/rfcs#230).

Specifically:

* It removes the `Runtime` trait and `bookkeeping` module, welding the scheduling infrastructure directly to native threads, and allowing a Rust program to shut down immediately once its main thread has terminated even if others are still running.

* It removes `libgreen` and `libnative` entirely.

* It rewrites `sync::mutex` as a trivial layer on top of native mutexes. Eventually, the two modules will be merged.

* It hides the vast majority of `std::rt`.

This completes the basic task of removing the runtime system (I/O and scheduling) and components that depend on it. 

After this lands, a follow-up PR will pull the `rustrt` crate back into `std`, turn `std::task` into `std::thread` (with API changes to go along with it). Other changes, including new [TLS](rust-lang/rfcs#461) and synchronization are in the RFC or pre-RFC phase.

Closes #17325
Closes #18687

[breaking-change]

r? @alexcrichton
@jnicklas

This comment has been minimized.

Show comment
Hide comment
@jnicklas

jnicklas Nov 20, 2014

When I hear TLS I think "Transport Layer Security", not "Thread Local Storage". Wouldn't it make more sense to call this std::thread_local or something?

When I hear TLS I think "Transport Layer Security", not "Thread Local Storage". Wouldn't it make more sense to call this std::thread_local or something?

@reem

This comment has been minimized.

Show comment
Hide comment
@reem

reem Nov 20, 2014

@alexcrichton Could &'thread T be emulated with:

struct ThreadRef<T> {
   marker: NoSend,
   data: &'static T
}

and the appropriate Deref impls?

reem commented Nov 20, 2014

@alexcrichton Could &'thread T be emulated with:

struct ThreadRef<T> {
   marker: NoSend,
   data: &'static T
}

and the appropriate Deref impls?

@alexcrichton

This comment has been minimized.

Show comment
Hide comment
@alexcrichton

alexcrichton Nov 20, 2014

Member

@reem I thought so! (and I was really hoping so). The catch is that the 'static > 'thread (note >, not >=). It should be the case that &'thread T does not satisfy 'static, but the struct you've shown sadly does. The details are in this comment above.

Member

alexcrichton commented Nov 20, 2014

@reem I thought so! (and I was really hoping so). The catch is that the 'static > 'thread (note >, not >=). It should be the case that &'thread T does not satisfy 'static, but the struct you've shown sadly does. The details are in this comment above.

bors added a commit to rust-lang/rust that referenced this pull request Nov 21, 2014

auto merge of #18967 : aturon/rust/remove-runtime, r=alexcrichton
This PR completes the removal of the runtime system and green-threaded abstractions as part of implementing [RFC 230](rust-lang/rfcs#230).

Specifically:

* It removes the `Runtime` trait, welding the scheduling infrastructure directly to native threads.

* It removes `libgreen` and `libnative` entirely.

* It rewrites `sync::mutex` as a trivial layer on top of native mutexes. Eventually, the two modules will be merged.

* It hides the vast majority of `std::rt`.

This completes the basic task of removing the runtime system (I/O and scheduling) and components that depend on it. 

After this lands, a follow-up PR will pull the `rustrt` crate back into `std`, turn `std::task` into `std::thread` (with API changes to go along with it), and completely cut out the remaining startup/teardown sequence. Other changes, including new [TLS](rust-lang/rfcs#461) and synchronization are in the RFC or pre-RFC phase.

Closes #17325
Closes #18687

[breaking-change]

r? @alexcrichton

@aturon aturon referenced this pull request in rust-lang/rust Nov 21, 2014

Closed

Overhaul thread local storage #19175

@aturon aturon merged commit 4fc3b6d into rust-lang:master Nov 21, 2014

@aturon

This comment has been minimized.

Show comment
Hide comment
@aturon

aturon Nov 21, 2014

Member

RFC accepted.
Discussion
Tracking

Member

aturon commented Nov 21, 2014

RFC accepted.
Discussion
Tracking

@reem

This comment has been minimized.

Show comment
Hide comment
@reem

reem Nov 21, 2014

@alexcrichton it looks like the simplest solution would just be to add a NoStatic marker - that seems like it may be generally useful to unsafe code.

reem commented Nov 21, 2014

@alexcrichton it looks like the simplest solution would just be to add a NoStatic marker - that seems like it may be generally useful to unsafe code.

alexcrichton added a commit to alexcrichton/rust that referenced this pull request Nov 21, 2014

std: Add a new top-level thread_local module
This commit removes the `std::local_data` module in favor of a new
`std::thread_local` module providing thread local storage. The module provides
two variants of TLS: one which owns its contents and one which is based on
scoped references. Each implementation has pros and cons listed in the
documentation.

Both flavors have accessors through a function called `with` which yield a
reference to a closure provided. Both flavors also panic if a reference cannot
be yielded and provide a function to test whether an access would panic or not.
This is an implementation of [RFC 461][rfc] and full details can be found in
that RFC.

This is a breaking change due to the removal of the `std::local_data` module.
All users can migrate to the new thread local system like so:

    thread_local!(static FOO: Rc<RefCell<Option<T>>> = Rc::new(RefCell::new(None)))

The old `local_data` module inherently contained the `Rc<RefCell<Option<T>>>` as
an implementation detail which must now be explicitly stated by users.

[rfc]: rust-lang/rfcs#461
[breaking-change]

alexcrichton added a commit to alexcrichton/rust that referenced this pull request Nov 21, 2014

std: Add a new top-level thread_local module
This commit removes the `std::local_data` module in favor of a new
`std::thread_local` module providing thread local storage. The module provides
two variants of TLS: one which owns its contents and one which is based on
scoped references. Each implementation has pros and cons listed in the
documentation.

Both flavors have accessors through a function called `with` which yield a
reference to a closure provided. Both flavors also panic if a reference cannot
be yielded and provide a function to test whether an access would panic or not.
This is an implementation of [RFC 461][rfc] and full details can be found in
that RFC.

This is a breaking change due to the removal of the `std::local_data` module.
All users can migrate to the new thread local system like so:

    thread_local!(static FOO: Rc<RefCell<Option<T>>> = Rc::new(RefCell::new(None)))

The old `local_data` module inherently contained the `Rc<RefCell<Option<T>>>` as
an implementation detail which must now be explicitly stated by users.

[rfc]: rust-lang/rfcs#461
[breaking-change]

alexcrichton added a commit to alexcrichton/rust that referenced this pull request Nov 21, 2014

std: Add a new top-level thread_local module
This commit removes the `std::local_data` module in favor of a new
`std::thread_local` module providing thread local storage. The module provides
two variants of TLS: one which owns its contents and one which is based on
scoped references. Each implementation has pros and cons listed in the
documentation.

Both flavors have accessors through a function called `with` which yield a
reference to a closure provided. Both flavors also panic if a reference cannot
be yielded and provide a function to test whether an access would panic or not.
This is an implementation of [RFC 461][rfc] and full details can be found in
that RFC.

This is a breaking change due to the removal of the `std::local_data` module.
All users can migrate to the new thread local system like so:

    thread_local!(static FOO: Rc<RefCell<Option<T>>> = Rc::new(RefCell::new(None)))

The old `local_data` module inherently contained the `Rc<RefCell<Option<T>>>` as
an implementation detail which must now be explicitly stated by users.

[rfc]: rust-lang/rfcs#461
[breaking-change]

bors added a commit to rust-lang/rust that referenced this pull request Nov 21, 2014

auto merge of #19094 : alexcrichton/rust/rm-std-local-data, r=aturon
This commit removes the `std::local_data` module in favor of a new `std::thread_local`
module providing thread local storage. The module provides two variants of TLS:
one which owns its contents and one which is based on scoped references. Each
implementation has pros and cons listed in the documentation.

Both flavors have accessors through a function called `with` which yield a
reference to a closure provided. Both flavors also panic if a reference cannot
be yielded and provide a function to test whether an access would panic or not.
This is an implementation of [RFC 461][rfc] and full details can be found in
that RFC.

This is a breaking change due to the removal of the `std::local_data` module.
All users can migrate to the new tls system like so:

    thread_local!(static FOO: Rc<RefCell<Option<T>>> = Rc::new(RefCell::new(None)))

The old `local_data` module inherently contained the `Rc<RefCell<Option<T>>>` as
an implementation detail which must now be explicitly stated by users.

[rfc]: rust-lang/rfcs#461
[breaking-change]

alexcrichton added a commit to alexcrichton/rust that referenced this pull request Nov 21, 2014

std: Add a new top-level thread_local module
This commit removes the `std::local_data` module in favor of a new
`std::thread_local` module providing thread local storage. The module provides
two variants of TLS: one which owns its contents and one which is based on
scoped references. Each implementation has pros and cons listed in the
documentation.

Both flavors have accessors through a function called `with` which yield a
reference to a closure provided. Both flavors also panic if a reference cannot
be yielded and provide a function to test whether an access would panic or not.
This is an implementation of [RFC 461][rfc] and full details can be found in
that RFC.

This is a breaking change due to the removal of the `std::local_data` module.
All users can migrate to the new thread local system like so:

    thread_local!(static FOO: Rc<RefCell<Option<T>>> = Rc::new(RefCell::new(None)))

The old `local_data` module inherently contained the `Rc<RefCell<Option<T>>>` as
an implementation detail which must now be explicitly stated by users.

[rfc]: rust-lang/rfcs#461
[breaking-change]

alexcrichton added a commit to alexcrichton/rust that referenced this pull request Nov 22, 2014

rollup merge of #19094: alexcrichton/rm-std-local-data
This commit removes the `std::local_data` module in favor of a new `std::thread_local`
module providing thread local storage. The module provides two variants of TLS:
one which owns its contents and one which is based on scoped references. Each
implementation has pros and cons listed in the documentation.

Both flavors have accessors through a function called `with` which yield a
reference to a closure provided. Both flavors also panic if a reference cannot
be yielded and provide a function to test whether an access would panic or not.
This is an implementation of [RFC 461][rfc] and full details can be found in
that RFC.

This is a breaking change due to the removal of the `std::local_data` module.
All users can migrate to the new tls system like so:

    thread_local!(static FOO: Rc<RefCell<Option<T>>> = Rc::new(RefCell::new(None)))

The old `local_data` module inherently contained the `Rc<RefCell<Option<T>>>` as
an implementation detail which must now be explicitly stated by users.

[rfc]: rust-lang/rfcs#461
[breaking-change]

bors added a commit to rust-lang/rust that referenced this pull request Nov 22, 2014

auto merge of #19094 : alexcrichton/rust/rm-std-local-data, r=aturon
This commit removes the `std::local_data` module in favor of a new `std::thread_local`
module providing thread local storage. The module provides two variants of TLS:
one which owns its contents and one which is based on scoped references. Each
implementation has pros and cons listed in the documentation.

Both flavors have accessors through a function called `with` which yield a
reference to a closure provided. Both flavors also panic if a reference cannot
be yielded and provide a function to test whether an access would panic or not.
This is an implementation of [RFC 461][rfc] and full details can be found in
that RFC.

This is a breaking change due to the removal of the `std::local_data` module.
All users can migrate to the new tls system like so:

    thread_local!(static FOO: Rc<RefCell<Option<T>>> = Rc::new(RefCell::new(None)))

The old `local_data` module inherently contained the `Rc<RefCell<Option<T>>>` as
an implementation detail which must now be explicitly stated by users.

[rfc]: rust-lang/rfcs#461
[breaking-change]

alexcrichton added a commit to alexcrichton/rust that referenced this pull request Nov 22, 2014

rollup merge of #19094: alexcrichton/rm-std-local-data
This commit removes the `std::local_data` module in favor of a new `std::thread_local`
module providing thread local storage. The module provides two variants of TLS:
one which owns its contents and one which is based on scoped references. Each
implementation has pros and cons listed in the documentation.

Both flavors have accessors through a function called `with` which yield a
reference to a closure provided. Both flavors also panic if a reference cannot
be yielded and provide a function to test whether an access would panic or not.
This is an implementation of [RFC 461][rfc] and full details can be found in
that RFC.

This is a breaking change due to the removal of the `std::local_data` module.
All users can migrate to the new tls system like so:

    thread_local!(static FOO: Rc<RefCell<Option<T>>> = Rc::new(RefCell::new(None)))

The old `local_data` module inherently contained the `Rc<RefCell<Option<T>>>` as
an implementation detail which must now be explicitly stated by users.

[rfc]: rust-lang/rfcs#461
[breaking-change]

alexcrichton added a commit to alexcrichton/rust that referenced this pull request Nov 22, 2014

std: Add a new top-level thread_local module
This commit removes the `std::local_data` module in favor of a new
`std::thread_local` module providing thread local storage. The module provides
two variants of TLS: one which owns its contents and one which is based on
scoped references. Each implementation has pros and cons listed in the
documentation.

Both flavors have accessors through a function called `with` which yield a
reference to a closure provided. Both flavors also panic if a reference cannot
be yielded and provide a function to test whether an access would panic or not.
This is an implementation of [RFC 461][rfc] and full details can be found in
that RFC.

This is a breaking change due to the removal of the `std::local_data` module.
All users can migrate to the new thread local system like so:

    thread_local!(static FOO: Rc<RefCell<Option<T>>> = Rc::new(RefCell::new(None)))

The old `local_data` module inherently contained the `Rc<RefCell<Option<T>>>` as
an implementation detail which must now be explicitly stated by users.

[rfc]: rust-lang/rfcs#461
[breaking-change]

bors added a commit to rust-lang/rust that referenced this pull request Nov 22, 2014

auto merge of #19094 : alexcrichton/rust/rm-std-local-data, r=aturon
This commit removes the `std::local_data` module in favor of a new `std::thread_local`
module providing thread local storage. The module provides two variants of TLS:
one which owns its contents and one which is based on scoped references. Each
implementation has pros and cons listed in the documentation.

Both flavors have accessors through a function called `with` which yield a
reference to a closure provided. Both flavors also panic if a reference cannot
be yielded and provide a function to test whether an access would panic or not.
This is an implementation of [RFC 461][rfc] and full details can be found in
that RFC.

This is a breaking change due to the removal of the `std::local_data` module.
All users can migrate to the new tls system like so:

    thread_local!(static FOO: Rc<RefCell<Option<T>>> = Rc::new(RefCell::new(None)))

The old `local_data` module inherently contained the `Rc<RefCell<Option<T>>>` as
an implementation detail which must now be explicitly stated by users.

[rfc]: rust-lang/rfcs#461
[breaking-change]

alexcrichton added a commit to alexcrichton/rust that referenced this pull request Nov 24, 2014

std: Add a new top-level thread_local module
This commit removes the `std::local_data` module in favor of a new
`std::thread_local` module providing thread local storage. The module provides
two variants of TLS: one which owns its contents and one which is based on
scoped references. Each implementation has pros and cons listed in the
documentation.

Both flavors have accessors through a function called `with` which yield a
reference to a closure provided. Both flavors also panic if a reference cannot
be yielded and provide a function to test whether an access would panic or not.
This is an implementation of [RFC 461][rfc] and full details can be found in
that RFC.

This is a breaking change due to the removal of the `std::local_data` module.
All users can migrate to the new thread local system like so:

    thread_local!(static FOO: Rc<RefCell<Option<T>>> = Rc::new(RefCell::new(None)))

The old `local_data` module inherently contained the `Rc<RefCell<Option<T>>>` as
an implementation detail which must now be explicitly stated by users.

[rfc]: rust-lang/rfcs#461
[breaking-change]

bors added a commit to rust-lang/rust that referenced this pull request Nov 24, 2014

auto merge of #19094 : alexcrichton/rust/rm-std-local-data, r=aturon
This commit removes the `std::local_data` module in favor of a new `std::thread_local`
module providing thread local storage. The module provides two variants of TLS:
one which owns its contents and one which is based on scoped references. Each
implementation has pros and cons listed in the documentation.

Both flavors have accessors through a function called `with` which yield a
reference to a closure provided. Both flavors also panic if a reference cannot
be yielded and provide a function to test whether an access would panic or not.
This is an implementation of [RFC 461][rfc] and full details can be found in
that RFC.

This is a breaking change due to the removal of the `std::local_data` module.
All users can migrate to the new tls system like so:

    thread_local!(static FOO: Rc<RefCell<Option<T>>> = Rc::new(RefCell::new(None)))

The old `local_data` module inherently contained the `Rc<RefCell<Option<T>>>` as
an implementation detail which must now be explicitly stated by users.

[rfc]: rust-lang/rfcs#461
[breaking-change]

alexcrichton added a commit to alexcrichton/rust that referenced this pull request Nov 24, 2014

std: Add a new top-level thread_local module
This commit removes the `std::local_data` module in favor of a new
`std::thread_local` module providing thread local storage. The module provides
two variants of TLS: one which owns its contents and one which is based on
scoped references. Each implementation has pros and cons listed in the
documentation.

Both flavors have accessors through a function called `with` which yield a
reference to a closure provided. Both flavors also panic if a reference cannot
be yielded and provide a function to test whether an access would panic or not.
This is an implementation of [RFC 461][rfc] and full details can be found in
that RFC.

This is a breaking change due to the removal of the `std::local_data` module.
All users can migrate to the new thread local system like so:

    thread_local!(static FOO: Rc<RefCell<Option<T>>> = Rc::new(RefCell::new(None)))

The old `local_data` module inherently contained the `Rc<RefCell<Option<T>>>` as
an implementation detail which must now be explicitly stated by users.

[rfc]: rust-lang/rfcs#461
[breaking-change]

bors added a commit to rust-lang/rust that referenced this pull request Nov 24, 2014

auto merge of #19094 : alexcrichton/rust/rm-std-local-data, r=aturon
This commit removes the `std::local_data` module in favor of a new `std::thread_local`
module providing thread local storage. The module provides two variants of TLS:
one which owns its contents and one which is based on scoped references. Each
implementation has pros and cons listed in the documentation.

Both flavors have accessors through a function called `with` which yield a
reference to a closure provided. Both flavors also panic if a reference cannot
be yielded and provide a function to test whether an access would panic or not.
This is an implementation of [RFC 461][rfc] and full details can be found in
that RFC.

This is a breaking change due to the removal of the `std::local_data` module.
All users can migrate to the new tls system like so:

    thread_local!(static FOO: Rc<RefCell<Option<T>>> = Rc::new(RefCell::new(None)))

The old `local_data` module inherently contained the `Rc<RefCell<Option<T>>>` as
an implementation detail which must now be explicitly stated by users.

[rfc]: rust-lang/rfcs#461
[breaking-change]

alexcrichton added a commit to alexcrichton/rust that referenced this pull request Nov 24, 2014

std: Add a new top-level thread_local module
This commit removes the `std::local_data` module in favor of a new
`std::thread_local` module providing thread local storage. The module provides
two variants of TLS: one which owns its contents and one which is based on
scoped references. Each implementation has pros and cons listed in the
documentation.

Both flavors have accessors through a function called `with` which yield a
reference to a closure provided. Both flavors also panic if a reference cannot
be yielded and provide a function to test whether an access would panic or not.
This is an implementation of [RFC 461][rfc] and full details can be found in
that RFC.

This is a breaking change due to the removal of the `std::local_data` module.
All users can migrate to the new thread local system like so:

    thread_local!(static FOO: Rc<RefCell<Option<T>>> = Rc::new(RefCell::new(None)))

The old `local_data` module inherently contained the `Rc<RefCell<Option<T>>>` as
an implementation detail which must now be explicitly stated by users.

[rfc]: rust-lang/rfcs#461
[breaking-change]

bors added a commit to rust-lang/rust that referenced this pull request Nov 24, 2014

auto merge of #19094 : alexcrichton/rust/rm-std-local-data, r=aturon
This commit removes the `std::local_data` module in favor of a new `std::thread_local`
module providing thread local storage. The module provides two variants of TLS:
one which owns its contents and one which is based on scoped references. Each
implementation has pros and cons listed in the documentation.

Both flavors have accessors through a function called `with` which yield a
reference to a closure provided. Both flavors also panic if a reference cannot
be yielded and provide a function to test whether an access would panic or not.
This is an implementation of [RFC 461][rfc] and full details can be found in
that RFC.

This is a breaking change due to the removal of the `std::local_data` module.
All users can migrate to the new tls system like so:

    thread_local!(static FOO: Rc<RefCell<Option<T>>> = Rc::new(RefCell::new(None)))

The old `local_data` module inherently contained the `Rc<RefCell<Option<T>>>` as
an implementation detail which must now be explicitly stated by users.

[rfc]: rust-lang/rfcs#461
[breaking-change]

cuviper pushed a commit to cuviper/rayon that referenced this pull request Mar 28, 2017

std: Add a new top-level thread_local module
This commit removes the `std::local_data` module in favor of a new
`std::thread_local` module providing thread local storage. The module provides
two variants of TLS: one which owns its contents and one which is based on
scoped references. Each implementation has pros and cons listed in the
documentation.

Both flavors have accessors through a function called `with` which yield a
reference to a closure provided. Both flavors also panic if a reference cannot
be yielded and provide a function to test whether an access would panic or not.
This is an implementation of [RFC 461][rfc] and full details can be found in
that RFC.

This is a breaking change due to the removal of the `std::local_data` module.
All users can migrate to the new thread local system like so:

    thread_local!(static FOO: Rc<RefCell<Option<T>>> = Rc::new(RefCell::new(None)))

The old `local_data` module inherently contained the `Rc<RefCell<Option<T>>>` as
an implementation detail which must now be explicitly stated by users.

[rfc]: rust-lang/rfcs#461
[breaking-change]

pitdicker pushed a commit to pitdicker/rand_clean_history that referenced this pull request Oct 8, 2017

std: Add a new top-level thread_local module
This commit removes the `std::local_data` module in favor of a new
`std::thread_local` module providing thread local storage. The module provides
two variants of TLS: one which owns its contents and one which is based on
scoped references. Each implementation has pros and cons listed in the
documentation.

Both flavors have accessors through a function called `with` which yield a
reference to a closure provided. Both flavors also panic if a reference cannot
be yielded and provide a function to test whether an access would panic or not.
This is an implementation of [RFC 461][rfc] and full details can be found in
that RFC.

This is a breaking change due to the removal of the `std::local_data` module.
All users can migrate to the new thread local system like so:

    thread_local!(static FOO: Rc<RefCell<Option<T>>> = Rc::new(RefCell::new(None)))

The old `local_data` module inherently contained the `Rc<RefCell<Option<T>>>` as
an implementation detail which must now be explicitly stated by users.

[rfc]: rust-lang/rfcs#461
[breaking-change]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment