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 crate::{atomic_data::AtomicDataWithOwner, resource_invariant::ValueInvariant};
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
37struct_with_invariants! {
38#[verifier::reject_recursive_types(V)]
58pub struct OnceImpl<V: 'static, F: ValueInvariant<V>> {
59 cell: PCell<Option<V>>,
60 state: vstd::atomic_ghost::AtomicU64<_, OnceState<V>, _>,
61 f: Ghost<F>,
62}
63
64#[verifier::type_invariant]
65pub closed spec fn wf(&self) -> bool {
66 invariant on state with (cell, f) is (v: u64, g: OnceState<V>) {
67 match g {
68 OnceState::Uninit(points_to) => {
69 &&& v == UNINIT
70 &&& points_to.id() == cell.id()
71 &&& points_to.value() is None
72 }
73 OnceState::Occupied => {
74 &&& v == OCCUPIED
75 }
76 OnceState::Init(points_to) => {
77 &&& v == INITED
78 &&& points_to.id() == cell.id()
79 &&& points_to.value() is Some
80 &&& F::inv(points_to.value()->0)
81 }
82 }
83 }
84}
85
86}
87
88#[verifier::external]
89unsafe impl<V, F: ValueInvariant<V>> Send for OnceImpl<V, F> {
90
91}
92
93#[verifier::external]
94unsafe impl<V, F: ValueInvariant<V>> Sync for OnceImpl<V, F> {
95
96}
97
98impl<V, F: ValueInvariant<V>> OnceImpl<V, F> {
99 pub closed spec fn inv(&self) -> F {
100 self.f@
101 }
102
103 pub const fn new(Ghost(f): Ghost<F>) -> (r: Self)
105 ensures
106 r.wf(),
107 r.inv() == f,
108 {
109 let (cell, Tracked(points_to)) = PCell::new(None);
110 let tracked state = OnceState::Uninit(points_to);
111 let state = vstd::atomic_ghost::AtomicU64::new(
112 Ghost((cell, Ghost(f))),
113 UNINIT,
114 Tracked(state),
115 );
116
117 Self { cell, state, f: Ghost(f) }
118 }
119
120 pub fn init(&self, v: V)
122 requires
123 F::inv(v),
124 self.wf(),
125 {
126 let cur_state =
127 atomic_with_ghost! {
128 &self.state => load(); ghost g => {}
129 };
130
131 if cur_state != UNINIT {
132 return;
133 } else {
134 let tracked mut points_to = None;
135 let res =
136 atomic_with_ghost! {
137 &self.state => compare_exchange(UNINIT, OCCUPIED);
138 returning res; ghost g => {
139 g = match g {
140 OnceState::Uninit(points_to_inner) => {
141 points_to = Some(points_to_inner);
142 OnceState::Occupied
143 }
144 _ => {
145 g
147 }
148 }
149 }
150 };
151
152 if !res.is_err() {
153 let tracked mut points_to = points_to.tracked_unwrap();
154 self.cell.replace(Tracked(&mut points_to), Some(v));
155 let tracked static_points_to = tracked_static_ref(points_to);
159 atomic_with_ghost! {
161 &self.state => store(INITED); ghost g => {
162 g = OnceState::Init(static_points_to);
163 }
164 }
165 return;
166 } else {
167 return;
169 }
170 }
171 }
172
173 pub fn get<'a>(&'a self) -> (r: Option<&'a V>)
177 requires
178 self.wf(),
179 ensures
180 self.wf(),
181 r matches Some(res) ==> F::inv(*res),
182 {
183 let tracked mut points_to = None;
184 let res =
185 atomic_with_ghost! {
186 &self.state => load(); ghost g => {
187 match g {
188 OnceState::Init(points_to_opt) => {
189 points_to = Some(points_to_opt);
190 }
191 _ => {}
192 }
193 }
194 };
195
196 if res == INITED {
197 let tracked points_to = points_to.tracked_unwrap();
198 let tracked static_points_to = tracked_static_ref(points_to);
199
200 self.cell.borrow(Tracked(static_points_to)).as_ref()
201 } else {
202 None
203 }
204 }
205}
206
207pub type Once<V, I, F> = OnceImpl<AtomicDataWithOwner<V, I>, F>;
214
215}