]>
Commit | Line | Data |
---|---|---|
e1599b0c XL |
1 | mod unsync { |
2 | use core::{ | |
3 | cell::Cell, | |
4 | sync::atomic::{AtomicUsize, Ordering::SeqCst}, | |
5 | }; | |
6 | ||
7 | use once_cell::unsync::{Lazy, OnceCell}; | |
8 | ||
9 | #[test] | |
10 | fn once_cell() { | |
11 | let c = OnceCell::new(); | |
12 | assert!(c.get().is_none()); | |
13 | c.get_or_init(|| 92); | |
14 | assert_eq!(c.get(), Some(&92)); | |
15 | ||
16 | c.get_or_init(|| panic!("Kabom!")); | |
17 | assert_eq!(c.get(), Some(&92)); | |
18 | } | |
19 | ||
20 | #[test] | |
21 | fn once_cell_get_mut() { | |
22 | let mut c = OnceCell::new(); | |
23 | assert!(c.get_mut().is_none()); | |
24 | c.set(90).unwrap(); | |
25 | *c.get_mut().unwrap() += 2; | |
26 | assert_eq!(c.get_mut(), Some(&mut 92)); | |
27 | } | |
28 | ||
29 | #[test] | |
30 | fn once_cell_drop() { | |
31 | static DROP_CNT: AtomicUsize = AtomicUsize::new(0); | |
32 | struct Dropper; | |
33 | impl Drop for Dropper { | |
34 | fn drop(&mut self) { | |
35 | DROP_CNT.fetch_add(1, SeqCst); | |
36 | } | |
37 | } | |
38 | ||
39 | let x = OnceCell::new(); | |
40 | x.get_or_init(|| Dropper); | |
41 | assert_eq!(DROP_CNT.load(SeqCst), 0); | |
42 | drop(x); | |
43 | assert_eq!(DROP_CNT.load(SeqCst), 1); | |
44 | } | |
45 | ||
46 | #[test] | |
47 | fn unsync_once_cell_drop_empty() { | |
48 | let x = OnceCell::<String>::new(); | |
49 | drop(x); | |
50 | } | |
51 | ||
52 | #[test] | |
53 | fn clone() { | |
54 | let s = OnceCell::new(); | |
55 | let c = s.clone(); | |
56 | assert!(c.get().is_none()); | |
57 | ||
58 | s.set("hello".to_string()).unwrap(); | |
59 | let c = s.clone(); | |
60 | assert_eq!(c.get().map(String::as_str), Some("hello")); | |
61 | } | |
62 | ||
63 | #[test] | |
64 | fn from_impl() { | |
65 | assert_eq!(OnceCell::from("value").get(), Some(&"value")); | |
66 | assert_ne!(OnceCell::from("foo").get(), Some(&"bar")); | |
67 | } | |
68 | ||
69 | #[test] | |
70 | fn partialeq_impl() { | |
71 | assert!(OnceCell::from("value") == OnceCell::from("value")); | |
72 | assert!(OnceCell::from("foo") != OnceCell::from("bar")); | |
73 | ||
74 | assert!(OnceCell::<String>::new() == OnceCell::new()); | |
75 | assert!(OnceCell::<String>::new() != OnceCell::from("value".to_owned())); | |
76 | } | |
77 | ||
78 | #[test] | |
79 | fn into_inner() { | |
80 | let cell: OnceCell<String> = OnceCell::new(); | |
81 | assert_eq!(cell.into_inner(), None); | |
82 | let cell = OnceCell::new(); | |
83 | cell.set("hello".to_string()).unwrap(); | |
84 | assert_eq!(cell.into_inner(), Some("hello".to_string())); | |
85 | } | |
86 | ||
87 | #[test] | |
88 | fn debug_impl() { | |
89 | let cell = OnceCell::new(); | |
90 | assert_eq!(format!("{:?}", cell), "OnceCell(Uninit)"); | |
91 | cell.set("hello".to_string()).unwrap(); | |
92 | assert_eq!(format!("{:?}", cell), "OnceCell(\"hello\")"); | |
93 | } | |
94 | ||
95 | #[test] | |
96 | fn lazy_new() { | |
97 | let called = Cell::new(0); | |
98 | let x = Lazy::new(|| { | |
99 | called.set(called.get() + 1); | |
100 | 92 | |
101 | }); | |
102 | ||
103 | assert_eq!(called.get(), 0); | |
104 | ||
105 | let y = *x - 30; | |
106 | assert_eq!(y, 62); | |
107 | assert_eq!(called.get(), 1); | |
108 | ||
109 | let y = *x - 30; | |
110 | assert_eq!(y, 62); | |
111 | assert_eq!(called.get(), 1); | |
112 | } | |
113 | ||
f035d41b XL |
114 | #[test] |
115 | fn lazy_deref_mut() { | |
116 | let called = Cell::new(0); | |
117 | let mut x = Lazy::new(|| { | |
118 | called.set(called.get() + 1); | |
119 | 92 | |
120 | }); | |
121 | ||
122 | assert_eq!(called.get(), 0); | |
123 | ||
124 | let y = *x - 30; | |
125 | assert_eq!(y, 62); | |
126 | assert_eq!(called.get(), 1); | |
127 | ||
128 | *x /= 2; | |
129 | assert_eq!(*x, 46); | |
130 | assert_eq!(called.get(), 1); | |
131 | } | |
132 | ||
e1599b0c XL |
133 | #[test] |
134 | fn lazy_default() { | |
135 | static CALLED: AtomicUsize = AtomicUsize::new(0); | |
136 | ||
137 | struct Foo(u8); | |
138 | impl Default for Foo { | |
139 | fn default() -> Self { | |
140 | CALLED.fetch_add(1, SeqCst); | |
141 | Foo(42) | |
142 | } | |
143 | } | |
144 | ||
145 | let lazy: Lazy<std::sync::Mutex<Foo>> = <_>::default(); | |
146 | ||
147 | assert_eq!(CALLED.load(SeqCst), 0); | |
148 | ||
149 | assert_eq!(lazy.lock().unwrap().0, 42); | |
150 | assert_eq!(CALLED.load(SeqCst), 1); | |
151 | ||
152 | lazy.lock().unwrap().0 = 21; | |
153 | ||
154 | assert_eq!(lazy.lock().unwrap().0, 21); | |
155 | assert_eq!(CALLED.load(SeqCst), 1); | |
156 | } | |
157 | ||
6a06907d XL |
158 | #[test] |
159 | fn lazy_into_value() { | |
160 | let l: Lazy<i32, _> = Lazy::new(|| panic!()); | |
161 | assert!(matches!(Lazy::into_value(l), Err(_))); | |
162 | let l = Lazy::new(|| -> i32 { 92 }); | |
163 | Lazy::force(&l); | |
164 | assert!(matches!(Lazy::into_value(l), Ok(92))); | |
165 | } | |
166 | ||
e1599b0c | 167 | #[test] |
e1599b0c XL |
168 | #[cfg(feature = "std")] |
169 | fn lazy_poisoning() { | |
170 | let x: Lazy<String> = Lazy::new(|| panic!("kaboom")); | |
171 | for _ in 0..2 { | |
172 | let res = std::panic::catch_unwind(|| x.len()); | |
173 | assert!(res.is_err()); | |
174 | } | |
175 | } | |
176 | ||
177 | #[test] | |
178 | fn aliasing_in_get() { | |
f035d41b | 179 | let x = OnceCell::new(); |
e1599b0c XL |
180 | x.set(42).unwrap(); |
181 | let at_x = x.get().unwrap(); // --- (shared) borrow of inner `Option<T>` --+ | |
182 | let _ = x.set(27); // <-- temporary (unique) borrow of inner `Option<T>` | | |
183 | println!("{}", at_x); // <------- up until here ---------------------------+ | |
184 | } | |
f035d41b XL |
185 | |
186 | #[test] | |
187 | #[should_panic(expected = "reentrant init")] | |
188 | fn reentrant_init() { | |
189 | let x: OnceCell<Box<i32>> = OnceCell::new(); | |
190 | let dangling_ref: Cell<Option<&i32>> = Cell::new(None); | |
191 | x.get_or_init(|| { | |
192 | let r = x.get_or_init(|| Box::new(92)); | |
193 | dangling_ref.set(Some(r)); | |
194 | Box::new(62) | |
195 | }); | |
196 | eprintln!("use after free: {:?}", dangling_ref.get().unwrap()); | |
197 | } | |
3dfed10e XL |
198 | |
199 | #[test] | |
200 | // https://github.com/rust-lang/rust/issues/34761#issuecomment-256320669 | |
201 | fn arrrrrrrrrrrrrrrrrrrrrr() { | |
202 | let cell = OnceCell::new(); | |
203 | { | |
204 | let s = String::new(); | |
205 | cell.set(&s).unwrap(); | |
206 | } | |
207 | } | |
e1599b0c XL |
208 | } |
209 | ||
210 | #[cfg(feature = "std")] | |
211 | mod sync { | |
212 | use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; | |
213 | ||
5869c6ff | 214 | use crossbeam_utils::thread::scope; |
e1599b0c | 215 | |
5869c6ff | 216 | use once_cell::sync::{Lazy, OnceCell}; |
e1599b0c XL |
217 | |
218 | #[test] | |
219 | fn once_cell() { | |
220 | let c = OnceCell::new(); | |
221 | assert!(c.get().is_none()); | |
222 | scope(|s| { | |
223 | s.spawn(|_| { | |
224 | c.get_or_init(|| 92); | |
225 | assert_eq!(c.get(), Some(&92)); | |
226 | }); | |
227 | }) | |
228 | .unwrap(); | |
229 | c.get_or_init(|| panic!("Kabom!")); | |
230 | assert_eq!(c.get(), Some(&92)); | |
231 | } | |
232 | ||
233 | #[test] | |
234 | fn once_cell_get_mut() { | |
235 | let mut c = OnceCell::new(); | |
236 | assert!(c.get_mut().is_none()); | |
237 | c.set(90).unwrap(); | |
238 | *c.get_mut().unwrap() += 2; | |
239 | assert_eq!(c.get_mut(), Some(&mut 92)); | |
240 | } | |
241 | ||
f035d41b XL |
242 | #[test] |
243 | fn once_cell_get_unchecked() { | |
244 | let c = OnceCell::new(); | |
245 | c.set(92).unwrap(); | |
246 | unsafe { | |
247 | assert_eq!(c.get_unchecked(), &92); | |
248 | } | |
249 | } | |
250 | ||
e1599b0c XL |
251 | #[test] |
252 | fn once_cell_drop() { | |
253 | static DROP_CNT: AtomicUsize = AtomicUsize::new(0); | |
254 | struct Dropper; | |
255 | impl Drop for Dropper { | |
256 | fn drop(&mut self) { | |
257 | DROP_CNT.fetch_add(1, SeqCst); | |
258 | } | |
259 | } | |
260 | ||
261 | let x = OnceCell::new(); | |
262 | scope(|s| { | |
263 | s.spawn(|_| { | |
264 | x.get_or_init(|| Dropper); | |
265 | assert_eq!(DROP_CNT.load(SeqCst), 0); | |
266 | drop(x); | |
267 | }); | |
268 | }) | |
269 | .unwrap(); | |
270 | assert_eq!(DROP_CNT.load(SeqCst), 1); | |
271 | } | |
272 | ||
273 | #[test] | |
274 | fn once_cell_drop_empty() { | |
275 | let x = OnceCell::<String>::new(); | |
276 | drop(x); | |
277 | } | |
278 | ||
279 | #[test] | |
280 | fn clone() { | |
281 | let s = OnceCell::new(); | |
282 | let c = s.clone(); | |
283 | assert!(c.get().is_none()); | |
284 | ||
285 | s.set("hello".to_string()).unwrap(); | |
286 | let c = s.clone(); | |
287 | assert_eq!(c.get().map(String::as_str), Some("hello")); | |
288 | } | |
289 | ||
290 | #[test] | |
e1599b0c XL |
291 | fn get_or_try_init() { |
292 | let cell: OnceCell<String> = OnceCell::new(); | |
293 | assert!(cell.get().is_none()); | |
294 | ||
295 | let res = | |
296 | std::panic::catch_unwind(|| cell.get_or_try_init(|| -> Result<_, ()> { panic!() })); | |
297 | assert!(res.is_err()); | |
298 | assert!(cell.get().is_none()); | |
299 | ||
300 | assert_eq!(cell.get_or_try_init(|| Err(())), Err(())); | |
301 | ||
302 | assert_eq!( | |
303 | cell.get_or_try_init(|| Ok::<_, ()>("hello".to_string())), | |
304 | Ok(&"hello".to_string()) | |
305 | ); | |
306 | assert_eq!(cell.get(), Some(&"hello".to_string())); | |
307 | } | |
308 | ||
309 | #[test] | |
310 | fn from_impl() { | |
311 | assert_eq!(OnceCell::from("value").get(), Some(&"value")); | |
312 | assert_ne!(OnceCell::from("foo").get(), Some(&"bar")); | |
313 | } | |
314 | ||
315 | #[test] | |
316 | fn partialeq_impl() { | |
317 | assert!(OnceCell::from("value") == OnceCell::from("value")); | |
318 | assert!(OnceCell::from("foo") != OnceCell::from("bar")); | |
319 | ||
320 | assert!(OnceCell::<String>::new() == OnceCell::new()); | |
321 | assert!(OnceCell::<String>::new() != OnceCell::from("value".to_owned())); | |
322 | } | |
323 | ||
324 | #[test] | |
325 | fn into_inner() { | |
326 | let cell: OnceCell<String> = OnceCell::new(); | |
327 | assert_eq!(cell.into_inner(), None); | |
328 | let cell = OnceCell::new(); | |
329 | cell.set("hello".to_string()).unwrap(); | |
330 | assert_eq!(cell.into_inner(), Some("hello".to_string())); | |
331 | } | |
332 | ||
333 | #[test] | |
334 | fn debug_impl() { | |
335 | let cell = OnceCell::new(); | |
336 | assert_eq!(format!("{:#?}", cell), "OnceCell(Uninit)"); | |
337 | cell.set(vec!["hello", "world"]).unwrap(); | |
338 | assert_eq!( | |
339 | format!("{:#?}", cell), | |
340 | r#"OnceCell( | |
341 | [ | |
342 | "hello", | |
343 | "world", | |
344 | ], | |
345 | )"# | |
346 | ); | |
347 | } | |
348 | ||
349 | #[test] | |
f035d41b | 350 | #[cfg_attr(miri, ignore)] // miri doesn't support processes |
e1599b0c XL |
351 | fn reentrant_init() { |
352 | let examples_dir = { | |
353 | let mut exe = std::env::current_exe().unwrap(); | |
354 | exe.pop(); | |
355 | exe.pop(); | |
356 | exe.push("examples"); | |
357 | exe | |
358 | }; | |
359 | let bin = examples_dir | |
360 | .join("reentrant_init_deadlocks") | |
361 | .with_extension(std::env::consts::EXE_EXTENSION); | |
362 | let mut guard = Guard { child: std::process::Command::new(bin).spawn().unwrap() }; | |
363 | std::thread::sleep(std::time::Duration::from_secs(2)); | |
364 | let status = guard.child.try_wait().unwrap(); | |
365 | assert!(status.is_none()); | |
366 | ||
367 | struct Guard { | |
368 | child: std::process::Child, | |
369 | } | |
370 | ||
371 | impl Drop for Guard { | |
372 | fn drop(&mut self) { | |
373 | let _ = self.child.kill(); | |
374 | } | |
375 | } | |
376 | } | |
377 | ||
378 | #[test] | |
379 | fn lazy_new() { | |
380 | let called = AtomicUsize::new(0); | |
381 | let x = Lazy::new(|| { | |
382 | called.fetch_add(1, SeqCst); | |
383 | 92 | |
384 | }); | |
385 | ||
386 | assert_eq!(called.load(SeqCst), 0); | |
387 | ||
388 | scope(|s| { | |
389 | s.spawn(|_| { | |
390 | let y = *x - 30; | |
391 | assert_eq!(y, 62); | |
392 | assert_eq!(called.load(SeqCst), 1); | |
393 | }); | |
394 | }) | |
395 | .unwrap(); | |
396 | ||
397 | let y = *x - 30; | |
398 | assert_eq!(y, 62); | |
399 | assert_eq!(called.load(SeqCst), 1); | |
400 | } | |
401 | ||
f035d41b XL |
402 | #[test] |
403 | fn lazy_deref_mut() { | |
404 | let called = AtomicUsize::new(0); | |
405 | let mut x = Lazy::new(|| { | |
406 | called.fetch_add(1, SeqCst); | |
407 | 92 | |
408 | }); | |
409 | ||
410 | assert_eq!(called.load(SeqCst), 0); | |
411 | ||
412 | let y = *x - 30; | |
413 | assert_eq!(y, 62); | |
414 | assert_eq!(called.load(SeqCst), 1); | |
415 | ||
416 | *x /= 2; | |
417 | assert_eq!(*x, 46); | |
418 | assert_eq!(called.load(SeqCst), 1); | |
419 | } | |
420 | ||
e1599b0c XL |
421 | #[test] |
422 | fn lazy_default() { | |
423 | static CALLED: AtomicUsize = AtomicUsize::new(0); | |
424 | ||
425 | struct Foo(u8); | |
426 | impl Default for Foo { | |
427 | fn default() -> Self { | |
428 | CALLED.fetch_add(1, SeqCst); | |
429 | Foo(42) | |
430 | } | |
431 | } | |
432 | ||
433 | let lazy: Lazy<std::sync::Mutex<Foo>> = <_>::default(); | |
434 | ||
435 | assert_eq!(CALLED.load(SeqCst), 0); | |
436 | ||
437 | assert_eq!(lazy.lock().unwrap().0, 42); | |
438 | assert_eq!(CALLED.load(SeqCst), 1); | |
439 | ||
440 | lazy.lock().unwrap().0 = 21; | |
441 | ||
442 | assert_eq!(lazy.lock().unwrap().0, 21); | |
443 | assert_eq!(CALLED.load(SeqCst), 1); | |
444 | } | |
445 | ||
446 | #[test] | |
e1599b0c XL |
447 | fn static_lazy() { |
448 | static XS: Lazy<Vec<i32>> = Lazy::new(|| { | |
449 | let mut xs = Vec::new(); | |
450 | xs.push(1); | |
451 | xs.push(2); | |
452 | xs.push(3); | |
453 | xs | |
454 | }); | |
455 | scope(|s| { | |
456 | s.spawn(|_| { | |
457 | assert_eq!(&*XS, &vec![1, 2, 3]); | |
458 | }); | |
459 | }) | |
460 | .unwrap(); | |
461 | assert_eq!(&*XS, &vec![1, 2, 3]); | |
462 | } | |
463 | ||
464 | #[test] | |
e1599b0c XL |
465 | fn static_lazy_via_fn() { |
466 | fn xs() -> &'static Vec<i32> { | |
467 | static XS: OnceCell<Vec<i32>> = OnceCell::new(); | |
468 | XS.get_or_init(|| { | |
469 | let mut xs = Vec::new(); | |
470 | xs.push(1); | |
471 | xs.push(2); | |
472 | xs.push(3); | |
473 | xs | |
474 | }) | |
475 | } | |
476 | assert_eq!(xs(), &vec![1, 2, 3]); | |
477 | } | |
478 | ||
6a06907d XL |
479 | #[test] |
480 | fn lazy_into_value() { | |
481 | let l: Lazy<i32, _> = Lazy::new(|| panic!()); | |
482 | assert!(matches!(Lazy::into_value(l), Err(_))); | |
483 | let l = Lazy::new(|| -> i32 { 92 }); | |
484 | Lazy::force(&l); | |
485 | assert!(matches!(Lazy::into_value(l), Ok(92))); | |
486 | } | |
487 | ||
e1599b0c | 488 | #[test] |
e1599b0c XL |
489 | fn lazy_poisoning() { |
490 | let x: Lazy<String> = Lazy::new(|| panic!("kaboom")); | |
491 | for _ in 0..2 { | |
492 | let res = std::panic::catch_unwind(|| x.len()); | |
493 | assert!(res.is_err()); | |
494 | } | |
495 | } | |
496 | ||
497 | #[test] | |
498 | fn once_cell_is_sync_send() { | |
499 | fn assert_traits<T: Send + Sync>() {} | |
500 | assert_traits::<OnceCell<String>>(); | |
501 | assert_traits::<Lazy<String>>(); | |
502 | } | |
503 | ||
504 | #[test] | |
e1599b0c XL |
505 | fn eval_once_macro() { |
506 | macro_rules! eval_once { | |
507 | (|| -> $ty:ty { | |
508 | $($body:tt)* | |
509 | }) => {{ | |
510 | static ONCE_CELL: OnceCell<$ty> = OnceCell::new(); | |
511 | fn init() -> $ty { | |
512 | $($body)* | |
513 | } | |
514 | ONCE_CELL.get_or_init(init) | |
515 | }}; | |
516 | } | |
517 | ||
518 | let fib: &'static Vec<i32> = eval_once! { | |
519 | || -> Vec<i32> { | |
520 | let mut res = vec![1, 1]; | |
521 | for i in 0..10 { | |
522 | let next = res[i] + res[i + 1]; | |
523 | res.push(next); | |
524 | } | |
525 | res | |
526 | } | |
527 | }; | |
528 | assert_eq!(fib[5], 8) | |
529 | } | |
530 | ||
531 | #[test] | |
5869c6ff | 532 | #[cfg_attr(miri, ignore)] // FIXME: deadlocks, likely caused by https://github.com/rust-lang/miri/issues/1388 |
e1599b0c XL |
533 | fn once_cell_does_not_leak_partially_constructed_boxes() { |
534 | let n_tries = 100; | |
535 | let n_readers = 10; | |
536 | let n_writers = 3; | |
537 | const MSG: &str = "Hello, World"; | |
538 | ||
539 | for _ in 0..n_tries { | |
540 | let cell: OnceCell<String> = OnceCell::new(); | |
541 | scope(|scope| { | |
542 | for _ in 0..n_readers { | |
543 | scope.spawn(|_| loop { | |
544 | if let Some(msg) = cell.get() { | |
545 | assert_eq!(msg, MSG); | |
546 | break; | |
547 | } | |
548 | }); | |
549 | } | |
550 | for _ in 0..n_writers { | |
f035d41b | 551 | let _ = scope.spawn(|_| cell.set(MSG.to_owned())); |
e1599b0c XL |
552 | } |
553 | }) | |
554 | .unwrap() | |
555 | } | |
556 | } | |
557 | ||
558 | #[test] | |
f035d41b | 559 | #[cfg_attr(miri, ignore)] // miri doesn't support Barrier |
e1599b0c XL |
560 | fn get_does_not_block() { |
561 | use std::sync::Barrier; | |
562 | ||
563 | let cell = OnceCell::new(); | |
564 | let barrier = Barrier::new(2); | |
565 | scope(|scope| { | |
566 | scope.spawn(|_| { | |
567 | cell.get_or_init(|| { | |
568 | barrier.wait(); | |
569 | barrier.wait(); | |
570 | "hello".to_string() | |
571 | }); | |
572 | }); | |
573 | barrier.wait(); | |
574 | assert_eq!(cell.get(), None); | |
575 | barrier.wait(); | |
576 | }) | |
577 | .unwrap(); | |
578 | assert_eq!(cell.get(), Some(&"hello".to_string())); | |
579 | } | |
3dfed10e XL |
580 | |
581 | #[test] | |
582 | // https://github.com/rust-lang/rust/issues/34761#issuecomment-256320669 | |
583 | fn arrrrrrrrrrrrrrrrrrrrrr() { | |
584 | let cell = OnceCell::new(); | |
585 | { | |
586 | let s = String::new(); | |
587 | cell.set(&s).unwrap(); | |
588 | } | |
589 | } | |
e1599b0c | 590 | } |
5869c6ff | 591 | |
6a06907d | 592 | #[cfg(feature = "race")] |
5869c6ff XL |
593 | mod race { |
594 | use std::{ | |
595 | num::NonZeroUsize, | |
596 | sync::{ | |
597 | atomic::{AtomicUsize, Ordering::SeqCst}, | |
598 | Barrier, | |
599 | }, | |
600 | }; | |
601 | ||
602 | use crossbeam_utils::thread::scope; | |
603 | ||
604 | use once_cell::race::{OnceBool, OnceNonZeroUsize}; | |
605 | ||
606 | #[test] | |
607 | fn once_non_zero_usize_smoke_test() { | |
608 | let cnt = AtomicUsize::new(0); | |
609 | let cell = OnceNonZeroUsize::new(); | |
610 | let val = NonZeroUsize::new(92).unwrap(); | |
611 | scope(|s| { | |
612 | s.spawn(|_| { | |
613 | assert_eq!( | |
614 | cell.get_or_init(|| { | |
615 | cnt.fetch_add(1, SeqCst); | |
616 | val | |
617 | }), | |
618 | val | |
619 | ); | |
620 | assert_eq!(cnt.load(SeqCst), 1); | |
621 | ||
622 | assert_eq!( | |
623 | cell.get_or_init(|| { | |
624 | cnt.fetch_add(1, SeqCst); | |
625 | val | |
626 | }), | |
627 | val | |
628 | ); | |
629 | assert_eq!(cnt.load(SeqCst), 1); | |
630 | }); | |
631 | }) | |
632 | .unwrap(); | |
633 | assert_eq!(cell.get(), Some(val)); | |
634 | assert_eq!(cnt.load(SeqCst), 1); | |
635 | } | |
636 | ||
637 | #[test] | |
638 | fn once_non_zero_usize_set() { | |
639 | let val1 = NonZeroUsize::new(92).unwrap(); | |
640 | let val2 = NonZeroUsize::new(62).unwrap(); | |
641 | ||
642 | let cell = OnceNonZeroUsize::new(); | |
643 | ||
644 | assert!(cell.set(val1).is_ok()); | |
645 | assert_eq!(cell.get(), Some(val1)); | |
646 | ||
647 | assert!(cell.set(val2).is_err()); | |
648 | assert_eq!(cell.get(), Some(val1)); | |
649 | } | |
650 | ||
651 | #[test] | |
652 | fn once_non_zero_usize_first_wins() { | |
653 | let val1 = NonZeroUsize::new(92).unwrap(); | |
654 | let val2 = NonZeroUsize::new(62).unwrap(); | |
655 | ||
656 | let cell = OnceNonZeroUsize::new(); | |
657 | ||
658 | let b1 = Barrier::new(2); | |
659 | let b2 = Barrier::new(2); | |
660 | let b3 = Barrier::new(2); | |
661 | scope(|s| { | |
662 | s.spawn(|_| { | |
663 | let r1 = cell.get_or_init(|| { | |
664 | b1.wait(); | |
665 | b2.wait(); | |
666 | val1 | |
667 | }); | |
668 | assert_eq!(r1, val1); | |
669 | b3.wait(); | |
670 | }); | |
671 | b1.wait(); | |
672 | s.spawn(|_| { | |
673 | let r2 = cell.get_or_init(|| { | |
674 | b2.wait(); | |
675 | b3.wait(); | |
676 | val2 | |
677 | }); | |
678 | assert_eq!(r2, val1); | |
679 | }); | |
680 | }) | |
681 | .unwrap(); | |
682 | ||
683 | assert_eq!(cell.get(), Some(val1)); | |
684 | } | |
685 | ||
686 | #[test] | |
687 | fn once_bool_smoke_test() { | |
688 | let cnt = AtomicUsize::new(0); | |
689 | let cell = OnceBool::new(); | |
690 | scope(|s| { | |
691 | s.spawn(|_| { | |
692 | assert_eq!( | |
693 | cell.get_or_init(|| { | |
694 | cnt.fetch_add(1, SeqCst); | |
695 | false | |
696 | }), | |
697 | false | |
698 | ); | |
699 | assert_eq!(cnt.load(SeqCst), 1); | |
700 | ||
701 | assert_eq!( | |
702 | cell.get_or_init(|| { | |
703 | cnt.fetch_add(1, SeqCst); | |
704 | false | |
705 | }), | |
706 | false | |
707 | ); | |
708 | assert_eq!(cnt.load(SeqCst), 1); | |
709 | }); | |
710 | }) | |
711 | .unwrap(); | |
712 | assert_eq!(cell.get(), Some(false)); | |
713 | assert_eq!(cnt.load(SeqCst), 1); | |
714 | } | |
715 | ||
716 | #[test] | |
717 | fn once_bool_set() { | |
718 | let cell = OnceBool::new(); | |
719 | ||
720 | assert!(cell.set(false).is_ok()); | |
721 | assert_eq!(cell.get(), Some(false)); | |
722 | ||
723 | assert!(cell.set(true).is_err()); | |
724 | assert_eq!(cell.get(), Some(false)); | |
725 | } | |
726 | } | |
727 | ||
6a06907d | 728 | #[cfg(all(feature = "race", feature = "alloc"))] |
5869c6ff XL |
729 | mod race_once_box { |
730 | use std::sync::{ | |
731 | atomic::{AtomicUsize, Ordering::SeqCst}, | |
732 | Arc, Barrier, | |
733 | }; | |
734 | ||
735 | use crossbeam_utils::thread::scope; | |
736 | use once_cell::race::OnceBox; | |
737 | ||
738 | #[derive(Default)] | |
739 | struct Heap { | |
740 | total: Arc<AtomicUsize>, | |
741 | } | |
742 | ||
743 | #[derive(Debug)] | |
744 | struct Pebble<T> { | |
745 | val: T, | |
746 | total: Arc<AtomicUsize>, | |
747 | } | |
748 | ||
749 | impl<T> Drop for Pebble<T> { | |
750 | fn drop(&mut self) { | |
751 | self.total.fetch_sub(1, SeqCst); | |
752 | } | |
753 | } | |
754 | ||
755 | impl Heap { | |
756 | fn total(&self) -> usize { | |
757 | self.total.load(SeqCst) | |
758 | } | |
759 | fn new_pebble<T>(&self, val: T) -> Pebble<T> { | |
760 | self.total.fetch_add(1, SeqCst); | |
761 | Pebble { val, total: Arc::clone(&self.total) } | |
762 | } | |
763 | } | |
764 | ||
765 | #[test] | |
766 | fn once_box_smoke_test() { | |
767 | let heap = Heap::default(); | |
768 | let global_cnt = AtomicUsize::new(0); | |
769 | let cell = OnceBox::new(); | |
770 | let b = Barrier::new(128); | |
771 | scope(|s| { | |
772 | for _ in 0..128 { | |
773 | s.spawn(|_| { | |
774 | let local_cnt = AtomicUsize::new(0); | |
775 | cell.get_or_init(|| { | |
776 | global_cnt.fetch_add(1, SeqCst); | |
777 | local_cnt.fetch_add(1, SeqCst); | |
778 | b.wait(); | |
779 | Box::new(heap.new_pebble(())) | |
780 | }); | |
781 | assert_eq!(local_cnt.load(SeqCst), 1); | |
782 | ||
783 | cell.get_or_init(|| { | |
784 | global_cnt.fetch_add(1, SeqCst); | |
785 | local_cnt.fetch_add(1, SeqCst); | |
786 | Box::new(heap.new_pebble(())) | |
787 | }); | |
788 | assert_eq!(local_cnt.load(SeqCst), 1); | |
789 | }); | |
790 | } | |
791 | }) | |
792 | .unwrap(); | |
793 | assert!(cell.get().is_some()); | |
794 | assert!(global_cnt.load(SeqCst) > 10); | |
795 | ||
796 | assert_eq!(heap.total(), 1); | |
797 | drop(cell); | |
798 | assert_eq!(heap.total(), 0); | |
799 | } | |
800 | ||
801 | #[test] | |
802 | fn once_box_set() { | |
803 | let heap = Heap::default(); | |
804 | let cell = OnceBox::new(); | |
805 | assert!(cell.get().is_none()); | |
806 | ||
807 | assert!(cell.set(Box::new(heap.new_pebble("hello"))).is_ok()); | |
808 | assert_eq!(cell.get().unwrap().val, "hello"); | |
809 | assert_eq!(heap.total(), 1); | |
810 | ||
811 | assert!(cell.set(Box::new(heap.new_pebble("world"))).is_err()); | |
812 | assert_eq!(cell.get().unwrap().val, "hello"); | |
813 | assert_eq!(heap.total(), 1); | |
814 | ||
815 | drop(cell); | |
816 | assert_eq!(heap.total(), 0); | |
817 | } | |
818 | ||
819 | #[test] | |
820 | fn once_box_first_wins() { | |
821 | let cell = OnceBox::new(); | |
822 | let val1 = 92; | |
823 | let val2 = 62; | |
824 | ||
825 | let b1 = Barrier::new(2); | |
826 | let b2 = Barrier::new(2); | |
827 | let b3 = Barrier::new(2); | |
828 | scope(|s| { | |
829 | s.spawn(|_| { | |
830 | let r1 = cell.get_or_init(|| { | |
831 | b1.wait(); | |
832 | b2.wait(); | |
833 | Box::new(val1) | |
834 | }); | |
835 | assert_eq!(*r1, val1); | |
836 | b3.wait(); | |
837 | }); | |
838 | b1.wait(); | |
839 | s.spawn(|_| { | |
840 | let r2 = cell.get_or_init(|| { | |
841 | b2.wait(); | |
842 | b3.wait(); | |
843 | Box::new(val2) | |
844 | }); | |
845 | assert_eq!(*r2, val1); | |
846 | }); | |
847 | }) | |
848 | .unwrap(); | |
849 | ||
850 | assert_eq!(cell.get(), Some(&val1)); | |
851 | } | |
852 | ||
853 | #[test] | |
854 | fn once_box_reentrant() { | |
855 | let cell = OnceBox::new(); | |
856 | let res = cell.get_or_init(|| { | |
857 | cell.get_or_init(|| Box::new("hello".to_string())); | |
858 | Box::new("world".to_string()) | |
859 | }); | |
860 | assert_eq!(res, "hello"); | |
861 | } | |
6a06907d XL |
862 | |
863 | #[test] | |
864 | fn once_box_default() { | |
865 | struct Foo; | |
866 | ||
867 | let cell: OnceBox<Foo> = Default::default(); | |
868 | assert!(cell.get().is_none()); | |
869 | } | |
5869c6ff | 870 | } |