ostd/sync/spin.rs
1// SPDX-License-Identifier: MPL-2.0
2use vstd::atomic_ghost::*;
3use vstd::cell::{self, pcell::*};
4use vstd::prelude::*;
5#[cfg(feature = "irc11")]
6use vstd::thread_view::Objective;
7use vstd_extra::prelude::*;
8
9use core::{
10 cell::UnsafeCell,
11 fmt,
12 marker::PhantomData,
13 ops::{Deref, DerefMut},
14 // sync::atomic::{AtomicBool, Ordering},
15};
16
17use super::{guard::SpinGuardian, LocalIrqDisabled /*, PreemptDisabled*/};
18//use crate::task::atomic_mode::AsAtomicModeGuard;
19
20verus! {
21
22/// The tracked resources transferred from the unlocked spin lock to its guard
23/// when the lock is acquired, and returned to the lock when the guard is dropped.
24tracked struct SpinLockResource<T, I: ResourceInvariant<T>> {
25 perm: PointsTo<T>,
26 resource: I::Resource,
27}
28
29#[cfg(feature = "irc11")]
30unsafe impl<T, I: ResourceInvariant<T>> Objective for SpinLockResource<T, I> {
31
32}
33
34impl<T, I: ResourceInvariant<T>> SpinLockResource<T, I> {
35 pub closed spec fn cell_id(self) -> cell::CellId {
36 self.perm.id()
37 }
38
39 pub closed spec fn value(self) -> T {
40 *self.perm.value()
41 }
42
43 pub closed spec fn resource(self) -> I::Resource {
44 self.resource
45 }
46}
47
48proof fn tracked_borrow_mut<R>(tracked resource: &mut R) -> (tracked result: &mut R)
49 ensures
50 *result == *old(resource),
51 *final(resource) == *final(result),
52{
53 resource
54}
55
56} // verus!
57
58/// A spin lock.
59///
60/// # Guard behavior
61///
62/// The type `G' specifies the guard behavior of the spin lock. While holding the lock,
63/// - if `G` is [`PreemptDisabled`], preemption is disabled;
64/// - if `G` is [`LocalIrqDisabled`], local IRQs are disabled.
65///
66/// The `G` can also be provided by other crates other than ostd,
67/// if it behaves similar like [`PreemptDisabled`] or [`LocalIrqDisabled`].
68///
69/// The guard behavior can be temporarily upgraded from [`PreemptDisabled`] to
70/// [`LocalIrqDisabled`] using the [`disable_irq`] method.
71///
72/// [`disable_irq`]: Self::disable_irq
73///
74/// # Verified properties
75///
76/// ## Ownership model
77///
78/// The `lock` field extends [`AtomicBool`] with a [`PointsTo<T>`] permission and a user-supplied
79/// tracked resource. These two resources are bundled in `SpinLockResource` and stored as the
80/// atomic ghost state while the lock is available:
81///
82/// ```rust
83/// tracked struct SpinLockResource<T, I: ResourceInvariant<T>> {
84/// perm: PointsTo<T>,
85/// resource: I::Resource,
86/// }
87///
88/// struct SpinLockInner<T, I: ResourceInvariant<T>> {
89/// lock: AtomicBool<_, Option<SpinLockResource<T, I>>, _>,
90/// val: PCell<T>,
91/// ghost_resource_constant: Ghost<<I as ResourceInvariant<T>>::Constant>,
92/// }
93/// ```
94///
95/// When the lock bit is `false`, the atomic ghost state is `Some`, the permission refers to `val`,
96/// and the protected value satisfies the user [`ResourceInvariant`]. Acquiring the lock changes
97/// the bit to `true` and transfers the complete `SpinLockResource` to the guard, leaving `None` in
98/// the atomic ghost state. Releasing the guard restores the resource invariant and returns the
99/// bundle to the lock.
100///
101/// The immutable resource constant remains available through `ghost_resource_constant` even while the
102/// tracked resource is owned by a guard. The complete relationship is encapsulated as a Verus type
103/// invariant; public operations expose only the permissions and resource-invariant facts needed
104/// by their callers.
105///
106/// ## Safety
107/// There are no data races.
108///
109/// ## Functional Correctness
110/// - At most one user can hold the lock at the same time.
111#[repr(transparent)]
112#[verus_verify]
113//pub struct SpinLock<T: ?Sized, G = PreemptDisabled> {
114pub struct SpinLock<T, G, I: ResourceInvariant<T> = TrivialResourceInvariant> {
115 phantom: PhantomData<G>,
116 /// Only the last field of a struct may have a dynamically sized type.
117 /// That's why SpinLockInner is put in the last field.
118 inner: SpinLockInner<T, I>,
119}
120
121struct_with_invariants! {
122struct SpinLockInner<T, I: ResourceInvariant<T>> {
123 lock: AtomicBool<_, Option<SpinLockResource<T, I>>, _>,
124 //val: UnsafeCell<T>,
125 val: PCell<T>, //TODO: Waiting the new PCell that supports ?Sized
126 ghost_resource_constant: Ghost<<I as ResourceInvariant<T>>::Constant>,
127}
128
129 #[verifier::type_invariant]
130 closed spec fn type_inv(self) -> bool {
131 invariant on lock with (val, ghost_resource_constant)
132 is (locked: bool, resource: Option<SpinLockResource<T, I>>)
133 {
134 match resource {
135 None => locked,
136 Some(resource) => {
137 &&& !locked
138 &&& resource.cell_id() == val.id()
139 &&& I::inv(
140 ghost_resource_constant@,
141 resource.value(),
142 resource.resource(),
143 )
144 }
145 }
146 }
147 }
148}
149
150verus! {
151
152impl<T, G, I: ResourceInvariant<T>> SpinLock<T, G, I> {
153 /// Creates a new spin lock.
154 ///
155 /// # Verified Properties
156 /// ## Safety
157 /// This function is written in safe Rust and there is no undefined behavior.
158 /// ## Preconditions
159 /// None.
160 /// ## Postconditions
161 /// - The function will not panic.
162 /// - The created spin lock satisfies the invariant.
163 pub const fn new(
164 val: T,
165 Ghost(resource_constant): Ghost<I::Constant>,
166 Tracked(resource): Tracked<I::Resource>,
167 ) -> (res: Self)
168 requires
169 I::inv(resource_constant, val, resource),
170 ensures
171 res.constant() == resource_constant,
172 {
173 let (val, Tracked(perm)) = PCell::new(val);
174 let tracked resource = SpinLockResource { perm, resource: resource };
175 let lock_inner = SpinLockInner {
176 lock: AtomicBool::new(
177 Ghost((val, Ghost(resource_constant))),
178 false,
179 Tracked(Some(resource)),
180 ),
181 //val: UnsafeCell::new(val),
182 val: val,
183 ghost_resource_constant: Ghost(resource_constant),
184 };
185 Self {
186 phantom: PhantomData,
187 inner: lock_inner,
188 }
189 }
190}
191
192}
193
194verus! {
195
196impl<T, G, I: ResourceInvariant<T>> SpinLock<T, G, I>
197{
198 /// Returns the unique [`CellId`](https://verus-lang.github.io/verus/verusdoc/vstd/cell/struct.CellId.html) of the internal `PCell<T>`.
199 pub closed spec fn cell_id(self) -> cell::CellId {
200 self.inner.val.id()
201 }
202
203 /// The immutable constant associated with the resource invariant.
204 pub closed spec fn constant(self) -> I::Constant {
205 self.inner.ghost_resource_constant@
206 }
207
208 /// Public well-formedness predicate for external wrappers.
209 pub closed spec fn wf(self) -> bool {
210 self.type_inv()
211 }
212
213 /// Encapsulates the invariant described in the *Invariant* section of [`SpinLock`].
214 #[verifier::type_invariant]
215 pub closed spec fn type_inv(self) -> bool{
216 self.inner.type_inv()
217 }
218}
219
220/*
221impl<T: ?Sized> SpinLock<T, PreemptDisabled> {
222 /// Converts the guard behavior from disabling preemption to disabling IRQs.
223 pub fn disable_irq(&self) -> &SpinLock<T, LocalIrqDisabled> {
224 let ptr = self as *const SpinLock<T, PreemptDisabled>;
225 let ptr = ptr as *const SpinLock<T, LocalIrqDisabled>;
226 // SAFETY:
227 // 1. The types `SpinLock<T, PreemptDisabled>`, `SpinLockInner<T>` and `SpinLock<T,
228 // IrqDisabled>` have the same memory layout guaranteed by `#[repr(transparent)]`.
229 // 2. The specified memory location can be borrowed as an immutable reference for the
230 // specified lifetime.
231 unsafe { &*ptr }
232 }
233}*/
234
235#[verus_verify]
236impl<T /*: ?Sized */, G: SpinGuardian, I: ResourceInvariant<T>> SpinLock<T, G, I> {
237 /// Acquires the spin lock.
238 ///
239 /// # Verified Properties
240 /// ## Safety
241 /// There are no data races. The lock ensures exclusive access to the protected data.
242 /// ## Preconditions
243 /// None. (The invariant of `SpinLock` always holds internally.)
244 /// ## Postconditions
245 /// The returned `SpinLockGuard` satisfies its type invariant and the user-supplied resource
246 /// invariant:
247 /// - An exclusive permission to access the protected data is held by the guard.
248 /// - The guard's permission matches the lock's internal cell ID.
249 /// - The protected value and tracked resource satisfy the resource invariant.
250 /// ## Key Verification Step
251 /// When the internal atomic compare-and-exchange operation in `acquire_lock` succeeds,
252 /// the ghost permission and user resource are simultaneously extracted from the lock.
253 /// ```rust
254 /// atomic_with_ghost! {
255 /// self.inner.lock => compare_exchange(false, true);
256 /// returning res;
257 /// ghost lock_resource => {
258 /// // Extract the resources when the lock is successfully acquired.
259 /// if res is Ok {
260 /// resource = Some(lock_resource.tracked_take());
261 /// }
262 /// }
263 ///}.is_ok()
264 /// ```
265 #[verus_spec(ret =>
266 ensures
267 ret.constant() == self.constant(),
268 I::inv(ret.constant(), ret.value(), ret.resource()),
269 )]
270 pub fn lock(&self) -> SpinLockGuard<'_, T, G, I> {
271 // Notice the guard must be created before acquiring the lock.
272 proof!{ use_type_invariant(self);}
273 proof_decl!{
274 let tracked resource: SpinLockResource<T, I>;
275 }
276 let inner_guard = G::guard();
277 proof_with! {=> Tracked(resource)}
278 self.acquire_lock();
279 proof_decl! {
280 let tracked SpinLockResource { perm, resource: resource } = resource;
281 }
282 SpinLockGuard {
283 lock: self,
284 guard: inner_guard,
285 tracked_perm: Tracked(perm),
286 tracked_resource: Tracked(resource),
287 }
288 }
289
290 /// Tries acquiring the spin lock immediately.
291 ///
292 /// # Verified Properties
293 /// ## Safety
294 /// There are no data races. The lock ensures exclusive access to the protected data.
295 /// ## Preconditions
296 /// None. (The invariant of `SpinLock` always holds internally.)
297 /// ## Postconditions
298 /// If `Some(guard)` is returned, it satisfies its type invariant:
299 /// - An exclusive permission to access the protected data is held by the guard.
300 /// - The guard's permission matches the lock's internal cell ID.
301 #[verus_spec(ret =>
302 ensures
303 ret is Some ==> {
304 &&& ret->0.constant() == self.constant()
305 &&& I::inv(ret->0.constant(), ret->0.value(), ret->0.resource())
306 },
307 )]
308 pub fn try_lock(&self) -> Option<SpinLockGuard<'_, T, G, I>> {
309 let inner_guard = G::guard();
310 proof_decl!{
311 let tracked mut resource: Option<SpinLockResource<T, I>> = None;
312 }
313 if #[verus_spec(with => Tracked(resource))] self.try_acquire_lock() {
314 proof_decl! {
315 let tracked SpinLockResource { perm, resource: resource } =
316 resource.tracked_unwrap();
317 }
318 let lock_guard = SpinLockGuard {
319 lock: self,
320 guard: inner_guard,
321 tracked_perm: Tracked(perm),
322 tracked_resource: Tracked(resource),
323 };
324 return Some(lock_guard);
325 }
326 None
327 }
328
329 /*
330 /// Returns a mutable reference to the underlying data.
331 ///
332 /// This method is zero-cost: By holding a mutable reference to the lock, the compiler has
333 /// already statically guaranteed that access to the data is exclusive.
334 pub fn get_mut(&mut self) -> &mut T {
335 self.inner.val.get_mut()
336 }*/
337
338 /// Acquires the spin lock, otherwise busy waiting
339 #[verus_spec(ret =>
340 with
341 -> resource: Tracked<SpinLockResource<T, I>>,
342 ensures
343 resource@.perm.id() == self.inner.val.id(),
344 I::inv(self.constant(), resource@.value(), resource@.resource()),
345 )]
346 #[verifier::exec_allows_no_decreases_clause]
347 fn acquire_lock(&self) {
348 proof_decl!{
349 let tracked mut resource: Option<SpinLockResource<T, I>> = None;
350 }
351 proof!{ use_type_invariant(self);}
352 #[verus_spec(
353 invariant self.type_inv(),
354 )]
355 while !#[verus_spec(with => Tracked(resource))]self.try_acquire_lock() {
356 core::hint::spin_loop();
357 }
358
359 proof_decl!{
360 let tracked resource = resource.tracked_unwrap();
361 }
362 // VERUS LIMITATION: Explicit return value to bind the ghost permission return value
363 #[verus_spec(with |= Tracked(resource))]
364 ()
365 }
366
367 #[verus_spec(ret =>
368 with
369 -> resource: Tracked<Option<SpinLockResource<T, I>>>,
370 ensures
371 ret ==> {
372 &&& resource@ is Some
373 &&& resource@->0.perm.id() == self.inner.val.id()
374 &&& I::inv(
375 self.constant(),
376 resource@->0.value(),
377 resource@->0.resource(),
378 )
379 },
380 !ret ==> resource@ is None,
381 )]
382 fn try_acquire_lock(&self) -> bool {
383 /*self.inner
384 .lock
385 .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
386 .is_ok()*/
387 proof_decl!{
388 let tracked mut resource: Option<SpinLockResource<T, I>> = None;
389 }
390 proof!{ use_type_invariant(self);}
391 proof_with!{ |= Tracked(resource)}
392 atomic_with_ghost! {
393 self.inner.lock => compare_exchange(false, true);
394 returning res;
395 ghost lock_resource => {
396 if res is Ok {
397 resource = Some(lock_resource.tracked_take());
398 }
399 }
400 }.is_ok()
401 }
402
403 #[verus_spec(
404 with
405 Tracked(resource): Tracked<SpinLockResource<T, I>>,
406 requires
407 resource.perm.id() == self.inner.val.id(),
408 I::inv(self.constant(), resource.value(), resource.resource()),
409 )]
410 fn release_lock(&self) {
411 proof!{
412 use_type_invariant(self);
413 }
414 //self.inner.lock.store(false, Ordering::Release);
415 atomic_with_ghost!{
416 self.inner.lock => store(false);
417 ghost lock_resource => {
418 lock_resource = Some(resource);
419 }
420 }
421 }
422}
423}
424
425/*
426impl<T: ?Sized + fmt::Debug, G> fmt::Debug for SpinLock<T, G> {
427 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
428 fmt::Debug::fmt(&self.inner.val, f)
429 }
430}*/
431
432// SAFETY: Only a single lock holder is permitted to access the inner data of Spinlock.
433#[verifier::external]
434unsafe impl<T: Send, G, I: ResourceInvariant<T>> Send for SpinLock<T, G, I> where I::Resource: Send {}
435#[verifier::external]
436unsafe impl<T: Send, G, I: ResourceInvariant<T>> Sync for SpinLock<T, G, I> where I::Resource: Send {}
437
438/// A guard that provides exclusive access to the data protected by a [`SpinLock`].
439///
440/// # Verified Properties
441/// ## Verification Design
442/// The guard is extended with tracked fields holding both the ghost permission
443/// ([`PointsTo<T>`](https://verus-lang.github.io/verus/verusdoc/vstd/cell/pcell/struct.PointsTo.html))
444/// and the user-supplied tracked resource. The permission grants exclusive ownership of the
445/// protected data and enables verified access to the `PCell<T>`.
446///
447///
448/// ## Invariant
449/// The guard maintains a type invariant ensuring that its ghost permission's ID matches
450/// the lock's internal cell ID. This guarantees that the permission corresponds to the
451/// correct protected data.
452///
453/// ```rust
454/// #[verifier::type_invariant]
455/// spec fn type_inv(self) -> bool{
456/// self.lock.cell_id() == self.tracked_perm@.id()
457/// }
458/// ```
459///
460/// *Note*: The invariant is encapsulated using the [`#[verifier::type_invariant]`](https://verus-lang.github.io/verus/guide/reference-type-invariants.html?highlight=type_#declaring-a-type-invariant) mechanism.
461/// It internally holds at all steps during the method executions and is **NOT** exposed in the public APIs' pre- and post-conditions.
462#[verifier::reject_recursive_types(T)]
463#[verifier::reject_recursive_types(G)]
464#[clippy::has_significant_drop]
465#[must_use]
466#[verus_verify]
467pub struct SpinLockGuard<
468 'a,
469 T, /*: ?Sized*/
470 G: SpinGuardian,
471 I: ResourceInvariant<T> = TrivialResourceInvariant,
472> {
473 guard: G::Guard,
474 lock: &'a SpinLock<T, G, I>,
475 /// Ghost permission for the protected value.
476 tracked_perm: Tracked<PointsTo<T>>,
477 /// User-supplied tracked resource.
478 tracked_resource: Tracked<I::Resource>,
479}
480
481verus! {
482impl<'a, T, G: SpinGuardian, I: ResourceInvariant<T>> SpinLockGuard<'a, T, G, I>
483{
484 #[verifier::type_invariant]
485 spec fn type_inv(self) -> bool{
486 self.lock.cell_id() == self.tracked_perm@.id()
487 }
488
489 /// The value stored in the lock.
490 pub closed spec fn value(self) -> T {
491 *self.tracked_perm@.value()
492 }
493
494 /// The tracked resource associated with the protected value.
495 pub closed spec fn resource(self) -> I::Resource {
496 self.tracked_resource@
497 }
498
499 /// The immutable user constant associated with the guarded spin lock.
500 pub closed spec fn constant(self) -> I::Constant {
501 self.lock.constant()
502 }
503
504 /// The value stored in the lock. It is an alias of `Self::value`.
505 pub open spec fn view(self) -> T {
506 self.value()
507 }
508
509 /// Mutably borrows the user-supplied tracked resource.
510 pub proof fn tracked_borrow_mut_resource(tracked &mut self) -> (tracked resource: &mut I::Resource)
511 ensures
512 *resource == old(self).resource(),
513 final(self).resource() == *final(resource),
514 final(self).value() == old(self).value(),
515 final(self).constant() == old(self).constant(),
516 {
517 use_type_invariant(&*self);
518 let tracked resource = tracked_borrow_mut(&mut *self.tracked_resource);
519 resource
520 }
521}
522/*
523impl<T: ?Sized, G: SpinGuardian> AsAtomicModeGuard for SpinLockGuard<'_, T, G> {
524 fn as_atomic_mode_guard(&self) -> &dyn crate::task::atomic_mode::InAtomicMode {
525 self.guard.as_atomic_mode_guard()
526 }
527}*/
528
529// FIXME: fix when verus attribute syntax supports Tracked.
530#[verus_verify]
531impl<T: /*?Sized*/, G: SpinGuardian, I: ResourceInvariant<T>> Deref
532 for SpinLockGuard<'_, T, G, I>
533{
534 type Target = T;
535
536 #[verus_spec(returns self.view())]
537 fn deref(&self) -> &T {
538 proof_decl! {
539 let tracked read_perm = self.tracked_perm.borrow();
540 }
541 proof!{
542 use_type_invariant(self);
543 }
544 // unsafe { &*self.lock.inner.val.get() }
545 // The internal implementation of `PCell<T>::borrow` is exactly unsafe { &(*(*self.ucell).get()) },
546 // and here we verify that we have the permission to call `borrow`.
547 self.lock.inner.val.borrow(Tracked(read_perm))
548 }
549}
550
551
552#[verus_verify]
553impl<T: /* ?Sized */, G: SpinGuardian, I: ResourceInvariant<T>> DerefMut
554 for SpinLockGuard<'_, T, G, I>
555{
556 #[verus_spec(ret =>
557 ensures
558 final(self).view() == *final(ret),
559 old(self).view() == *ret,
560 final(self).resource() == old(self).resource(),
561 final(self).constant() == old(self).constant(),
562 )]
563 fn deref_mut(&mut self) -> &mut Self::Target
564 {
565 proof!{
566 use_type_invariant(&*self);
567 }
568 // unsafe { &mut *self.lock.inner.val.get() }
569 self.lock.inner.val.borrow_mut(Tracked(&mut *self.tracked_perm))
570 }
571}
572}
573
574/* impl<T: ?Sized, G: SpinGuardian> Drop for SpinLockGuard<'_, T, G> {
575 fn drop(&mut self) {
576 self.lock.release_lock();
577 }
578}
579*/
580
581#[verus_verify]
582impl<'a, T /*:?Sized */, G: SpinGuardian, I: ResourceInvariant<T>> SpinLockGuard<'a, T, G, I> {
583 /// VERUS LIMITATION: We implement `drop` and call it manually because Verus's support for `Drop` is incomplete for now.
584 #[verus_spec(
585 requires
586 I::inv(self.constant(), self.value(), self.resource()),
587 )]
588 pub fn drop(self) {
589 proof! {use_type_invariant(&self);}
590 proof_decl! {
591 let tracked perm = self.tracked_perm.get();
592 let tracked resource = self.tracked_resource.get();
593 let tracked resource = SpinLockResource { perm, resource };
594 }
595 proof_with!(Tracked(resource));
596 self.lock.release_lock();
597 }
598}
599
600/* impl<T: ?Sized + fmt::Debug, G: SpinGuardian> fmt::Debug for SpinLockGuard<'_, T, G> {
601 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
602 fmt::Debug::fmt(&**self, f)
603 }
604}*/
605
606#[verus_verify]
607impl<T: ?Sized, G: SpinGuardian, I: ResourceInvariant<T>> !Send for SpinLockGuard<'_, T, G, I> {}
608
609#[verifier::external]
610// SAFETY: `SpinLockGuard` can be shared between tasks/threads in same CPU.
611// As `lock()` is only called when there are no race conditions caused by interrupts.
612unsafe impl<T: Sync, G: SpinGuardian, I: ResourceInvariant<T>> Sync for SpinLockGuard<'_, T, G, I> {}