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