ostd/sync/once.rs
1use vstd::{
2 atomic_with_ghost,
3 cell::pcell::{PCell, PointsTo},
4 modes::tracked_static_ref,
5 prelude::*,
6};
7
8use super::AtomicDataWithOwner;
9
10verus! {
11
12pub const UNINIT: u64 = 0;
13
14pub const OCCUPIED: u64 = 1;
15
16pub const INITED: u64 = 2;
17
18/// A tracked state of a¸ [`Once`] that can be used to ensure that the cell is
19/// initialized before accessing its value.
20pub tracked enum OnceState<V: 'static> {
21 /// The cell is uninitialized.
22 Uninit(PointsTo<Option<V>>),
23 /// The cell is occupied meaning it is being *written*.
24 Occupied,
25 /// The cell is initialized with a value and extended with
26 /// static lifetime.
27 Init(&'static PointsTo<Option<V>>),
28}
29
30/// A [`Predicate`] is something you're gonna preserve during the lifetime
31/// of any synchronization primitives like [`Once`].
32pub trait Predicate<V> {
33 spec fn inv(self, v: V) -> bool;
34}
35
36/// A trivial predicate that holds for any value.
37/// Use with [`OnceImpl`] when no invariant is needed.
38pub struct TrivialPred;
39
40impl<V> Predicate<V> for TrivialPred {
41 open spec fn inv(self, v: V) -> bool {
42 true
43 }
44}
45
46struct_with_invariants! {
47/// A synchronization primitive which can nominally be written to only once.
48///
49/// This type is a thread-safe [`Once`], and can be used in statics.
50/// In many simple cases, you can use [`LazyLock<T, F>`] instead to get the benefits of this type
51/// with less effort: `LazyLock<T, F>` "looks like" `&T` because it initializes with `F` on deref!
52/// Where OnceLock shines is when LazyLock is too simple to support a given case, as LazyLock
53/// doesn't allow additional inputs to its function after you call [`LazyLock::new(|| ...)`].
54///
55/// A `OnceLock` can be thought of as a safe abstraction over uninitialized data that becomes
56/// initialized once written.
57///
58/// # Examples
59///
60/// ```rust
61/// static MY_ONCE: Once<i32> = Once::new();
62///
63/// let value = MY_ONCE.get();
64/// assert(value.is_some()); // unsatisfied precondition, as MY_ONCE is uninitialized.
65/// ```
66#[verifier::reject_recursive_types(V)]
67pub struct OnceImpl<V: 'static, F: Predicate<V>> {
68 cell: PCell<Option<V>>,
69 state: vstd::atomic_ghost::AtomicU64<_, OnceState<V>, _>,
70 f: Ghost<F>,
71}
72
73#[verifier::type_invariant]
74pub closed spec fn wf(&self) -> bool {
75 invariant on state with (cell, f) is (v: u64, g: OnceState<V>) {
76 match g {
77 OnceState::Uninit(points_to) => {
78 &&& v == UNINIT
79 &&& points_to.id() == cell.id()
80 &&& points_to.value() is None
81 }
82 OnceState::Occupied => {
83 &&& v == OCCUPIED
84 }
85 OnceState::Init(points_to) => {
86 &&& v == INITED
87 &&& points_to.id() == cell.id()
88 &&& points_to.value() is Some
89 &&& f@.inv(points_to.value()->Some_0)
90 }
91 }
92 }
93}
94
95}
96
97#[verifier::external]
98unsafe impl<V, F: Predicate<V>> Send for OnceImpl<V, F> {
99
100}
101
102#[verifier::external]
103unsafe impl<V, F: Predicate<V>> Sync for OnceImpl<V, F> {
104
105}
106
107impl<V, F: Predicate<V>> OnceImpl<V, F> {
108 pub closed spec fn inv(&self) -> F {
109 self.f@
110 }
111
112 /// Creates a new uninitialized [`Once`].
113 pub const fn new(Ghost(f): Ghost<F>) -> (r: Self)
114 ensures
115 r.wf(),
116 r.inv() == f,
117 {
118 let (cell, Tracked(points_to)) = PCell::new(None);
119 let tracked state = OnceState::Uninit(points_to);
120 let state = vstd::atomic_ghost::AtomicU64::new(
121 Ghost((cell, Ghost(f))),
122 UNINIT,
123 Tracked(state),
124 );
125
126 Self { cell, state, f: Ghost(f) }
127 }
128
129 /// Initializes the [`Once`] with the given value `v`.
130 pub fn init(&self, v: V)
131 requires
132 self.inv().inv(v),
133 self.wf(),
134 {
135 let cur_state =
136 atomic_with_ghost! {
137 &self.state => load(); ghost g => {}
138 };
139
140 if cur_state != UNINIT {
141 return ;
142 } else {
143 let tracked mut points_to = None;
144 let res =
145 atomic_with_ghost! {
146 &self.state => compare_exchange(UNINIT, OCCUPIED);
147 returning res; ghost g => {
148 g = match g {
149 OnceState::Uninit(points_to_inner) => {
150 points_to = Some(points_to_inner);
151 OnceState::Occupied
152 }
153 _ => {
154 // If we are not in Uninit state, we cannot do anything.
155 g
156 }
157 }
158 }
159 };
160
161 if !res.is_err() {
162 let tracked mut points_to = points_to.tracked_unwrap();
163 self.cell.replace(Tracked(&mut points_to), Some(v));
164 // Extending the permission to static because `OnceLock` is
165 // often shared among threads and we want to ensure that
166 // the value is accessible globally.
167 let tracked static_points_to = tracked_static_ref(points_to);
168 // let tracked _ = self.inst.borrow().do_deposit(points_to, points_to, &mut token);
169 atomic_with_ghost! {
170 &self.state => store(INITED); ghost g => {
171 g = OnceState::Init(static_points_to);
172 }
173 }
174 return ;
175 } else {
176 // wait or abort.
177 return ;
178 }
179 }
180 }
181
182 /// Try to get the value stored in the [`Once`]. A [`Option::Some`]
183 /// is returned if the cell is initialized; otherwise, [`Option::None`]
184 /// is returned.
185 pub fn get<'a>(&'a self) -> (r: Option<&'a V>)
186 requires
187 self.wf(),
188 ensures
189 self.wf(),
190 r matches Some(res) ==> self.inv().inv(*res),
191 {
192 let tracked mut points_to = None;
193 let res =
194 atomic_with_ghost! {
195 &self.state => load(); ghost g => {
196 match g {
197 OnceState::Init(points_to_opt) => {
198 points_to = Some(points_to_opt);
199 }
200 _ => {}
201 }
202 }
203 };
204
205 if res == INITED {
206 let tracked points_to = points_to.tracked_unwrap();
207 let tracked static_points_to = tracked_static_ref(points_to);
208
209 self.cell.borrow(Tracked(static_points_to)).as_ref()
210 } else {
211 None
212 }
213 }
214}
215
216/// A `Once` that combines some data with a permission to access it.
217///
218/// This type alias automatically lifts the target value `V` into
219/// a wrapper [`AtomicDataWithOwner<V, Own>`] where `Own` is the
220/// permission type so that we can reason about non-trivial runtime
221/// properties in verification.
222pub type Once<V, Own, F> = OnceImpl<AtomicDataWithOwner<V, Own>, F>;
223
224} // verus!