Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 175ad22

Browse files
committedJun 4, 2024
Auto merge of rust-lang#125525 - joboet:tls_accessor, r=cuviper
Make TLS accessors closures that return pointers The current TLS macros generate a function that returns an `Option<&'static T>`. This is both risky as we lie about lifetimes, and necessitates that those functions are `unsafe`. By returning a `*const T` instead, the accessor function do not have safety requirements any longer and can be made closures without hassle. This PR does exactly that! For native TLS, the closure approach makes it trivial to select the right accessor function at compile-time, which could result in a slight speed-up (I have the hope that the accessors are now simple enough for the MIR-inliner to kick in).
2 parents 6ef46b3 + 078095a commit 175ad22

File tree

6 files changed

+107
-180
lines changed

6 files changed

+107
-180
lines changed
 

‎std/src/sys/thread_local/fast_local/eager.rs

+13-21
Original file line numberDiff line numberDiff line change
@@ -21,43 +21,35 @@ impl<T> Storage<T> {
2121
Storage { state: Cell::new(State::Initial), val: UnsafeCell::new(val) }
2222
}
2323

24-
/// Get a reference to the TLS value. If the TLS variable has been destroyed,
25-
/// `None` is returned.
24+
/// Get a pointer to the TLS value. If the TLS variable has been destroyed,
25+
/// a null pointer is returned.
2626
///
27-
/// # Safety
28-
/// * The `self` reference must remain valid until the TLS destructor has been
29-
/// run.
30-
/// * The returned reference may only be used until thread destruction occurs
31-
/// and may not be used after reentrant initialization has occurred.
27+
/// The resulting pointer may not be used after thread destruction has
28+
/// occurred.
3229
///
33-
// FIXME(#110897): return NonNull instead of lying about the lifetime.
30+
/// # Safety
31+
/// The `self` reference must remain valid until the TLS destructor is run.
3432
#[inline]
35-
pub unsafe fn get(&self) -> Option<&'static T> {
33+
pub unsafe fn get(&self) -> *const T {
3634
match self.state.get() {
37-
// SAFETY: as the state is not `Destroyed`, the value cannot have
38-
// been destroyed yet. The reference fulfills the terms outlined
39-
// above.
40-
State::Alive => unsafe { Some(&*self.val.get()) },
41-
State::Destroyed => None,
35+
State::Alive => self.val.get(),
36+
State::Destroyed => ptr::null(),
4237
State::Initial => unsafe { self.initialize() },
4338
}
4439
}
4540

4641
#[cold]
47-
unsafe fn initialize(&self) -> Option<&'static T> {
42+
unsafe fn initialize(&self) -> *const T {
4843
// Register the destructor
4944

5045
// SAFETY:
51-
// * the destructor will be called at thread destruction.
52-
// * the caller guarantees that `self` will be valid until that time.
46+
// The caller guarantees that `self` will be valid until thread destruction.
5347
unsafe {
5448
register_dtor(ptr::from_ref(self).cast_mut().cast(), destroy::<T>);
5549
}
50+
5651
self.state.set(State::Alive);
57-
// SAFETY: as the state is not `Destroyed`, the value cannot have
58-
// been destroyed yet. The reference fulfills the terms outlined
59-
// above.
60-
unsafe { Some(&*self.val.get()) }
52+
self.val.get()
6153
}
6254
}
6355

‎std/src/sys/thread_local/fast_local/lazy.rs

+13-33
Original file line numberDiff line numberDiff line change
@@ -39,49 +39,31 @@ where
3939
Storage { state: UnsafeCell::new(State::Initial) }
4040
}
4141

42-
/// Get a reference to the TLS value, potentially initializing it with the
43-
/// provided parameters. If the TLS variable has been destroyed, `None` is
44-
/// returned.
42+
/// Get a pointer to the TLS value, potentially initializing it with the
43+
/// provided parameters. If the TLS variable has been destroyed, a null
44+
/// pointer is returned.
4545
///
46-
/// # Safety
47-
/// * The `self` reference must remain valid until the TLS destructor is run,
48-
/// at which point the returned reference is invalidated.
49-
/// * The returned reference may only be used until thread destruction occurs
50-
/// and may not be used after reentrant initialization has occurred.
46+
/// The resulting pointer may not be used after reentrant inialialization
47+
/// or thread destruction has occurred.
5148
///
52-
// FIXME(#110897): return NonNull instead of lying about the lifetime.
49+
/// # Safety
50+
/// The `self` reference must remain valid until the TLS destructor is run.
5351
#[inline]
54-
pub unsafe fn get_or_init(
55-
&self,
56-
i: Option<&mut Option<T>>,
57-
f: impl FnOnce() -> T,
58-
) -> Option<&'static T> {
59-
// SAFETY:
60-
// No mutable reference to the inner value exists outside the calls to
61-
// `replace`. The lifetime of the returned reference fulfills the terms
62-
// outlined above.
52+
pub unsafe fn get_or_init(&self, i: Option<&mut Option<T>>, f: impl FnOnce() -> T) -> *const T {
6353
let state = unsafe { &*self.state.get() };
6454
match state {
65-
State::Alive(v) => Some(v),
66-
State::Destroyed(_) => None,
55+
State::Alive(v) => v,
56+
State::Destroyed(_) => ptr::null(),
6757
State::Initial => unsafe { self.initialize(i, f) },
6858
}
6959
}
7060

