| use std::sync::OnceLock; | |
| use std::sync::atomic::AtomicUsize; | |
| use std::sync::atomic::Ordering::SeqCst; | |
| use std::sync::mpsc::channel; | |
| use std::{panic, thread}; | |
| fn spawn_and_wait<R: Send + 'static>(f: impl FnOnce() -> R + Send + 'static) -> R { | |
| thread::spawn(f).join().unwrap() | |
| } | |
| #[test] | |
| #[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads | |
| fn sync_once_cell() { | |
| static ONCE_CELL: OnceLock<i32> = OnceLock::new(); | |
| assert!(ONCE_CELL.get().is_none()); | |
| spawn_and_wait(|| { | |
| ONCE_CELL.get_or_init(|| 92); | |
| assert_eq!(ONCE_CELL.get(), Some(&92)); | |
| }); | |
| ONCE_CELL.get_or_init(|| panic!("Kaboom!")); | |
| assert_eq!(ONCE_CELL.get(), Some(&92)); | |
| } | |
| #[test] | |
| fn sync_once_cell_get_mut() { | |
| let mut c = OnceLock::new(); | |
| assert!(c.get_mut().is_none()); | |
| c.set(90).unwrap(); | |
| *c.get_mut().unwrap() += 2; | |
| assert_eq!(c.get_mut(), Some(&mut 92)); | |
| } | |
| #[test] | |
| #[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads | |
| fn sync_once_cell_drop() { | |
| static DROP_CNT: AtomicUsize = AtomicUsize::new(0); | |
| struct Dropper; | |
| impl Drop for Dropper { | |
| fn drop(&mut self) { | |
| DROP_CNT.fetch_add(1, SeqCst); | |
| } | |
| } | |
| let x = OnceLock::new(); | |
| spawn_and_wait(move || { | |
| x.get_or_init(|| Dropper); | |
| assert_eq!(DROP_CNT.load(SeqCst), 0); | |
| drop(x); | |
| }); | |
| assert_eq!(DROP_CNT.load(SeqCst), 1); | |
| } | |
| #[test] | |
| fn sync_once_cell_drop_empty() { | |
| let x = OnceLock::<String>::new(); | |
| drop(x); | |
| } | |
| #[test] | |
| fn clone() { | |
| let s = OnceLock::new(); | |
| let c = s.clone(); | |
| assert!(c.get().is_none()); | |
| s.set("hello".to_string()).unwrap(); | |
| let c = s.clone(); | |
| assert_eq!(c.get().map(String::as_str), Some("hello")); | |
| } | |
| #[test] | |
| #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] | |
| fn get_or_try_init() { | |
| let cell: OnceLock<String> = OnceLock::new(); | |
| assert!(cell.get().is_none()); | |
| if cfg!(panic = "unwind") { | |
| let res = panic::catch_unwind(|| cell.get_or_try_init(|| -> Result<_, ()> { panic!() })); | |
| assert!(res.is_err()); | |
| } | |
| assert!(cell.get().is_none()); | |
| assert_eq!(cell.get_or_try_init(|| Err(())), Err(())); | |
| assert_eq!(cell.get_or_try_init(|| Ok::<_, ()>("hello".to_string())), Ok(&"hello".to_string())); | |
| assert_eq!(cell.get(), Some(&"hello".to_string())); | |
| } | |
| #[test] | |
| fn from_impl() { | |
| assert_eq!(OnceLock::from("value").get(), Some(&"value")); | |
| assert_ne!(OnceLock::from("foo").get(), Some(&"bar")); | |
| } | |
| #[test] | |
| fn partialeq_impl() { | |
| assert!(OnceLock::from("value") == OnceLock::from("value")); | |
| assert!(OnceLock::from("foo") != OnceLock::from("bar")); | |
| assert!(OnceLock::<String>::new() == OnceLock::new()); | |
| assert!(OnceLock::<String>::new() != OnceLock::from("value".to_owned())); | |
| } | |
| #[test] | |
| fn into_inner() { | |
| let cell: OnceLock<String> = OnceLock::new(); | |
| assert_eq!(cell.into_inner(), None); | |
| let cell = OnceLock::new(); | |
| cell.set("hello".to_string()).unwrap(); | |
| assert_eq!(cell.into_inner(), Some("hello".to_string())); | |
| } | |
| #[test] | |
| fn is_sync_send() { | |
| fn assert_traits<T: Send + Sync>() {} | |
| assert_traits::<OnceLock<String>>(); | |
| } | |
| #[test] | |
| fn eval_once_macro() { | |
| macro_rules! eval_once { | |
| (|| -> $ty:ty { | |
| $($body:tt)* | |
| }) => {{ | |
| static ONCE_CELL: OnceLock<$ty> = OnceLock::new(); | |
| fn init() -> $ty { | |
| $($body)* | |
| } | |
| ONCE_CELL.get_or_init(init) | |
| }}; | |
| } | |
| let fib: &'static Vec<i32> = eval_once! { | |
| || -> Vec<i32> { | |
| let mut res = vec![1, 1]; | |
| for i in 0..10 { | |
| let next = res[i] + res[i + 1]; | |
| res.push(next); | |
| } | |
| res | |
| } | |
| }; | |
| assert_eq!(fib[5], 8) | |
| } | |
| #[test] | |
| #[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads | |
| fn sync_once_cell_does_not_leak_partially_constructed_boxes() { | |
| static ONCE_CELL: OnceLock<String> = OnceLock::new(); | |
| let n_readers = 10; | |
| let n_writers = 3; | |
| const MSG: &str = "Hello, World"; | |
| let (tx, rx) = channel(); | |
| for _ in 0..n_readers { | |
| let tx = tx.clone(); | |
| thread::spawn(move || { | |
| loop { | |
| if let Some(msg) = ONCE_CELL.get() { | |
| tx.send(msg).unwrap(); | |
| break; | |
| } | |
| #[cfg(target_env = "sgx")] | |
| std::thread::yield_now(); | |
| } | |
| }); | |
| } | |
| for _ in 0..n_writers { | |
| thread::spawn(move || { | |
| let _ = ONCE_CELL.set(MSG.to_owned()); | |
| }); | |
| } | |
| for _ in 0..n_readers { | |
| let msg = rx.recv().unwrap(); | |
| assert_eq!(msg, MSG); | |
| } | |
| } | |
| #[test] | |
| fn dropck() { | |
| let cell = OnceLock::new(); | |
| { | |
| let s = String::new(); | |
| cell.set(&s).unwrap(); | |
| } | |
| } |