Skip to content

Commit bfea6a3

Browse files
committed
Use thread local list to store destructors
1 parent 60cdb6a commit bfea6a3

File tree

1 file changed

+67
-45
lines changed
  • library/std/src/sys/thread_local/key

1 file changed

+67
-45
lines changed

library/std/src/sys/thread_local/key/racy.rs

Lines changed: 67 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@ pub struct LazyKey {
1717
key: AtomicUsize,
1818
/// Destructor for the TLS value.
1919
dtor: Option<unsafe extern "C" fn(*mut u8)>,
20-
/// Next element of process-wide destructor list.
21-
#[cfg(not(target_thread_local))]
22-
next: atomic::AtomicPtr<LazyKey>,
2320
}
2421

2522
// Define a sentinel value that is likely not to be returned
@@ -34,12 +31,7 @@ const KEY_SENTVAL: usize = libc::PTHREAD_KEYS_MAX + 1;
3431

3532
impl LazyKey {
3633
pub const fn new(dtor: Option<unsafe extern "C" fn(*mut u8)>) -> LazyKey {
37-
LazyKey {
38-
key: atomic::AtomicUsize::new(KEY_SENTVAL),
39-
dtor,
40-
#[cfg(not(target_thread_local))]
41-
next: atomic::AtomicPtr::new(crate::ptr::null_mut()),
42-
}
34+
LazyKey { key: atomic::AtomicUsize::new(KEY_SENTVAL), dtor }
4335
}
4436

4537
#[inline]
@@ -71,79 +63,109 @@ impl LazyKey {
7163
key2
7264
};
7365
rtassert!(key as usize != KEY_SENTVAL);
74-
match self.key.compare_exchange(
66+
67+
let final_key = match self.key.compare_exchange(
7568
KEY_SENTVAL,
7669
key as usize,
7770
Ordering::Release,
7871
Ordering::Acquire,
7972
) {
8073
// The CAS succeeded, so we've created the actual key
81-
Ok(_) => {
82-
#[cfg(not(target_thread_local))]
83-
if self.dtor.is_some() {
84-
unsafe { register_dtor(self) };
85-
}
86-
key as usize
87-
}
74+
Ok(_) => key as usize,
8875
// If someone beat us to the punch, use their key instead
8976
Err(n) => unsafe {
9077
super::destroy(key);
9178
n
9279
},
80+
};
81+
82+
#[cfg(not(target_thread_local))]
83+
if self.dtor.is_some() {
84+
unsafe { register_dtor(self) };
9385
}
86+
87+
final_key
9488
}
9589
}
9690

9791
/// POSIX does not run TLS destructors on process exit.
98-
/// Thus we keep our own global list for that purpose.
92+
/// Thus we keep our own thread-local list for that purpose.
9993
#[cfg(not(target_thread_local))]
100-
static DTORS: atomic::AtomicPtr<LazyKey> = atomic::AtomicPtr::new(crate::ptr::null_mut());
94+
fn lazy_keys() -> &'static crate::cell::RefCell<Vec<&'static LazyKey>> {
95+
static KEY: atomic::AtomicUsize = atomic::AtomicUsize::new(KEY_SENTVAL);
96+
97+
unsafe extern "C" fn drop_lazy_keys(ptr: *mut u8) {
98+
let ptr = ptr as *mut crate::cell::RefCell<Vec<&'static LazyKey>>;
99+
if !ptr.is_null() {
100+
drop(unsafe { Box::from_raw(ptr) });
101+
}
102+
}
103+
104+
// Allocate a TLS key to store the thread local destructor list.
105+
let mut key = KEY.load(Ordering::Acquire) as super::Key;
106+
if key == KEY_SENTVAL as _ {
107+
let new_key = super::create(Some(drop_lazy_keys));
108+
match KEY.compare_exchange_weak(
109+
KEY_SENTVAL,
110+
new_key as _,
111+
Ordering::Release,
112+
Ordering::Acquire,
113+
) {
114+
Ok(_) => key = new_key,
115+
Err(other_key) => {
116+
unsafe { super::destroy(new_key) };
117+
key = other_key as _;
118+
}
119+
}
120+
}
121+
122+
// And allocate the list for this thread if necessary.
123+
let mut ptr = unsafe { super::get(key) as *const crate::cell::RefCell<Vec<&'static LazyKey>> };
124+
if ptr.is_null() {
125+
let list = Box::new(crate::cell::RefCell::new(Vec::new()));
126+
ptr = Box::into_raw(list);
127+
unsafe { super::set(key, ptr as _) };
128+
}
129+
130+
unsafe { &*ptr }
131+
}
101132

102133
/// Registers destructor to run at process exit.
103134
#[cfg(not(target_thread_local))]
104-
unsafe fn register_dtor(key: &'static LazyKey) {
135+
unsafe fn register_dtor(lazy_key: &'static LazyKey) {
105136
crate::sys::thread_local::guard::enable();
106137

107-
let this = <*const LazyKey>::cast_mut(key);
108-
// Use acquire ordering to pass along the changes done by the previously
109-
// registered keys when we store the new head with release ordering.
110-
let mut head = DTORS.load(Ordering::Acquire);
111-
loop {
112-
key.next.store(head, Ordering::Relaxed);
113-
match DTORS.compare_exchange_weak(head, this, Ordering::Release, Ordering::Acquire) {
114-
Ok(_) => break,
115-
Err(new) => head = new,
116-
}
117-
}
138+
lazy_keys().borrow_mut().push(lazy_key);
118139
}
119140

120141
/// Run destructors at process exit.
121142
///
122143
/// SAFETY: This will and must only be run by the destructor callback in [`guard`].
123144
#[cfg(not(target_thread_local))]
124145
pub unsafe fn run_dtors() {
146+
let lazy_keys_cell = lazy_keys();
147+
let mut lazy_keys = lazy_keys_cell.take();
148+
125149
for _ in 0..5 {
126150
let mut any_run = false;
127-
128-
// Use acquire ordering to observe key initialization.
129-
let mut cur = DTORS.load(Ordering::Acquire);
130-
while !cur.is_null() {
131-
let key = unsafe { (*cur).key.load(Ordering::Acquire) };
132-
let dtor = unsafe { (*cur).dtor.unwrap() };
133-
cur = unsafe { (*cur).next.load(Ordering::Relaxed) };
134-
135-
let ptr = unsafe { super::get(key as _) };
136-
if !ptr.is_null() {
137-
unsafe {
138-
super::set(key as _, crate::ptr::null_mut());
139-
dtor(ptr as *mut _);
151+
for lazy_key in &lazy_keys {
152+
if let Some(dtor) = &lazy_key.dtor {
153+
let key = lazy_key.force();
154+
let ptr = unsafe { super::get(key) };
155+
if !ptr.is_null() {
156+
unsafe { dtor(ptr) };
157+
unsafe { super::set(key, crate::ptr::null_mut()) };
140158
any_run = true;
141159
}
142160
}
143161
}
144162

145-
if !any_run {
163+
let mut new_lazy_keys = lazy_keys_cell.borrow_mut();
164+
165+
if !any_run && new_lazy_keys.is_empty() {
146166
break;
147167
}
168+
169+
lazy_keys.extend(new_lazy_keys.drain(..));
148170
}
149171
}

0 commit comments

Comments
 (0)