7161
#[cold]
72-
unsafe fn initialize(
73-
&self,
74-
i: Option<&mut Option<T>>,
75-
f: impl FnOnce() -> T,
76-
) -> Option<&'static T> {
62+
unsafe fn initialize(&self, i: Option<&mut Option<T>>, f: impl FnOnce() -> T) -> *const T {
7763
// Perform initialization
7864

7965
let v = i.and_then(Option::take).unwrap_or_else(f);
8066

81-
// SAFETY:
82-
// If references to the inner value exist, they were created in `f`
83-
// and are invalidated here. The caller promises to never use them
84-
// after this.
8567
let old = unsafe { self.state.get().replace(State::Alive(v)) };
8668
match old {
8769
// If the variable is not being recursively initialized, register
@@ -92,12 +74,10 @@ where
9274
val => drop(val),
9375
}
9476

95-
// SAFETY:
96-
// Initialization was completed and the state was set to `Alive`, so the
97-
// reference fulfills the terms outlined above.
77+
// SAFETY: the state was just set to `Alive`
9878
unsafe {
9979
let State::Alive(v) = &*self.state.get() else { unreachable_unchecked() };
100-
Some(v)
80+
v
10181
}
10282
}
10383
}

‎std/src/sys/thread_local/fast_local/mod.rs

