1 #![deny(warnings, rust_2018_idioms)]
2 use loom::thread;
3 use std::cell::RefCell;
4 use std::sync::atomic::{AtomicUsize, Ordering};
5 
6 #[test]
thread_local()7 fn thread_local() {
8     loom::thread_local! {
9         static THREAD_LOCAL: RefCell<usize> = RefCell::new(1);
10     }
11 
12     fn do_test(n: usize) {
13         THREAD_LOCAL.with(|local| {
14             assert_eq!(*local.borrow(), 1);
15         });
16         THREAD_LOCAL.with(|local| {
17             assert_eq!(*local.borrow(), 1);
18             *local.borrow_mut() = n;
19             assert_eq!(*local.borrow(), n);
20         });
21         THREAD_LOCAL.with(|local| {
22             assert_eq!(*local.borrow(), n);
23         });
24     }
25 
26     loom::model(|| {
27         let t1 = thread::spawn(|| do_test(2));
28 
29         let t2 = thread::spawn(|| do_test(3));
30 
31         do_test(4);
32 
33         t1.join().unwrap();
34         t2.join().unwrap();
35     });
36 }
37 
38 #[test]
nested_with()39 fn nested_with() {
40     loom::thread_local! {
41         static LOCAL1: RefCell<usize> = RefCell::new(1);
42         static LOCAL2: RefCell<usize> = RefCell::new(2);
43     }
44 
45     loom::model(|| {
46         LOCAL1.with(|local1| *local1.borrow_mut() = LOCAL2.with(|local2| local2.borrow().clone()));
47     });
48 }
49 
50 #[test]
drop()51 fn drop() {
52     static DROPS: AtomicUsize = AtomicUsize::new(0);
53 
54     struct CountDrops {
55         drops: &'static AtomicUsize,
56         dummy: bool,
57     }
58 
59     impl Drop for CountDrops {
60         fn drop(&mut self) {
61             self.drops.fetch_add(1, Ordering::Release);
62         }
63     }
64 
65     impl CountDrops {
66         fn new(drops: &'static AtomicUsize) -> Self {
67             Self { drops, dummy: true }
68         }
69     }
70 
71     loom::thread_local! {
72         static DROPPED_LOCAL: CountDrops = CountDrops::new(&DROPS);
73     }
74 
75     loom::model(|| {
76         assert_eq!(DROPS.load(Ordering::Acquire), 0);
77 
78         thread::spawn(|| {
79             // force access to the thread local so that it's initialized.
80             DROPPED_LOCAL.with(|local| assert!(local.dummy));
81             assert_eq!(DROPS.load(Ordering::Acquire), 0);
82         })
83         .join()
84         .unwrap();
85 
86         // When the first spawned thread completed, its copy of the thread local
87         // should have been dropped.
88         assert_eq!(DROPS.load(Ordering::Acquire), 1);
89 
90         thread::spawn(|| {
91             // force access to the thread local so that it's initialized.
92             DROPPED_LOCAL.with(|local| assert!(local.dummy));
93             assert_eq!(DROPS.load(Ordering::Acquire), 1);
94         })
95         .join()
96         .unwrap();
97 
98         // When the second spawned thread completed, its copy of the thread local
99         // should have been dropped as well.
100         assert_eq!(DROPS.load(Ordering::Acquire), 2);
101 
102         // force access to the thread local so that it's initialized.
103         DROPPED_LOCAL.with(|local| assert!(local.dummy));
104     });
105 
106     // Finally, when the model's "main thread" completes, its copy of the local
107     // should also be dropped.
108     assert_eq!(DROPS.load(Ordering::Acquire), 3);
109 }
110