std: make TLS accessors closures that return pointers

This commit is contained in:
joboet 2024-05-25 00:19:47 +02:00
parent 9e297bf54d
commit 1052d2931c
No known key found for this signature in database
GPG Key ID: 704E0149B0194B3C
6 changed files with 111 additions and 184 deletions

View File

@ -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()
}
}

View File

@ -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
}
}
}

View File

@ -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)*) => {

View File

@ -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 }
}
}

View File

@ -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.

View File

@ -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",
)