1 use std::mem;
2 use std::sync::atomic::AtomicUsize;
3 use std::sync::atomic::Ordering::SeqCst;
4 
5 use crossbeam_utils::atomic::AtomicCell;
6 
7 #[test]
is_lock_free()8 fn is_lock_free() {
9     struct UsizeWrap(#[allow(dead_code)] usize);
10     struct U8Wrap(#[allow(dead_code)] bool);
11     struct I16Wrap(#[allow(dead_code)] i16);
12     #[repr(align(8))]
13     struct U64Align8(#[allow(dead_code)] u64);
14 
15     assert!(AtomicCell::<usize>::is_lock_free());
16     assert!(AtomicCell::<isize>::is_lock_free());
17     assert!(AtomicCell::<UsizeWrap>::is_lock_free());
18 
19     assert!(AtomicCell::<()>::is_lock_free());
20 
21     assert!(AtomicCell::<u8>::is_lock_free());
22     assert!(AtomicCell::<i8>::is_lock_free());
23     assert!(AtomicCell::<bool>::is_lock_free());
24     assert!(AtomicCell::<U8Wrap>::is_lock_free());
25 
26     assert!(AtomicCell::<u16>::is_lock_free());
27     assert!(AtomicCell::<i16>::is_lock_free());
28     assert!(AtomicCell::<I16Wrap>::is_lock_free());
29 
30     assert!(AtomicCell::<u32>::is_lock_free());
31     assert!(AtomicCell::<i32>::is_lock_free());
32 
33     // Sizes of both types must be equal, and the alignment of `u64` must be greater or equal than
34     // that of `AtomicU64`. In i686-unknown-linux-gnu, the alignment of `u64` is `4` and alignment
35     // of `AtomicU64` is `8`, so `AtomicCell<u64>` is not lock-free.
36     assert_eq!(
37         AtomicCell::<u64>::is_lock_free(),
38         cfg!(target_has_atomic = "64") && std::mem::align_of::<u64>() == 8
39     );
40     assert_eq!(mem::size_of::<U64Align8>(), 8);
41     assert_eq!(mem::align_of::<U64Align8>(), 8);
42     assert_eq!(
43         AtomicCell::<U64Align8>::is_lock_free(),
44         cfg!(target_has_atomic = "64")
45     );
46 
47     // AtomicU128 is unstable
48     assert!(!AtomicCell::<u128>::is_lock_free());
49 }
50 
51 #[test]
const_is_lock_free()52 fn const_is_lock_free() {
53     const _U: bool = AtomicCell::<usize>::is_lock_free();
54     const _I: bool = AtomicCell::<isize>::is_lock_free();
55 }
56 
57 #[test]
drops_unit()58 fn drops_unit() {
59     static CNT: AtomicUsize = AtomicUsize::new(0);
60     CNT.store(0, SeqCst);
61 
62     #[derive(Debug, PartialEq, Eq)]
63     struct Foo();
64 
65     impl Foo {
66         fn new() -> Foo {
67             CNT.fetch_add(1, SeqCst);
68             Foo()
69         }
70     }
71 
72     impl Drop for Foo {
73         fn drop(&mut self) {
74             CNT.fetch_sub(1, SeqCst);
75         }
76     }
77 
78     impl Default for Foo {
79         fn default() -> Foo {
80             Foo::new()
81         }
82     }
83 
84     let a = AtomicCell::new(Foo::new());
85 
86     assert_eq!(a.swap(Foo::new()), Foo::new());
87     assert_eq!(CNT.load(SeqCst), 1);
88 
89     a.store(Foo::new());
90     assert_eq!(CNT.load(SeqCst), 1);
91 
92     assert_eq!(a.swap(Foo::default()), Foo::new());
93     assert_eq!(CNT.load(SeqCst), 1);
94 
95     drop(a);
96     assert_eq!(CNT.load(SeqCst), 0);
97 }
98 
99 #[test]
drops_u8()100 fn drops_u8() {
101     static CNT: AtomicUsize = AtomicUsize::new(0);
102     CNT.store(0, SeqCst);
103 
104     #[derive(Debug, PartialEq, Eq)]
105     struct Foo(u8);
106 
107     impl Foo {
108         fn new(val: u8) -> Foo {
109             CNT.fetch_add(1, SeqCst);
110             Foo(val)
111         }
112     }
113 
114     impl Drop for Foo {
115         fn drop(&mut self) {
116             CNT.fetch_sub(1, SeqCst);
117         }
118     }
119 
120     impl Default for Foo {
121         fn default() -> Foo {
122             Foo::new(0)
123         }
124     }
125 
126     let a = AtomicCell::new(Foo::new(5));
127 
128     assert_eq!(a.swap(Foo::new(6)), Foo::new(5));
129     assert_eq!(a.swap(Foo::new(1)), Foo::new(6));
130     assert_eq!(CNT.load(SeqCst), 1);
131 
132     a.store(Foo::new(2));
133     assert_eq!(CNT.load(SeqCst), 1);
134 
135     assert_eq!(a.swap(Foo::default()), Foo::new(2));
136     assert_eq!(CNT.load(SeqCst), 1);
137 
138     assert_eq!(a.swap(Foo::default()), Foo::new(0));
139     assert_eq!(CNT.load(SeqCst), 1);
140 
141     drop(a);
142     assert_eq!(CNT.load(SeqCst), 0);
143 }
144 
145 #[test]
drops_usize()146 fn drops_usize() {
147     static CNT: AtomicUsize = AtomicUsize::new(0);
148     CNT.store(0, SeqCst);
149 
150     #[derive(Debug, PartialEq, Eq)]
151     struct Foo(usize);
152 
153     impl Foo {
154         fn new(val: usize) -> Foo {
155             CNT.fetch_add(1, SeqCst);
156             Foo(val)
157         }
158     }
159 
160     impl Drop for Foo {
161         fn drop(&mut self) {
162             CNT.fetch_sub(1, SeqCst);
163         }
164     }
165 
166     impl Default for Foo {
167         fn default() -> Foo {
168             Foo::new(0)
169         }
170     }
171 
172     let a = AtomicCell::new(Foo::new(5));
173 
174     assert_eq!(a.swap(Foo::new(6)), Foo::new(5));
175     assert_eq!(a.swap(Foo::new(1)), Foo::new(6));
176     assert_eq!(CNT.load(SeqCst), 1);
177 
178     a.store(Foo::new(2));
179     assert_eq!(CNT.load(SeqCst), 1);
180 
181     assert_eq!(a.swap(Foo::default()), Foo::new(2));
182     assert_eq!(CNT.load(SeqCst), 1);
183 
184     assert_eq!(a.swap(Foo::default()), Foo::new(0));
185     assert_eq!(CNT.load(SeqCst), 1);
186 
187     drop(a);
188     assert_eq!(CNT.load(SeqCst), 0);
189 }
190 
191 #[test]
modular_u8()192 fn modular_u8() {
193     #[derive(Clone, Copy, Eq, Debug, Default)]
194     struct Foo(u8);
195 
196     impl PartialEq for Foo {
197         fn eq(&self, other: &Foo) -> bool {
198             self.0 % 5 == other.0 % 5
199         }
200     }
201 
202     let a = AtomicCell::new(Foo(1));
203 
204     assert_eq!(a.load(), Foo(1));
205     assert_eq!(a.swap(Foo(2)), Foo(11));
206     assert_eq!(a.load(), Foo(52));
207 
208     a.store(Foo(0));
209     assert_eq!(a.compare_exchange(Foo(0), Foo(5)), Ok(Foo(100)));
210     assert_eq!(a.load().0, 5);
211     assert_eq!(a.compare_exchange(Foo(10), Foo(15)), Ok(Foo(100)));
212     assert_eq!(a.load().0, 15);
213 }
214 
215 #[test]
modular_usize()216 fn modular_usize() {
217     #[derive(Clone, Copy, Eq, Debug, Default)]
218     struct Foo(usize);
219 
220     impl PartialEq for Foo {
221         fn eq(&self, other: &Foo) -> bool {
222             self.0 % 5 == other.0 % 5
223         }
224     }
225 
226     let a = AtomicCell::new(Foo(1));
227 
228     assert_eq!(a.load(), Foo(1));
229     assert_eq!(a.swap(Foo(2)), Foo(11));
230     assert_eq!(a.load(), Foo(52));
231 
232     a.store(Foo(0));
233     assert_eq!(a.compare_exchange(Foo(0), Foo(5)), Ok(Foo(100)));
234     assert_eq!(a.load().0, 5);
235     assert_eq!(a.compare_exchange(Foo(10), Foo(15)), Ok(Foo(100)));
236     assert_eq!(a.load().0, 15);
237 }
238 
239 #[test]
garbage_padding()240 fn garbage_padding() {
241     #[derive(Copy, Clone, Eq, PartialEq)]
242     struct Object {
243         a: i64,
244         b: i32,
245     }
246 
247     let cell = AtomicCell::new(Object { a: 0, b: 0 });
248     let _garbage = [0xfe, 0xfe, 0xfe, 0xfe, 0xfe]; // Needed
249     let next = Object { a: 0, b: 0 };
250 
251     let prev = cell.load();
252     assert!(cell.compare_exchange(prev, next).is_ok());
253     println!();
254 }
255 
256 #[test]
const_atomic_cell_new()257 fn const_atomic_cell_new() {
258     static CELL: AtomicCell<usize> = AtomicCell::new(0);
259 
260     CELL.store(1);
261     assert_eq!(CELL.load(), 1);
262 }
263 
264 // https://github.com/crossbeam-rs/crossbeam/pull/767
265 macro_rules! test_arithmetic {
266     ($test_name:ident, $ty:ident) => {
267         #[test]
268         fn $test_name() {
269             let a: AtomicCell<$ty> = AtomicCell::new(7);
270 
271             assert_eq!(a.fetch_add(3), 7);
272             assert_eq!(a.load(), 10);
273 
274             assert_eq!(a.fetch_sub(3), 10);
275             assert_eq!(a.load(), 7);
276 
277             assert_eq!(a.fetch_and(3), 7);
278             assert_eq!(a.load(), 3);
279 
280             assert_eq!(a.fetch_or(16), 3);
281             assert_eq!(a.load(), 19);
282 
283             assert_eq!(a.fetch_xor(2), 19);
284             assert_eq!(a.load(), 17);
285 
286             assert_eq!(a.fetch_max(18), 17);
287             assert_eq!(a.load(), 18);
288 
289             assert_eq!(a.fetch_min(17), 18);
290             assert_eq!(a.load(), 17);
291 
292             assert_eq!(a.fetch_nand(7), 17);
293             assert_eq!(a.load(), !(17 & 7));
294         }
295     };
296 }
297 test_arithmetic!(arithmetic_u8, u8);
298 test_arithmetic!(arithmetic_i8, i8);
299 test_arithmetic!(arithmetic_u16, u16);
300 test_arithmetic!(arithmetic_i16, i16);
301 test_arithmetic!(arithmetic_u32, u32);
302 test_arithmetic!(arithmetic_i32, i32);
303 test_arithmetic!(arithmetic_u64, u64);
304 test_arithmetic!(arithmetic_i64, i64);
305 test_arithmetic!(arithmetic_u128, u128);
306 test_arithmetic!(arithmetic_i128, i128);
307 
308 // https://github.com/crossbeam-rs/crossbeam/issues/748
309 #[cfg_attr(miri, ignore)] // TODO
310 #[test]
issue_748()311 fn issue_748() {
312     #[allow(dead_code)]
313     #[repr(align(8))]
314     #[derive(Debug, Clone, Copy, PartialEq, Eq)]
315     enum Test {
316         Field(u32),
317         FieldLess,
318     }
319 
320     assert_eq!(mem::size_of::<Test>(), 8);
321     assert_eq!(
322         AtomicCell::<Test>::is_lock_free(),
323         cfg!(target_has_atomic = "64")
324     );
325     let x = AtomicCell::new(Test::FieldLess);
326     assert_eq!(x.load(), Test::FieldLess);
327 }
328 
329 // https://github.com/crossbeam-rs/crossbeam/issues/833
330 #[test]
issue_833()331 fn issue_833() {
332     use std::num::NonZeroU128;
333     use std::sync::atomic::{AtomicBool, Ordering};
334     use std::thread;
335 
336     #[cfg(miri)]
337     const N: usize = 10_000;
338     #[cfg(not(miri))]
339     const N: usize = 1_000_000;
340 
341     #[allow(dead_code)]
342     enum Enum {
343         NeverConstructed,
344         Cell(AtomicCell<NonZeroU128>),
345     }
346 
347     static STATIC: Enum = Enum::Cell(AtomicCell::new(match NonZeroU128::new(1) {
348         Some(nonzero) => nonzero,
349         None => unreachable!(),
350     }));
351     static FINISHED: AtomicBool = AtomicBool::new(false);
352 
353     let handle = thread::spawn(|| {
354         let cell = match &STATIC {
355             Enum::NeverConstructed => unreachable!(),
356             Enum::Cell(cell) => cell,
357         };
358         let x = NonZeroU128::new(0xFFFF_FFFF_FFFF_FFFF_0000_0000_0000_0000).unwrap();
359         let y = NonZeroU128::new(0x0000_0000_0000_0000_FFFF_FFFF_FFFF_FFFF).unwrap();
360         while !FINISHED.load(Ordering::Relaxed) {
361             cell.store(x);
362             cell.store(y);
363         }
364     });
365 
366     for _ in 0..N {
367         if let Enum::NeverConstructed = STATIC {
368             unreachable!(":(");
369         }
370     }
371 
372     FINISHED.store(true, Ordering::Relaxed);
373     handle.join().unwrap();
374 }
375