Skip to main content

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> {}