Skip to content

Commit 70e1dc9

Browse files
committed
Avoid unwind across extern "C" in thread_local::fast_local.rs
1 parent 398fa21 commit 70e1dc9

File tree

3 files changed

+33
-12
lines changed

3 files changed

+33
-12
lines changed

library/std/src/sys/common/thread_local/fast_local.rs

+11-11
Original file line numberDiff line numberDiff line change
@@ -33,28 +33,29 @@ pub macro thread_local_inner {
3333
// 1 == dtor registered, dtor not run
3434
// 2 == dtor registered and is running or has run
3535
#[thread_local]
36-
static mut STATE: $crate::primitive::u8 = 0;
36+
static STATE: $crate::cell::Cell<$crate::primitive::u8> = $crate::cell::Cell::new(0);
3737

38+
// Safety: Performs `drop_in_place(ptr as *mut $t)`, and requires
39+
// all that comes with it.
3840
unsafe extern "C" fn destroy(ptr: *mut $crate::primitive::u8) {
39-
let ptr = ptr as *mut $t;
40-
41-
unsafe {
42-
$crate::debug_assert_eq!(STATE, 1);
43-
STATE = 2;
44-
$crate::ptr::drop_in_place(ptr);
45-
}
41+
$crate::thread::local_impl::abort_on_dtor_unwind(|| {
42+
let old_state = STATE.replace(2);
43+
$crate::debug_assert_eq!(old_state, 1);
44+
// Safety: safety requirement is passed on to caller.
45+
unsafe { $crate::ptr::drop_in_place(ptr.cast::<$t>()); }
46+
});
4647
}
4748

4849
unsafe {
49-
match STATE {
50+
match STATE.get() {
5051
// 0 == we haven't registered a destructor, so do
5152
// so now.
5253
0 => {
5354
$crate::thread::local_impl::Key::<$t>::register_dtor(
5455
$crate::ptr::addr_of_mut!(VAL) as *mut $crate::primitive::u8,
5556
destroy,
5657
);
57-
STATE = 1;
58+
STATE.set(1);
5859
$crate::option::Option::Some(&VAL)
5960
}
6061
// 1 == the destructor is registered and the value
@@ -148,7 +149,6 @@ impl<T> fmt::Debug for Key<T> {
148149
f.debug_struct("Key").finish_non_exhaustive()
149150
}
150151
}
151-
152152
impl<T> Key<T> {
153153
pub const fn new() -> Key<T> {
154154
Key { inner: LazyKeyInner::new(), dtor_state: Cell::new(DtorState::Unregistered) }

library/std/src/sys/common/thread_local/mod.rs

+21
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,24 @@ mod lazy {
101101
}
102102
}
103103
}
104+
105+
/// Run a callback in a scenario which must not unwind (such as a `extern "C"
106+
/// fn` declared in a user crate). If the callback unwinds anyway, then
107+
/// `rtabort` with a message about thread local panicking on drop.
108+
#[inline]
109+
pub fn abort_on_dtor_unwind(f: impl FnOnce()) {
110+
// Using a guard like this is lower cost.
111+
let guard = DtorUnwindGuard;
112+
f();
113+
core::mem::forget(guard);
114+
115+
struct DtorUnwindGuard;
116+
impl Drop for DtorUnwindGuard {
117+
#[inline]
118+
fn drop(&mut self) {
119+
// This is not terribly descriptive, but it doesn't need to be as we'll
120+
// already have printed a panic message at this point.
121+
rtabort!("thread local panicked on drop");
122+
}
123+
}
124+
}

library/std/src/thread/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ cfg_if::cfg_if! {
206206
#[doc(hidden)]
207207
#[unstable(feature = "thread_local_internals", issue = "none")]
208208
pub mod local_impl {
209-
pub use crate::sys::common::thread_local::{thread_local_inner, Key};
209+
pub use crate::sys::common::thread_local::{thread_local_inner, Key, abort_on_dtor_unwind};
210210
}
211211
}
212212
}

0 commit comments

Comments
 (0)