1 | #[cfg (feature = "std" )] |
2 | use std::sync::Barrier; |
3 | use std::sync::{ |
4 | atomic::{AtomicUsize, Ordering::SeqCst}, |
5 | Arc, |
6 | }; |
7 | |
8 | use once_cell::race::OnceBox; |
9 | |
10 | #[derive(Default)] |
11 | struct Heap { |
12 | total: Arc<AtomicUsize>, |
13 | } |
14 | |
15 | #[derive(Debug)] |
16 | struct Pebble<T> { |
17 | val: T, |
18 | total: Arc<AtomicUsize>, |
19 | } |
20 | |
21 | impl<T> Drop for Pebble<T> { |
22 | fn drop(&mut self) { |
23 | self.total.fetch_sub(1, SeqCst); |
24 | } |
25 | } |
26 | |
27 | impl Heap { |
28 | fn total(&self) -> usize { |
29 | self.total.load(SeqCst) |
30 | } |
31 | fn new_pebble<T>(&self, val: T) -> Pebble<T> { |
32 | self.total.fetch_add(1, SeqCst); |
33 | Pebble { val, total: Arc::clone(&self.total) } |
34 | } |
35 | } |
36 | |
37 | #[cfg (feature = "std" )] |
38 | #[test] |
39 | fn once_box_smoke_test() { |
40 | use std::thread::scope; |
41 | |
42 | let heap = Heap::default(); |
43 | let global_cnt = AtomicUsize::new(0); |
44 | let cell = OnceBox::new(); |
45 | let b = Barrier::new(128); |
46 | scope(|s| { |
47 | for _ in 0..128 { |
48 | s.spawn(|| { |
49 | let local_cnt = AtomicUsize::new(0); |
50 | cell.get_or_init(|| { |
51 | global_cnt.fetch_add(1, SeqCst); |
52 | local_cnt.fetch_add(1, SeqCst); |
53 | b.wait(); |
54 | Box::new(heap.new_pebble(())) |
55 | }); |
56 | assert_eq!(local_cnt.load(SeqCst), 1); |
57 | |
58 | cell.get_or_init(|| { |
59 | global_cnt.fetch_add(1, SeqCst); |
60 | local_cnt.fetch_add(1, SeqCst); |
61 | Box::new(heap.new_pebble(())) |
62 | }); |
63 | assert_eq!(local_cnt.load(SeqCst), 1); |
64 | }); |
65 | } |
66 | }); |
67 | assert!(cell.get().is_some()); |
68 | assert!(global_cnt.load(SeqCst) > 10); |
69 | |
70 | assert_eq!(heap.total(), 1); |
71 | drop(cell); |
72 | assert_eq!(heap.total(), 0); |
73 | } |
74 | |
75 | #[test] |
76 | fn once_box_set() { |
77 | let heap = Heap::default(); |
78 | let cell = OnceBox::new(); |
79 | assert!(cell.get().is_none()); |
80 | |
81 | assert!(cell.set(Box::new(heap.new_pebble("hello" ))).is_ok()); |
82 | assert_eq!(cell.get().unwrap().val, "hello" ); |
83 | assert_eq!(heap.total(), 1); |
84 | |
85 | assert!(cell.set(Box::new(heap.new_pebble("world" ))).is_err()); |
86 | assert_eq!(cell.get().unwrap().val, "hello" ); |
87 | assert_eq!(heap.total(), 1); |
88 | |
89 | drop(cell); |
90 | assert_eq!(heap.total(), 0); |
91 | } |
92 | |
93 | #[cfg (feature = "std" )] |
94 | #[test] |
95 | fn once_box_first_wins() { |
96 | use std::thread::scope; |
97 | |
98 | let cell = OnceBox::new(); |
99 | let val1 = 92; |
100 | let val2 = 62; |
101 | |
102 | let b1 = Barrier::new(2); |
103 | let b2 = Barrier::new(2); |
104 | let b3 = Barrier::new(2); |
105 | scope(|s| { |
106 | s.spawn(|| { |
107 | let r1 = cell.get_or_init(|| { |
108 | b1.wait(); |
109 | b2.wait(); |
110 | Box::new(val1) |
111 | }); |
112 | assert_eq!(*r1, val1); |
113 | b3.wait(); |
114 | }); |
115 | b1.wait(); |
116 | s.spawn(|| { |
117 | let r2 = cell.get_or_init(|| { |
118 | b2.wait(); |
119 | b3.wait(); |
120 | Box::new(val2) |
121 | }); |
122 | assert_eq!(*r2, val1); |
123 | }); |
124 | }); |
125 | |
126 | assert_eq!(cell.get(), Some(&val1)); |
127 | } |
128 | |
129 | #[test] |
130 | fn once_box_reentrant() { |
131 | let cell = OnceBox::new(); |
132 | let res = cell.get_or_init(|| { |
133 | cell.get_or_init(|| Box::new("hello" .to_string())); |
134 | Box::new("world" .to_string()) |
135 | }); |
136 | assert_eq!(res, "hello" ); |
137 | } |
138 | |
139 | #[test] |
140 | fn once_box_default() { |
141 | struct Foo; |
142 | |
143 | let cell: OnceBox<Foo> = Default::default(); |
144 | assert!(cell.get().is_none()); |
145 | } |
146 | |