Change:
There is an  in the scoped TLS module, which allows for sharing of data that wasn't meant to be shared. An unqualified should only_ be used when provides some sort of threadsafe locking mechanism. and do not. This lets us, for example, clone an or between threads, in safe code: Here, we have clones from different threads being interleaved, which can cause unsafety if there's a race on the refcount. We could also cause unsafety with a , or any other non-Sync type. basically lets us share across threads even if . I put the sleeps in there because somehow without them the routines are run serially. Note that this is not a problem with the API. I'm using here because to exploit one needs to be able to share across threads, and has an additional bound which doesn't satisfy. Any usable API will probably have to make the assumption that . Nor is this a problem with , since it can be done with , too. Solution: Make it ... I think. I'm not familiar with this API, and I'm not sure why it's even in the first place.
cc
cc This seems concerning: this is designed to be thread-local, so that e.g. storing a works (which would be safe, if it were guaranteed to stay on one thread). However currently requires unconditionally, hence this overly-accepting impl.
Qualifying the impl with breaks scoped TLS for (see the associated PR); because becomes no longer and thus can't be stuffed inside a static.
I believe the only way statics can be safe is by requiring the absence of Sync. That may sound odd, but consider the following: - sending thread-local references across threads is memory unsafe even with no interior mutability a static with no interior mutability is almost entirely useless, certainly not a common case non- containers offering interior mutabilities are cheaper and preferred to ones, where they can be used The main issue is actually within the libstd TLS implementation: is a wrapper for getters so it's unaffected by it, but may live in a static or a global one, depending on whether the platform natively supports TLS or not. -ing a across threads is actually safe on the architectures which do not have native TLS support. However, allowing it on some architectures and not on others is undesirable. Should use an accessor model like ? Then the actual borrow of a static is an implementation detail and happens on the thread where the key is used, not where it was itself borrowed.
"I believe the only way #[threadlocal] statics can be safe is by requiring the absence of ." This is wrong, I think. For example, one might use thread local data for intrusive linked lists of hazard pointers. They would be thread local but might still be (one can guarantee existence with scoped threads or in certain circumstances where threads are bound to cores). That said you can obviously come up with other representations. In any case, I've mostly come around to believing that we need a separate lifetime (not just for this, but for things like ensuring that can't catch an error in a thread that changes thread local data, violating exception safety).
Based on what wrote, it seems like the TLS interface requires a that only allows non- data and is still accessible like one. One idea I had of how to enable this in todays Rust is to make generate a unit like struct instead of a . That struct would be and have a impl: According to this could also help with , but I'm not familiar enough with the TLS API in general to really reason about this.
I was expecting the impl to target an intermediary type, not the contents themselves. and have their own APIs to deal with. You would still be about to use the TLS API provided by libstd with thread-safe data, it's just the low-level which would have the requirement. Ideas of thread-local lifetimes predate the removal of from , and I'm not sure how relevant they are anymore. I'm also conflating thread-safe data access and thread lifetime... but how long does a thread live? What would unify with? Actually, there's an... easy solution for this. Somehow we were blinded by language semantics, but their details are fluid. It is trivial to make borrows of behave like borrows of an arbitrary rvalue, allowing the resulting reference to be passed down, but not returned. That would ensure that the the thread is just as alive for such borrows as it is for stack ones. would still require a like has, because it may not be when native TLS is supported. But both their APIs are compatible with such a change.
Wouldn't that limit the usability of thread locals in some cases, though? e.g., wouldn't that make it difficult to have a thread local point to another thread local? Maybe I just have to think about it. (BTW, I'm aware we're just talking about the low-level API :)).
in a , borrows are , I didn't even think of that. First off, a global static shouldn't be allowed to borrow a static. And in general, having borrows in a is going to be difficult to support, except for non- data, because they would be right now. We could disallow borrows or we could give some kind of scope to those borrows. Which makes me think, as a compromise between a lifetime and , there could be a pseudo-(rvalue)-scope. Combined with the ability to leave out in globals' type (or not having a type at all), the scope should bubble up into users of those statics. They would behave as if they were borrowed before everything else in the function where they're used, but still not (and they can't be returned from functions).
There's a few intertwined issues here at play, so I just want to quickly summarize the way I see things: This does not affect as it which has an , which while sound should be fixed, allowing removal of the block for . A borrow of a static should not yield something with the lifetime, as that is clearly not sound. I'm not sure I follow why we might require the types in a are not , I haven't quite followed that. Due the cross-platform requirements of std, however, I believe the best solution here is to use the same strategy as with an indirect accessor to ensure the semantics are the same across platforms that have and those that don't (where a normal is used).
triage: P-medium This is an unstable API so it's not super-high priority to fix, but we should defnitely fix!
requiring that a is not is an easy way to ensure that a borrow of it, while , cannot escape the current thread. I've changed my mind and prefer an rvalue borrow semantic for , but it still is the case that and use non- statics (ignoring the workarounds for ). If was supported everywhere, having rvalue borrow semantics wouldn't hurt the std TLS API (aside from stability issues with ), as the access patterns are already through anonymous lifetimes (references passed to closures). Maybe a combined strategy, where borrows of statics are iff the data is not (and rvalue-like otherwise), might work better and would require fewer changes to the std TLS implementation.
Right, but returning a borrow with a lifetime for a global is unsafe for other reasons (the lifetime of the thread is not ).
how can it be abused without sending it to a different thread?
The actual values themselves do not have a lifetime, so it can be a source of unsafety to advertise otherwise. For example you could stick a into a TLS value with a destructor, but when the destructor runs that pointer could be invalid.

Discussion:
pub use self::scoped_tls::ScopedKey; #[doc(hidden)] pub use self::local::__KeyInner as __LocalKeyInner; <ins> #[doc(hidden)] pub use self::scoped_tls::__KeyInner as __ScopedKeyInner; </ins> //////////////////////////////////////////////////////////////////////////////// // Builder

use prelude::v1::*; <ins> #[doc(hidden)] pub use self::imp::KeyInner as __KeyInner; </ins> /// Type representing a thread local storage key corresponding to a reference /// to the type parameter `T`. ///

#[unstable(feature = "scoped_tls", reason = "scoped TLS has yet to have wide enough use to fully consider  stabilizing its interface")] <del> pub struct ScopedKey<T> { inner: imp::KeyInner<T> } </del> <ins> pub struct ScopedKey<T> { inner: fn() -> &'static imp::KeyInner<T> } </ins> /// Declare a new scoped thread local storage key. ///

/// information. #[macro_export] #[allow_internal_unstable] <del> #[cfg(not(no_elf_tls))] </del> macro_rules! scoped_thread_local { (static $name:ident: $t:ty) => ( <del> #[cfg_attr(not(any(windows, target_os = "android", target_os = "ios", target_os = "openbsd", target_arch = "aarch64")), thread_local)] </del> static $name: ::std::thread::ScopedKey<$t> = <del> ::std::thread::ScopedKey::new(); </del> <ins> __scoped_thread_local_inner!($t); </ins> ); (pub static $name:ident: $t:ty) => ( <del> #[cfg_attr(not(any(windows, target_os = "android", target_os = "ios", target_os = "openbsd", target_arch = "aarch64")), thread_local)] </del> pub static $name: ::std::thread::ScopedKey<$t> = <del> ::std::thread::ScopedKey::new(); </del> <ins> __scoped_thread_local_inner!($t); </ins> ); } <ins> #[doc(hidden)] #[unstable(feature = "thread_local_internals", reason = "should not be necessary")] </ins> #[macro_export] #[allow_internal_unstable] #[cfg(no_elf_tls)] <del> macro_rules! scoped_thread_local { (static $name:ident: $t:ty) => ( static $name: ::std::thread::ScopedKey<$t> = ::std::thread::ScopedKey::new(); ); (pub static $name:ident: $t:ty) => ( pub static $name: ::std::thread::ScopedKey<$t> = ::std::thread::ScopedKey::new(); ); </del> <ins> macro_rules! __scoped_thread_local_inner { ($t:ty) => {{ static _KEY: ::std::thread::__ScopedKeyInner<$t> = ::std::thread::__ScopedKeyInner::new(); fn _getit() -> &'static ::std::thread::__ScopedKeyInner<$t> { &_KEY } ::std::thread::ScopedKey::new(_getit) }} } #[doc(hidden)] #[unstable(feature = "thread_local_internals", reason = "should not be necessary")] #[macro_export] #[allow_internal_unstable] #[cfg(not(no_elf_tls))] macro_rules! __scoped_thread_local_inner { ($t:ty) => {{ #[cfg_attr(not(any(windows, target_os = "android", target_os = "ios", target_os = "openbsd", target_arch = "aarch64")), thread_local)] static _KEY: ::std::thread::__ScopedKeyInner<$t> = ::std::thread::__ScopedKeyInner::new(); fn _getit() -> &'static ::std::thread::__ScopedKeyInner<$t> { &_KEY } ::std::thread::ScopedKey::new(_getit) }} </ins> } #[unstable(feature = "scoped_tls",

stabilizing its interface")] impl<T> ScopedKey<T> { #[doc(hidden)] <del> pub const fn new() -> ScopedKey<T> { ScopedKey { inner: imp::KeyInner::new() } </del> <ins> pub const fn new(inner: fn() -> &'static imp::KeyInner<T>) -> ScopedKey<T> { ScopedKey { inner: inner } </ins> } /// Inserts a value into this scoped thread local storage slot for a

} } <ins> let inner = (self.inner)(); </ins> let prev = unsafe { <del> let prev = self.inner.get(); self.inner.set(t as *const T as *mut T); </del> <ins> let prev = inner.get(); inner.set(t as *const T as *mut T); </ins> prev }; <del> let _reset = Reset { key: &self.inner, val: prev }; </del> <ins> let _reset = Reset { key: inner, val: prev }; </ins> cb() }

F: FnOnce(&T) -> R { unsafe { <del> let ptr = self.inner.get(); </del> <ins> let ptr = (self.inner)().get(); </ins> assert!(!ptr.is_null(), "cannot access a scoped thread local  variable without calling `set` first"); cb(&*ptr)

/// Test whether this TLS key has been `set` for the current thread. pub fn is_set(&'static self) -> bool { <del> unsafe { !self.inner.get().is_null() } </del> <ins> unsafe { !(self.inner)().get().is_null() } </ins> } }

target_os = "openbsd", target_arch = "aarch64", no_elf_tls)))] <ins> #[doc(hidden)] </ins> mod imp { use std::cell::Cell;

target_os = "openbsd", target_arch = "aarch64", no_elf_tls))] <ins> #[doc(hidden)] </ins> mod imp { use prelude::v1::*;