1 use crate::cell::{Cell, UnsafeCell};
2 use crate::sync::atomic::{AtomicU8, Ordering};
3 use crate::sync::mpsc::{channel, Sender};
4 use crate::thread::{self, LocalKey};
5 use crate::thread_local;
7 struct Foo(Sender<()>);
11 let Foo(ref s) = *self;
18 thread_local!(static FOO: Cell<i32> = Cell::new(1));
20 thread_local!(static FOO2: Cell<i32> = const { Cell::new(1) });
23 fn run(key: &'static LocalKey<Cell<i32>>) {
25 assert_eq!(f.get(), 1);
28 let t = thread::spawn(move || {
30 assert_eq!(f.get(), 1);
36 assert_eq!(f.get(), 2);
43 struct Foo(&'static LocalKey<Foo>);
46 assert!(self.0.try_with(|_| ()).is_err());
50 thread_local!(static FOO: Foo = Foo(&FOO));
52 thread_local!(static FOO2: Foo = const { Foo(&FOO2) });
55 fn run(foo: &'static LocalKey<Foo>) {
56 thread::spawn(move || {
57 assert!(foo.try_with(|_| ()).is_ok());
66 thread_local!(static FOO: UnsafeCell<Option<Foo>> = UnsafeCell::new(None));
68 thread_local!(static FOO2: UnsafeCell<Option<Foo>> = const { UnsafeCell::new(None) });
71 fn run(key: &'static LocalKey<UnsafeCell<Option<Foo>>>) {
72 let (tx, rx) = channel();
73 let t = thread::spawn(move || unsafe {
74 let mut tx = Some(tx);
76 *f.get() = Some(Foo(tx.take().unwrap()));
86 struct S1(&'static LocalKey<UnsafeCell<Option<S1>>>, &'static LocalKey<UnsafeCell<Option<S2>>>);
87 struct S2(&'static LocalKey<UnsafeCell<Option<S1>>>, &'static LocalKey<UnsafeCell<Option<S2>>>);
88 thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell::new(None));
89 thread_local!(static K2: UnsafeCell<Option<S2>> = UnsafeCell::new(None));
90 thread_local!(static K3: UnsafeCell<Option<S1>> = const { UnsafeCell::new(None) });
91 thread_local!(static K4: UnsafeCell<Option<S2>> = const { UnsafeCell::new(None) });
92 static mut HITS: usize = 0;
98 if self.1.try_with(|_| ()).is_err() {
102 self.1.with(|s| *s.get() = Some(S2(self.0, self.1)));
114 assert!(self.0.try_with(|_| ()).is_ok());
116 self.0.with(|s| *s.get() = Some(S1(self.0, self.1)));
121 thread::spawn(move || {
131 thread::spawn(move || {
139 fn self_referential() {
140 struct S1(&'static LocalKey<UnsafeCell<Option<S1>>>);
142 thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell::new(None));
143 thread_local!(static K2: UnsafeCell<Option<S1>> = const { UnsafeCell::new(None) });
147 assert!(self.0.try_with(|_| ()).is_err());
151 thread::spawn(move || unsafe {
152 K1.with(|s| *s.get() = Some(S1(&K1)));
157 thread::spawn(move || unsafe {
158 K2.with(|s| *s.get() = Some(S1(&K2)));
164 // Note that this test will deadlock if TLS destructors aren't run (this
165 // requires the destructor to be run to pass the test).
167 fn dtors_in_dtors_in_dtors() {
168 struct S1(Sender<()>);
169 thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell::new(None));
170 thread_local!(static K2: UnsafeCell<Option<Foo>> = UnsafeCell::new(None));
174 let S1(ref tx) = *self;
176 let _ = K2.try_with(|s| *s.get() = Some(Foo(tx.clone())));
181 let (tx, rx) = channel();
182 let _t = thread::spawn(move || unsafe {
183 let mut tx = Some(tx);
184 K1.with(|s| *s.get() = Some(S1(tx.take().unwrap())));
190 fn dtors_in_dtors_in_dtors_const_init() {
191 struct S1(Sender<()>);
192 thread_local!(static K1: UnsafeCell<Option<S1>> = const { UnsafeCell::new(None) });
193 thread_local!(static K2: UnsafeCell<Option<Foo>> = const { UnsafeCell::new(None) });
197 let S1(ref tx) = *self;
199 let _ = K2.try_with(|s| *s.get() = Some(Foo(tx.clone())));
204 let (tx, rx) = channel();
205 let _t = thread::spawn(move || unsafe {
206 let mut tx = Some(tx);
207 K1.with(|s| *s.get() = Some(S1(tx.take().unwrap())));
212 // This test tests that TLS destructors have run before the thread joins. The
213 // test has no false positives (meaning: if the test fails, there's actually
214 // an ordering problem). It may have false negatives, where the test passes but
215 // join is not guaranteed to be after the TLS destructors. However, false
216 // negatives should be exceedingly rare due to judicious use of
217 // thread::yield_now and running the test several times.
219 fn join_orders_after_tls_destructors() {
220 // We emulate a synchronous MPSC rendezvous channel using only atomics and
221 // thread::yield_now. We can't use std::mpsc as the implementation itself
222 // may rely on thread locals.
224 // The basic state machine for an SPSC rendezvous channel is:
225 // FRESH -> THREAD1_WAITING -> MAIN_THREAD_RENDEZVOUS
226 // where the first transition is done by the “receiving” thread and the 2nd
227 // transition is done by the “sending” thread.
229 // We add an additional state `THREAD2_LAUNCHED` between `FRESH` and
230 // `THREAD1_WAITING` to block until all threads are actually running.
232 // A thread that joins on the “receiving” thread completion should never
233 // observe the channel in the `THREAD1_WAITING` state. If this does occur,
234 // we switch to the “poison” state `THREAD2_JOINED` and panic all around.
235 // (This is equivalent to “sending” from an alternate producer thread.)
237 const THREAD2_LAUNCHED: u8 = 1;
238 const THREAD1_WAITING: u8 = 2;
239 const MAIN_THREAD_RENDEZVOUS: u8 = 3;
240 const THREAD2_JOINED: u8 = 4;
241 static SYNC_STATE: AtomicU8 = AtomicU8::new(FRESH);
244 SYNC_STATE.store(FRESH, Ordering::SeqCst);
246 let jh = thread::Builder::new()
247 .name("thread1".into())
251 impl Drop for TlDrop {
253 let mut sync_state = SYNC_STATE.swap(THREAD1_WAITING, Ordering::SeqCst);
256 THREAD2_LAUNCHED | THREAD1_WAITING => thread::yield_now(),
257 MAIN_THREAD_RENDEZVOUS => break,
258 THREAD2_JOINED => panic!(
259 "Thread 1 still running after thread 2 joined on thread 1"
261 v => unreachable!("sync state: {}", v),
263 sync_state = SYNC_STATE.load(Ordering::SeqCst);
269 static TL_DROP: TlDrop = TlDrop;
272 TL_DROP.with(|_| {});
275 match SYNC_STATE.load(Ordering::SeqCst) {
276 FRESH => thread::yield_now(),
277 THREAD2_LAUNCHED => break,
278 v => unreachable!("sync state: {}", v),
284 let jh2 = thread::Builder::new()
285 .name("thread2".into())
287 assert_eq!(SYNC_STATE.swap(THREAD2_LAUNCHED, Ordering::SeqCst), FRESH);
289 match SYNC_STATE.swap(THREAD2_JOINED, Ordering::SeqCst) {
290 MAIN_THREAD_RENDEZVOUS => return,
291 THREAD2_LAUNCHED | THREAD1_WAITING => {
292 panic!("Thread 2 running after thread 1 join before main thread rendezvous")
294 v => unreachable!("sync state: {:?}", v),
300 match SYNC_STATE.compare_exchange(
302 MAIN_THREAD_RENDEZVOUS,
307 Err(FRESH) => thread::yield_now(),
308 Err(THREAD2_LAUNCHED) => thread::yield_now(),
309 Err(THREAD2_JOINED) => {
310 panic!("Main thread rendezvous after thread 2 joined thread 1")
312 v => unreachable!("sync state: {:?}", v),