Skip to main content

ostd/sync/
spin.rs

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