+34-45
Original file line numberDiff line numberDiff line change
@@ -52,32 +52,26 @@ pub macro thread_local_inner {
5252
(@key $t:ty, const $init:expr) => {{
5353
const __INIT: $t = $init;
5454

55-
#[inline]
56-
#[deny(unsafe_op_in_unsafe_fn)]
57-
unsafe fn __getit(
58-
_init: $crate::option::Option<&mut $crate::option::Option<$t>>,
59-
) -> $crate::option::Option<&'static $t> {
60-
use $crate::thread::local_impl::EagerStorage;
55+
unsafe {
6156
use $crate::mem::needs_drop;
62-
use $crate::ptr::addr_of;
57+
use $crate::thread::LocalKey;
58+
use $crate::thread::local_impl::EagerStorage;
6359

64-
if needs_drop::<$t>() {
65-
#[thread_local]
66-
static VAL: EagerStorage<$t> = EagerStorage::new(__INIT);
67-
unsafe {
68-
VAL.get()
60+
LocalKey::new(const {
61+
if needs_drop::<$t>() {
62+
|_| {
63+
#[thread_local]
64+
static VAL: EagerStorage<$t> = EagerStorage::new(__INIT);
65+
VAL.get()
66+
}
67+
} else {
68+
|_| {
69+
#[thread_local]
70+
static VAL: $t = __INIT;
71+
&VAL
72+
}
6973
}
70-
} else {
71-
#[thread_local]
72-
static VAL: $t = __INIT;
73-
unsafe {
74-
$crate::option::Option::Some(&*addr_of!(VAL))
75-
}
76-
}
77-
}
78-
79-
unsafe {
80-
$crate::thread::LocalKey::new(__getit)
74+
})
8175
}
8276
}},
8377

@@ -88,31 +82,26 @@ pub macro thread_local_inner {
8882
$init
8983
}
9084

91-
#[inline]
92-
#[deny(unsafe_op_in_unsafe_fn)]
93-
unsafe fn __getit(
94-
init: $crate::option::Option<&mut $crate::option::Option<$t>>,
95-
) -> $crate::option::Option<&'static $t> {
96-
use $crate::thread::local_impl::LazyStorage;
85+
unsafe {
9786
use $crate::mem::needs_drop;
87+
use $crate::thread::LocalKey;
88+
use $crate::thread::local_impl::LazyStorage;
9889

99-
if needs_drop::<$t>() {
100-
#[thread_local]
101-
static VAL: LazyStorage<$t, ()> = LazyStorage::new();
102-
unsafe {
103-
VAL.get_or_init(init, __init)
90+
LocalKey::new(const {
91+
if needs_drop::<$t>() {
92+
|init| {
93+
#[thread_local]
94+
static VAL: LazyStorage<$t, ()> = LazyStorage::new();
95+
VAL.get_or_init(init, __init)
96+
}
97+
} else {
98+
|init| {
99+
#[thread_local]
100+
static VAL: LazyStorage<$t, !> = LazyStorage::new();
101+
VAL.get_or_init(init, __init)
102+
}
104103
}
105-
} else {
106-
#[thread_local]
107-
static VAL: LazyStorage<$t, !> = LazyStorage::new();
108-
unsafe {
109-
VAL.get_or_init(init, __init)
110-
}
111-
}
112-
}
113-
114-
unsafe {
115-
$crate::thread::LocalKey::new(__getit)
104+
})
116105
}
117106
}},
118107
($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {

‎std/src/sys/thread_local/os_local.rs

+25-38
Original file line numberDiff line numberDiff line change
@@ -16,30 +16,22 @@ pub macro thread_local_inner {
1616
},
1717

1818
// used to generate the `LocalKey` value for `thread_local!`
19-
(@key $t:ty, $init:expr) => {
20-
{
21-
#[inline]
22-
fn __init() -> $t { $init }
19+
(@key $t:ty, $init:expr) => {{
20+
#[inline]
21+
fn __init() -> $t { $init }
2322

24-
// `#[inline] does not work on windows-gnu due to linking errors around dllimports.
25-
// See https://github.com/rust-lang/rust/issues/109797.
26-
#[cfg_attr(not(windows), inline)]
27-
unsafe fn __getit(
28-
init: $crate::option::Option<&mut $crate::option::Option<$t>>,
29-
) -> $crate::option::Option<&'static $t> {
30-
use $crate::thread::local_impl::Key;
31-
32-
static __KEY: Key<$t> = Key::new();
33-
unsafe {
34-
__KEY.get(init, __init)
35-
}
36-
}
23+
unsafe {
24+
use $crate::thread::LocalKey;
25+
use $crate::thread::local_impl::Key;
3726

38-
unsafe {
39-
$crate::thread::LocalKey::new(__getit)
40-
}
27+
// Inlining does not work on windows-gnu due to linking errors around
28+
// dllimports. See https://github.com/rust-lang/rust/issues/109797.
29+
LocalKey::new(#[cfg_attr(windows, inline(never))] |init| {
30+
static VAL: Key<$t> = Key::new();
31+
VAL.get(init, __init)
32+
})
4133
}
42-
},
34+
}},
4335
($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
4436
$(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> =
4537
$crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*);
@@ -67,38 +59,33 @@ impl<T: 'static> Key<T> {
6759
Key { os: OsKey::new(Some(destroy_value::<T>)), marker: PhantomData }
6860
}
6961

70-
/// Get the value associated with this key, initializating it if necessary.
62+
/// Get a pointer to the TLS value, potentially initializing it with the
63+
/// provided parameters. If the TLS variable has been destroyed, a null
64+
/// pointer is returned.
7165
///
72-
/// # Safety
73-
/// * the returned reference must not be used after recursive initialization
74-
/// or thread destruction occurs.
75-
pub unsafe fn get(
76-
&'static self,
77-
i: Option<&mut Option<T>>,
78-
f: impl FnOnce() -> T,
79-
) -> Option<&'static T> {
66+
/// The resulting pointer may not be used after reentrant inialialization
67+
/// or thread destruction has occurred.
68+
pub fn get(&'static self, i: Option<&mut Option<T>>, f: impl FnOnce() -> T) -> *const T {
8069
// SAFETY: (FIXME: get should actually be safe)
8170
let ptr = unsafe { self.os.get() as *mut Value<T> };
8271
if ptr.addr() > 1 {
8372
// SAFETY: the check ensured the pointer is safe (its destructor
8473
// is not running) + it is coming from a trusted source (self).
85-
unsafe { Some(&(*ptr).value) }
74+
unsafe { &(*ptr).value }
8675
} else {
87-
// SAFETY: At this point we are sure we have no value and so
88-
// initializing (or trying to) is safe.
89-
unsafe { self.try_initialize(ptr, i, f) }
76+
self.try_initialize(ptr, i, f)
9077
}
9178
}
9279

93-
unsafe fn try_initialize(
80+
fn try_initialize(
9481
&'static self,
9582
ptr: *mut Value<T>,
9683
i: Option<&mut Option<T>>,
9784
f: impl FnOnce() -> T,
98-
) -> Option<&'static T> {
85+
) -> *const T {
9986
if ptr.addr() == 1 {
10087
// destructor is running
101-
return None;
88+
return ptr::null();
10289
}
10390

10491
let value = i.and_then(Option::take).unwrap_or_else(f);
@@ -119,7 +106,7 @@ impl<T: 'static> Key<T> {
119106
}
120107

121108
// SAFETY: We just created this value above.
122-
unsafe { Some(&(*ptr).value) }
109+
unsafe { &(*ptr).value }
123110
}
124111
}
125112

There was a problem loading the remainder of the diff.

0 commit comments

Comments
 (0)
Failed to load comments.