1#[cfg(feature = "irc11")]
2use vstd::thread_view::Objective;
3use vstd::{
4 atomic_with_ghost,
5 cell::pcell::{PCell, PointsTo},
6 modes::tracked_static_ref,
7 prelude::*,
8};
9
10use super::AtomicDataWithOwner;
11
12verus! {
13
14pub const UNINIT: u64 = 0;
15
16pub const OCCUPIED: u64 = 1;
17
18pub const INITED: u64 = 2;
19
20pub tracked enum OnceState<V: 'static> {
23 Uninit(PointsTo<Option<V>>),
25 Occupied,
27 Init(&'static PointsTo<Option<V>>),
30}
31
32#[cfg(feature = "irc11")]
33unsafe impl<V> Objective for OnceState<V> {
34
35}
36
37pub trait Predicate<V> {
40 spec fn inv(self, v: V) -> bool;
41}
42
43pub struct TrivialPred;
46
47impl<V> Predicate<V> for TrivialPred {
48 open spec fn inv(self, v: V) -> bool {
49 true
50 }
51}
52
53struct_with_invariants! {
54#[verifier::reject_recursive_types(V)]
74pub struct OnceImpl<V: 'static, F: Predicate<V>> {
75 cell: PCell<Option<V>>,
76 state: vstd::atomic_ghost::AtomicU64<_, OnceState<V>, _>,
77 f: Ghost<F>,
78}
79
80#[verifier::type_invariant]
81pub closed spec fn wf(&self) -> bool {
82 invariant on state with (cell, f) is (v: u64, g: OnceState<V>) {
83 match g {
84 OnceState::Uninit(points_to) => {
85 &&& v == UNINIT
86 &&& points_to.id() == cell.id()
87 &&& points_to.value() is None
88 }
89 OnceState::Occupied => {
90 &&& v == OCCUPIED
91 }
92 OnceState::Init(points_to) => {
93 &&& v == INITED
94 &&& points_to.id() == cell.id()
95 &&& points_to.value() is Some
96 &&& f@.inv(points_to.value()->0)
97 }
98 }
99 }
100}
101
102}
103
104#[verifier::external]
105unsafe impl<V, F: Predicate<V>> Send for OnceImpl<V, F> {
106
107}
108
109#[verifier::external]
110unsafe impl<V, F: Predicate<V>> Sync for OnceImpl<V, F> {
111
112}
113
114impl<V, F: Predicate<V>> OnceImpl<V, F> {
115 pub closed spec fn inv(&self) -> F {
116 self.f@
117 }
118
119 pub const fn new(Ghost(f): Ghost<F>) -> (r: Self)
121 ensures
122 r.wf(),
123 r.inv() == f,
124 {
125 let (cell, Tracked(points_to)) = PCell::new(None);
126 let tracked state = OnceState::Uninit(points_to);
127 let state = vstd::atomic_ghost::AtomicU64::new(
128 Ghost((cell, Ghost(f))),
129 UNINIT,
130 Tracked(state),
131 );
132
133 Self { cell, state, f: Ghost(f) }
134 }
135
136 pub fn init(&self, v: V)
138 requires
139 self.inv().inv(v),
140 self.wf(),
141 {
142 let cur_state =
143 atomic_with_ghost! {
144 &self.state => load(); ghost g => {}
145 };
146
147 if cur_state != UNINIT {
148 return;
149 } else {
150 let tracked mut points_to = None;
151 let res =
152 atomic_with_ghost! {
153 &self.state => compare_exchange(UNINIT, OCCUPIED);
154 returning res; ghost g => {
155 g = match g {
156 OnceState::Uninit(points_to_inner) => {
157 points_to = Some(points_to_inner);
158 OnceState::Occupied
159 }
160 _ => {
161 g
163 }
164 }
165 }
166 };
167
168 if !res.is_err() {
169 let tracked mut points_to = points_to.tracked_unwrap();
170 self.cell.replace(Tracked(&mut points_to), Some(v));
171 let tracked static_points_to = tracked_static_ref(points_to);
175 atomic_with_ghost! {
177 &self.state => store(INITED); ghost g => {
178 g = OnceState::Init(static_points_to);
179 }
180 }
181 return;
182 } else {
183 return;
185 }
186 }
187 }
188
189 pub fn get<'a>(&'a self) -> (r: Option<&'a V>)
193 requires
194 self.wf(),
195 ensures
196 self.wf(),
197 r matches Some(res) ==> self.inv().inv(*res),
198 {
199 let tracked mut points_to = None;
200 let res =
201 atomic_with_ghost! {
202 &self.state => load(); ghost g => {
203 match g {
204 OnceState::Init(points_to_opt) => {
205 points_to = Some(points_to_opt);
206 }
207 _ => {}
208 }
209 }
210 };
211
212 if res == INITED {
213 let tracked points_to = points_to.tracked_unwrap();
214 let tracked static_points_to = tracked_static_ref(points_to);
215
216 self.cell.borrow(Tracked(static_points_to)).as_ref()
217 } else {
218 None
219 }
220 }
221}
222
223pub type Once<V, Own, F> = OnceImpl<AtomicDataWithOwner<V, Own>, F>;
230
231}