Add "OnceList" example to motivate OnceLock · model-checking/verify-rust-std@06d9b0e (original) (raw)
`@@ -8,8 +8,14 @@ use crate::sync::Once;
`
8
8
`/// A synchronization primitive which can be written to only once.
`
9
9
`///
`
10
10
`` /// This type is a thread-safe [OnceCell
], and can be used in statics.
``
``
11
`` +
/// In many simple cases, you can use [LazyLock<T, F>
] instead to get the benefits of this type
``
``
12
`` +
/// with less effort: LazyLock<T, F>
"looks like" &T
because it initializes with F
on deref!
``
``
13
`+
/// Where OnceLock shines is when LazyLock is too simple to support a given case, as LazyLock
`
``
14
`` +
/// doesn't allow additional inputs to its function after you call [LazyLock::new(|| ...)
].
``
11
15
`///
`
12
16
`` /// [OnceCell
]: crate::cell::OnceCell
``
``
17
`` +
/// [LazyLock<T, F>
]: crate::sync::LazyLock
``
``
18
`` +
/// [LazyLock::new(|| ...)
]: crate::sync::LazyLock::new
``
13
19
`///
`
14
20
`/// # Examples
`
15
21
`///
`
`@@ -37,6 +43,55 @@ use crate::sync::Once;
`
37
43
`/// Some(&12345),
`
38
44
`/// );
`
39
45
```` /// ```
``
`46`
`+
///
`
``
`47`
`` +
/// You can use `OnceLock` to implement a type that requires "append-only" logic:
``
``
`48`
`+
///
`
``
`49`
```` +
/// ```
``
50
`+
/// use std::sync::{OnceLock, atomic::{AtomicU32, Ordering}};
`
``
51
`+
/// use std::thread;
`
``
52
`+
///
`
``
53
`+
/// struct OnceList {
`
``
54
`+
/// data: OnceLock,
`
``
55
`+
/// next: OnceLock<Box<OnceList>>,
`
``
56
`+
/// }
`
``
57
`+
/// impl OnceList {
`
``
58
`+
/// const fn new() -> OnceList {
`
``
59
`+
/// OnceList { data: OnceLock::new(), next: OnceLock::new() }
`
``
60
`+
/// }
`
``
61
`+
/// fn push(&self, value: T) {
`
``
62
`+
/// // FIXME: this impl is concise, but is also slow for long lists or many threads.
`
``
63
`+
/// // as an exercise, consider how you might improve on it while preserving the behavior
`
``
64
`+
/// if let Err(value) = self.data.set(value) {
`
``
65
`+
/// let next = self.next.get_or_init(|| Box::new(OnceList::new()));
`
``
66
`+
/// next.push(value)
`
``
67
`+
/// };
`
``
68
`+
/// }
`
``
69
`+
/// fn contains(&self, example: &T) -> bool
`
``
70
`+
/// where
`
``
71
`+
/// T: PartialEq,
`
``
72
`+
/// {
`
``
73
`+
/// self.data.get().map(|item| item == example).filter(|v| *v).unwrap_or_else(|| {
`
``
74
`+
/// self.next.get().map(|next| next.contains(example)).unwrap_or(false)
`
``
75
`+
/// })
`
``
76
`+
/// }
`
``
77
`+
/// }
`
``
78
`+
///
`
``
79
`+
/// // Let's exercise this new Sync append-only list by doing a little counting
`
``
80
`+
/// static LIST: OnceList = OnceList::new();
`
``
81
`+
/// static COUNTER: AtomicU32 = AtomicU32::new(0);
`
``
82
`+
///
`
``
83
`+
/// let vec = (0..thread::available_parallelism().unwrap().get()).map(|_| thread::spawn(|| {
`
``
84
`+
/// while let i @ 0..=1000 = COUNTER.fetch_add(1, Ordering::Relaxed) {
`
``
85
`+
/// LIST.push(i);
`
``
86
`+
/// }
`
``
87
`+
/// })).collect::<Vec<thread::JoinHandle<_>>>();
`
``
88
`+
/// vec.into_iter().for_each(|handle| handle.join().unwrap());
`
``
89
`+
///
`
``
90
`+
/// for i in 0..=1000 {
`
``
91
`+
/// assert!(LIST.contains(&i));
`
``
92
`+
/// }
`
``
93
`+
///
`
``
94
/// ```
40
95
`#[stable(feature = "once_cell", since = "1.70.0")]
`
41
96
`pub struct OnceLock {
`
42
97
`once: Once,
`