std: make TLS accessors closures that return pointers
This commit is contained in:
parent
9e297bf54d
commit
1052d2931c
@ -21,43 +21,35 @@ pub const fn new(val: T) -> Storage<T> {
|
||||
Storage { state: Cell::new(State::Initial), val: UnsafeCell::new(val) }
|
||||
}
|
||||
|
||||
/// Get a reference to the TLS value. If the TLS variable has been destroyed,
|
||||
/// `None` is returned.
|
||||
/// Get a pointer to the TLS value. If the TLS variable has been destroyed,
|
||||
/// a null pointer is returned.
|
||||
///
|
||||
/// The resulting pointer may not be used after thread destruction has
|
||||
/// occurred.
|
||||
///
|
||||
/// # Safety
|
||||
/// * The `self` reference must remain valid until the TLS destructor has been
|
||||
/// run.
|
||||
/// * The returned reference may only be used until thread destruction occurs
|
||||
/// and may not be used after reentrant initialization has occurred.
|
||||
///
|
||||
// FIXME(#110897): return NonNull instead of lying about the lifetime.
|
||||
/// The `self` reference must remain valid until the TLS destructor is run.
|
||||
#[inline]
|
||||
pub unsafe fn get(&self) -> Option<&'static T> {
|
||||
pub unsafe fn get(&self) -> *const T {
|
||||
match self.state.get() {
|
||||
// SAFETY: as the state is not `Destroyed`, the value cannot have
|
||||
// been destroyed yet. The reference fulfills the terms outlined
|
||||
// above.
|
||||
State::Alive => unsafe { Some(&*self.val.get()) },
|
||||
State::Destroyed => None,
|
||||
State::Alive => self.val.get(),
|
||||
State::Destroyed => ptr::null(),
|
||||
State::Initial => unsafe { self.initialize() },
|
||||
}
|
||||
}
|
||||
|
||||
#[cold]
|
||||
unsafe fn initialize(&self) -> Option<&'static T> {
|
||||
unsafe fn initialize(&self) -> *const T {
|
||||
// Register the destructor
|
||||
|
||||
// SAFETY:
|
||||
// * the destructor will be called at thread destruction.
|
||||
// * the caller guarantees that `self` will be valid until that time.
|
||||
// The caller guarantees that `self` will be valid until thread destruction.
|
||||
unsafe {
|
||||
register_dtor(ptr::from_ref(self).cast_mut().cast(), destroy::<T>);
|
||||
}
|
||||
|
||||
self.state.set(State::Alive);
|
||||
// SAFETY: as the state is not `Destroyed`, the value cannot have
|
||||
// been destroyed yet. The reference fulfills the terms outlined
|
||||
// above.
|
||||
unsafe { Some(&*self.val.get()) }
|
||||
self.val.get()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,49 +39,31 @@ pub const fn new() -> Storage<T, D> {
|
||||
Storage { state: UnsafeCell::new(State::Initial) }
|
||||
}
|
||||
|
||||
/// Get a reference to the TLS value, potentially initializing it with the
|
||||
/// provided parameters. If the TLS variable has been destroyed, `None` is
|
||||
/// returned.
|
||||
/// Get a pointer to the TLS value, potentially initializing it with the
|
||||
/// provided parameters. If the TLS variable has been destroyed, a null
|
||||
/// pointer is returned.
|
||||
///
|
||||
/// The resulting pointer may not be used after reentrant inialialization
|
||||
/// or thread destruction has occurred.
|
||||
///
|
||||
/// # Safety
|
||||
/// * The `self` reference must remain valid until the TLS destructor is run,
|
||||
/// at which point the returned reference is invalidated.
|
||||
/// * The returned reference may only be used until thread destruction occurs
|
||||
/// and may not be used after reentrant initialization has occurred.
|
||||
///
|
||||
// FIXME(#110897): return NonNull instead of lying about the lifetime.
|
||||
/// The `self` reference must remain valid until the TLS destructor is run.
|
||||
#[inline]
|
||||
pub unsafe fn get_or_init(
|
||||
&self,
|
||||
i: Option<&mut Option<T>>,
|
||||
f: impl FnOnce() -> T,
|
||||
) -> Option<&'static T> {
|
||||
// SAFETY:
|
||||
// No mutable reference to the inner value exists outside the calls to
|
||||
// `replace`. The lifetime of the returned reference fulfills the terms
|
||||
// outlined above.
|
||||
pub unsafe fn get_or_init(&self, i: Option<&mut Option<T>>, f: impl FnOnce() -> T) -> *const T {
|
||||
let state = unsafe { &*self.state.get() };
|
||||
match state {
|
||||
State::Alive(v) => Some(v),
|
||||
State::Destroyed(_) => None,
|
||||
State::Alive(v) => v,
|
||||
State::Destroyed(_) => ptr::null(),
|
||||
State::Initial => unsafe { self.initialize(i, f) },
|
||||
}
|
||||
}
|
||||
|
||||
#[cold]
|
||||
unsafe fn initialize(
|
||||
&self,
|
||||
i: Option<&mut Option<T>>,
|
||||
f: impl FnOnce() -> T,
|
||||
) -> Option<&'static T> {
|
||||
unsafe fn initialize(&self, i: Option<&mut Option<T>>, f: impl FnOnce() -> T) -> *const T {
|
||||
// Perform initialization
|
||||
|
||||
let v = i.and_then(Option::take).unwrap_or_else(f);
|
||||
|
||||
// SAFETY:
|
||||
// If references to the inner value exist, they were created in `f`
|
||||
// and are invalidated here. The caller promises to never use them
|
||||
// after this.
|
||||
let old = unsafe { self.state.get().replace(State::Alive(v)) };
|
||||
match old {
|
||||
// If the variable is not being recursively initialized, register
|
||||
@ -92,12 +74,10 @@ unsafe fn initialize(
|
||||
val => drop(val),
|
||||
}
|
||||
|
||||
// SAFETY:
|
||||
// Initialization was completed and the state was set to `Alive`, so the
|
||||
// reference fulfills the terms outlined above.
|
||||
// SAFETY: the state was just set to `Alive`
|
||||
unsafe {
|
||||
let State::Alive(v) = &*self.state.get() else { unreachable_unchecked() };
|
||||
Some(v)
|
||||
v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,32 +52,26 @@
|
||||
(@key $t:ty, const $init:expr) => {{
|
||||
const __INIT: $t = $init;
|
||||
|
||||
#[inline]
|
||||
#[deny(unsafe_op_in_unsafe_fn)]
|
||||
unsafe fn __getit(
|
||||
_init: $crate::option::Option<&mut $crate::option::Option<$t>>,
|
||||
) -> $crate::option::Option<&'static $t> {
|
||||
use $crate::thread::local_impl::EagerStorage;
|
||||
use $crate::mem::needs_drop;
|
||||
use $crate::ptr::addr_of;
|
||||
|
||||
if needs_drop::<$t>() {
|
||||
#[thread_local]
|
||||
static VAL: EagerStorage<$t> = EagerStorage::new(__INIT);
|
||||
unsafe {
|
||||
VAL.get()
|
||||
}
|
||||
} else {
|
||||
#[thread_local]
|
||||
static VAL: $t = __INIT;
|
||||
unsafe {
|
||||
$crate::option::Option::Some(&*addr_of!(VAL))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
$crate::thread::LocalKey::new(__getit)
|
||||
use $crate::mem::needs_drop;
|
||||
use $crate::thread::LocalKey;
|
||||
use $crate::thread::local_impl::EagerStorage;
|
||||
|
||||
LocalKey::new(const {
|
||||
if needs_drop::<$t>() {
|
||||
|_| {
|
||||
#[thread_local]
|
||||
static VAL: EagerStorage<$t> = EagerStorage::new(__INIT);
|
||||
VAL.get()
|
||||
}
|
||||
} else {
|
||||
|_| {
|
||||
#[thread_local]
|
||||
static VAL: $t = __INIT;
|
||||
&VAL
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}},
|
||||
|
||||
@ -88,31 +82,26 @@ fn __init() -> $t {
|
||||
$init
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[deny(unsafe_op_in_unsafe_fn)]
|
||||
unsafe fn __getit(
|
||||
init: $crate::option::Option<&mut $crate::option::Option<$t>>,
|
||||
) -> $crate::option::Option<&'static $t> {
|
||||
use $crate::thread::local_impl::LazyStorage;
|
||||
use $crate::mem::needs_drop;
|
||||
|
||||
if needs_drop::<$t>() {
|
||||
#[thread_local]
|
||||
static VAL: LazyStorage<$t, ()> = LazyStorage::new();
|
||||
unsafe {
|
||||
VAL.get_or_init(init, __init)
|
||||
}
|
||||
} else {
|
||||
#[thread_local]
|
||||
static VAL: LazyStorage<$t, !> = LazyStorage::new();
|
||||
unsafe {
|
||||
VAL.get_or_init(init, __init)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
$crate::thread::LocalKey::new(__getit)
|
||||
use $crate::mem::needs_drop;
|
||||
use $crate::thread::LocalKey;
|
||||
use $crate::thread::local_impl::LazyStorage;
|
||||
|
||||
LocalKey::new(const {
|
||||
if needs_drop::<$t>() {
|
||||
|init| {
|
||||
#[thread_local]
|
||||
static VAL: LazyStorage<$t, ()> = LazyStorage::new();
|
||||
VAL.get_or_init(init, __init)
|
||||
}
|
||||
} else {
|
||||
|init| {
|
||||
#[thread_local]
|
||||
static VAL: LazyStorage<$t, !> = LazyStorage::new();
|
||||
VAL.get_or_init(init, __init)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}},
|
||||
($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
|
||||
|
@ -16,30 +16,22 @@
|
||||
},
|
||||
|
||||
// used to generate the `LocalKey` value for `thread_local!`
|
||||
(@key $t:ty, $init:expr) => {
|
||||
{
|
||||
#[inline]
|
||||
fn __init() -> $t { $init }
|
||||
(@key $t:ty, $init:expr) => {{
|
||||
#[inline]
|
||||
fn __init() -> $t { $init }
|
||||
|
||||
// `#[inline] does not work on windows-gnu due to linking errors around dllimports.
|
||||
// See https://github.com/rust-lang/rust/issues/109797.
|
||||
#[cfg_attr(not(windows), inline)]
|
||||
unsafe fn __getit(
|
||||
init: $crate::option::Option<&mut $crate::option::Option<$t>>,
|
||||
) -> $crate::option::Option<&'static $t> {
|
||||
use $crate::thread::local_impl::Key;
|
||||
unsafe {
|
||||
use $crate::thread::LocalKey;
|
||||
use $crate::thread::local_impl::Key;
|
||||
|
||||
static __KEY: Key<$t> = Key::new();
|
||||
unsafe {
|
||||
__KEY.get(init, __init)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
$crate::thread::LocalKey::new(__getit)
|
||||
}
|
||||
// Inlining does not work on windows-gnu due to linking errors around
|
||||
// dllimports. See https://github.com/rust-lang/rust/issues/109797.
|
||||
LocalKey::new(#[cfg_attr(windows, inline(never))] |init| {
|
||||
static VAL: Key<$t> = Key::new();
|
||||
VAL.get(init, __init)
|
||||
})
|
||||
}
|
||||
},
|
||||
}},
|
||||
($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
|
||||
$(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> =
|
||||
$crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*);
|
||||
@ -67,38 +59,33 @@ pub const fn new() -> Key<T> {
|
||||
Key { os: OsKey::new(Some(destroy_value::<T>)), marker: PhantomData }
|
||||
}
|
||||
|
||||
/// Get the value associated with this key, initializating it if necessary.
|
||||
/// Get a pointer to the TLS value, potentially initializing it with the
|
||||
/// provided parameters. If the TLS variable has been destroyed, a null
|
||||
/// pointer is returned.
|
||||
///
|
||||
/// # Safety
|
||||
/// * the returned reference must not be used after recursive initialization
|
||||
/// or thread destruction occurs.
|
||||
pub unsafe fn get(
|
||||
&'static self,
|
||||
i: Option<&mut Option<T>>,
|
||||
f: impl FnOnce() -> T,
|
||||
) -> Option<&'static T> {
|
||||
/// The resulting pointer may not be used after reentrant inialialization
|
||||
/// or thread destruction has occurred.
|
||||
pub fn get(&'static self, i: Option<&mut Option<T>>, f: impl FnOnce() -> T) -> *const T {
|
||||
// SAFETY: (FIXME: get should actually be safe)
|
||||
let ptr = unsafe { self.os.get() as *mut Value<T> };
|
||||
if ptr.addr() > 1 {
|
||||
// SAFETY: the check ensured the pointer is safe (its destructor
|
||||
// is not running) + it is coming from a trusted source (self).
|
||||
unsafe { Some(&(*ptr).value) }
|
||||
unsafe { &(*ptr).value }
|
||||
} else {
|
||||
// SAFETY: At this point we are sure we have no value and so
|
||||
// initializing (or trying to) is safe.
|
||||
unsafe { self.try_initialize(ptr, i, f) }
|
||||
self.try_initialize(ptr, i, f)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn try_initialize(
|
||||
fn try_initialize(
|
||||
&'static self,
|
||||
ptr: *mut Value<T>,
|
||||
i: Option<&mut Option<T>>,
|
||||
f: impl FnOnce() -> T,
|
||||
) -> Option<&'static T> {
|
||||
) -> *const T {
|
||||
if ptr.addr() == 1 {
|
||||
// destructor is running
|
||||
return None;
|
||||
return ptr::null();
|
||||
}
|
||||
|
||||
let value = i.and_then(Option::take).unwrap_or_else(f);
|
||||
@ -119,7 +106,7 @@ unsafe fn try_initialize(
|
||||
}
|
||||
|
||||
// SAFETY: We just created this value above.
|
||||
unsafe { Some(&(*ptr).value) }
|
||||
unsafe { &(*ptr).value }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,19 +13,14 @@
|
||||
(@key $t:ty, const $init:expr) => {{
|
||||
const __INIT: $t = $init;
|
||||
|
||||
#[inline]
|
||||
#[deny(unsafe_op_in_unsafe_fn)]
|
||||
unsafe fn __getit(
|
||||
_init: $crate::option::Option<&mut $crate::option::Option<$t>>,
|
||||
) -> $crate::option::Option<&'static $t> {
|
||||
unsafe {
|
||||
use $crate::thread::LocalKey;
|
||||
use $crate::thread::local_impl::EagerStorage;
|
||||
|
||||
static VAL: EagerStorage<$t> = EagerStorage { value: __INIT };
|
||||
$crate::option::Option::Some(&VAL.value)
|
||||
}
|
||||
|
||||
unsafe {
|
||||
$crate::thread::LocalKey::new(__getit)
|
||||
LocalKey::new(|_| {
|
||||
static VAL: EagerStorage<$t> = EagerStorage { value: __INIT };
|
||||
&VAL.value
|
||||
})
|
||||
}
|
||||
}},
|
||||
|
||||
@ -34,19 +29,14 @@ unsafe fn __getit(
|
||||
#[inline]
|
||||
fn __init() -> $t { $init }
|
||||
|
||||
#[inline]
|
||||
#[deny(unsafe_op_in_unsafe_fn)]
|
||||
unsafe fn __getit(
|
||||
init: $crate::option::Option<&mut $crate::option::Option<$t>>,
|
||||
) -> $crate::option::Option<&'static $t> {
|
||||
unsafe {
|
||||
use $crate::thread::LocalKey;
|
||||
use $crate::thread::local_impl::LazyStorage;
|
||||
|
||||
static VAL: LazyStorage<$t> = LazyStorage::new();
|
||||
unsafe { $crate::option::Option::Some(VAL.get(init, __init)) }
|
||||
}
|
||||
|
||||
unsafe {
|
||||
$crate::thread::LocalKey::new(__getit)
|
||||
LocalKey::new(|init| {
|
||||
static VAL: LazyStorage<$t> = LazyStorage::new();
|
||||
VAL.get(init, __init)
|
||||
})
|
||||
}
|
||||
}},
|
||||
($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
|
||||
@ -73,16 +63,13 @@ pub const fn new() -> LazyStorage<T> {
|
||||
LazyStorage { value: UnsafeCell::new(None) }
|
||||
}
|
||||
|
||||
/// Gets a reference to the contained value, initializing it if necessary.
|
||||
/// Get a pointer to the TLS value, potentially initializing it with the
|
||||
/// provided parameters.
|
||||
///
|
||||
/// # Safety
|
||||
/// The returned reference may not be used after reentrant initialization has occurred.
|
||||
/// The resulting pointer may not be used after reentrant inialialization
|
||||
/// has occurred.
|
||||
#[inline]
|
||||
pub unsafe fn get(
|
||||
&'static self,
|
||||
i: Option<&mut Option<T>>,
|
||||
f: impl FnOnce() -> T,
|
||||
) -> &'static T {
|
||||
pub fn get(&'static self, i: Option<&mut Option<T>>, f: impl FnOnce() -> T) -> *const T {
|
||||
let value = unsafe { &*self.value.get() };
|
||||
match value {
|
||||
Some(v) => v,
|
||||
@ -91,11 +78,7 @@ pub unsafe fn get(
|
||||
}
|
||||
|
||||
#[cold]
|
||||
unsafe fn initialize(
|
||||
&'static self,
|
||||
i: Option<&mut Option<T>>,
|
||||
f: impl FnOnce() -> T,
|
||||
) -> &'static T {
|
||||
fn initialize(&'static self, i: Option<&mut Option<T>>, f: impl FnOnce() -> T) -> *const T {
|
||||
let value = i.and_then(Option::take).unwrap_or_else(f);
|
||||
// Destroy the old value, after updating the TLS variable as the
|
||||
// destructor might reference it.
|
||||
|
@ -123,7 +123,7 @@ pub struct LocalKey<T: 'static> {
|
||||
// trivially devirtualizable by LLVM because the value of `inner` never
|
||||
// changes and the constant should be readonly within a crate. This mainly
|
||||
// only runs into problems when TLS statics are exported across crates.
|
||||
inner: unsafe fn(Option<&mut Option<T>>) -> Option<&'static T>,
|
||||
inner: fn(Option<&mut Option<T>>) -> *const T,
|
||||
}
|
||||
|
||||
#[stable(feature = "std_debug", since = "1.16.0")]
|
||||
@ -238,9 +238,7 @@ impl<T: 'static> LocalKey<T> {
|
||||
issue = "none"
|
||||
)]
|
||||
#[rustc_const_unstable(feature = "thread_local_internals", issue = "none")]
|
||||
pub const unsafe fn new(
|
||||
inner: unsafe fn(Option<&mut Option<T>>) -> Option<&'static T>,
|
||||
) -> LocalKey<T> {
|
||||
pub const unsafe fn new(inner: fn(Option<&mut Option<T>>) -> *const T) -> LocalKey<T> {
|
||||
LocalKey { inner }
|
||||
}
|
||||
|
||||
@ -281,8 +279,7 @@ pub fn try_with<F, R>(&'static self, f: F) -> Result<R, AccessError>
|
||||
where
|
||||
F: FnOnce(&T) -> R,
|
||||
{
|
||||
// SAFETY: `inner` is safe to call within the lifetime of the thread
|
||||
let thread_local = unsafe { (self.inner)(None).ok_or(AccessError)? };
|
||||
let thread_local = unsafe { (self.inner)(None).as_ref().ok_or(AccessError)? };
|
||||
Ok(f(thread_local))
|
||||
}
|
||||
|
||||
@ -304,9 +301,8 @@ fn initialize_with<F, R>(&'static self, init: T, f: F) -> R
|
||||
{
|
||||
let mut init = Some(init);
|
||||
|
||||
// SAFETY: `inner` is safe to call within the lifetime of the thread
|
||||
let reference = unsafe {
|
||||
(self.inner)(Some(&mut init)).expect(
|
||||
(self.inner)(Some(&mut init)).as_ref().expect(
|
||||
"cannot access a Thread Local Storage value \
|
||||
during or after destruction",
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user