1#[cfg(feature = "std")]
2use std::sync::Barrier;
3use std::sync::{
4 atomic::{AtomicUsize, Ordering::SeqCst},
5 Arc,
6};
7
8use once_cell::race::OnceBox;
9
10#[derive(Default)]
11struct Heap {
12 total: Arc<AtomicUsize>,
13}
14
15#[derive(Debug)]
16struct Pebble<T> {
17 val: T,
18 total: Arc<AtomicUsize>,
19}
20
21impl<T> Drop for Pebble<T> {
22 fn drop(&mut self) {
23 self.total.fetch_sub(1, SeqCst);
24 }
25}
26
27impl 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]
39fn 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]
76fn 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]
95fn 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]
130fn 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]
140fn once_box_default() {
141 struct Foo;
142
143 let cell: OnceBox<Foo> = Default::default();
144 assert!(cell.get().is_none());
145}
146