Skip to main content

ostd/sync/
rwarc.rs

1// SPDX-License-Identifier: MPL-2.0
2use vstd::{atomic_ghost::*, prelude::*};
3use vstd_extra::prelude::*;
4
5use alloc::sync::Arc;
6//use core::sync::atomic::{fence, AtomicUsize, Ordering};
7
8use super::{PreemptDisabled, RwLock, RwLockReadGuard, RwLockWriteGuard};
9
10verus! {
11
12/// A reference-counting pointer with read-write capabilities.
13///
14/// This is essentially `Arc<RwLock<T>>`, so it can provide read-write capabilities through
15/// [`RwArc::read`] and [`RwArc::write`].
16///
17/// In addition, this allows to derive another reference-counting pointer with read-only
18/// capabilities ([`RoArc`]) via [`RwArc::clone_ro`].
19///
20/// The purpose of having this type is to allow lockless (read) access to the underlying data when
21/// there is only one [`RwArc`] instance for the particular allocation (note that there can be any
22/// number of [`RoArc`] instances for that allocation). See the [`RwArc::get`] method for more
23/// details.
24pub struct RwArc<T>(Arc<Inner<T>>);
25
26/// A reference-counting pointer with read-only capabilities.
27///
28/// This type can be created from an existing [`RwArc`] using its [`RwArc::clone_ro`] method. See
29/// the type and method documentation for more details.
30pub struct RoArc<T>(Arc<Inner<T>>);
31
32struct_with_invariants!{
33struct Inner<T> {
34    data: RwLock<T, PreemptDisabled>,
35    num_rw: AtomicUsize<_,int,_>,
36}
37
38closed spec fn wf(&self) -> bool {
39  invariant on num_rw with (data) is (v:usize, g:int) {
40    v == g
41  }
42}
43}
44
45impl<T> RwArc<T> {
46    #[verifier::type_invariant]
47    closed spec fn type_inv(self) -> bool {
48        self.wf()
49    }
50
51    pub closed spec fn wf(&self) -> bool {
52        &&& self.0.wf()
53        &&& self.0.num_rw.well_formed()
54    }
55}
56
57impl<T> RwArc<T> {
58    /// Creates a new `RwArc<T>`.
59    pub fn new(data: T) -> (r: Self)
60        ensures
61            r.wf(),
62    {
63        //let inner = Inner { data: RwLock::new(data), num_rw: AtomicUsize::new(1) };
64        let data = RwLock::new(data);
65        let inner = Inner { data, num_rw: AtomicUsize::new(Ghost(data), 1, Tracked(1int)) };
66        Self(Arc::new(inner))
67    }
68
69    /// Acquires the read lock for immutable access.
70    pub fn read(&self) -> RwLockReadGuard<'_, T, PreemptDisabled> {
71        self.0.data.read()
72    }
73
74    /// Acquires the write lock for mutable access.
75    pub fn write(&self) -> RwLockWriteGuard<'_, T, PreemptDisabled> {
76        self.0.data.write()
77    }
78
79    /*
80    /// Returns a raw pointer to the contained value.
81    pub fn as_ptr(&self) -> *const T {
82        self.0.data.as_ptr()
83    }
84
85    /// Returns an immutable reference if no other `RwArc` points to the same allocation.
86    ///
87    /// This method is cheap because it does not acquire a lock.
88    ///
89    /// It's still sound because:
90    /// - The mutable reference to `self` and the condition ensure that we are exclusively
91    ///   accessing the unique `RwArc` instance for the particular allocation.
92    /// - There may be any number of [`RoArc`]s pointing to the same allocation, but they may only
93    ///   produce immutable references to the underlying data.
94    #[verifier::external_body]
95    pub fn get(&mut self) -> Option<&T> {
96        if self.0.num_rw.load(Ordering::Relaxed) > 1 {
97            return None;
98        }
99        // This will synchronize with `RwArc::drop` to make sure its changes are visible to us.
100
101        fence(Ordering::Acquire);
102
103        let data_ptr = self.0.data.as_ptr();
104
105        // SAFETY: The data is valid. During the lifetime, no one will be able to create a mutable
106        // reference to the data, so it's okay to create an immutable reference like the one below.
107        Some(unsafe { &*data_ptr })
108    }
109    */
110    /// Clones a [`RoArc`] that points to the same allocation.
111    pub fn clone_ro(&self) -> RoArc<T> {
112        RoArc(self.0.clone())
113    }
114}
115
116#[verus_verify]
117impl<T> Clone for RwArc<T> {
118    #[verus_spec(returns self)]
119    fn clone(&self) -> Self {
120        proof!{
121            use_type_invariant(self);
122        }
123        let inner = self.0.clone();
124        // Note that overflowing the counter will make it unsound. But not to worry: the above
125        // `Arc::clone` must have already aborted the kernel before this happens.
126        // inner.num_rw.fetch_add(1, Ordering::Relaxed);
127        atomic_with_ghost! {
128            inner.num_rw => fetch_add(1);
129            update prev -> next;
130            ghost g => {
131                assert(InvariantPredicate_auto_Inner_num_rw::atomic_inv(
132                    self.0.num_rw.constant(),
133                    prev as usize,
134                    g,
135                ));
136                assume(g < usize::MAX);
137                g = g + 1;
138            }
139        };
140
141        Self(inner)
142    }
143}
144
145/*
146impl<T> Drop for RwArc<T> {
147    #[verifier::external_body]
148    fn drop(&mut self)
149        opens_invariants none
150        no_unwind
151    {
152        self.0.num_rw.fetch_sub(1, Ordering::Release);
153    }
154}*/
155
156impl<T> RwArc<T> {
157    /// VERUS LIMITATION: We implement `drop` and call it manually because Verus's support for `Drop` is incomplete for now.
158    pub fn drop(self) {
159        proof!{
160            use_type_invariant(&self);
161        }
162        atomic_with_ghost! {
163            self.0.num_rw => fetch_sub(1);
164            ghost g => {
165                assume(g > 0);
166                g = g - 1;
167            }
168    };
169    }
170}
171
172impl<T: Clone> RwArc<T> {
173    /// Returns the contained value by cloning it.
174    pub fn get_cloned(&self) -> T {
175        let guard = self.read();
176        guard.clone()
177    }
178}
179
180impl<T> RoArc<T> {
181    /// Acquires the read lock for immutable access.
182    pub fn read(&self) -> RwLockReadGuard<'_, T, PreemptDisabled> {
183        self.0.data.read()
184    }
185}
186
187} // verus!
188#[cfg(ktest)]
189mod test {
190    use super::*;
191    use crate::prelude::*;
192
193    #[ktest]
194    fn lockless_get() {
195        let mut rw1 = RwArc::new(1u32);
196        assert_eq!(rw1.get(), Some(1).as_ref());
197
198        let _ro = rw1.clone_ro();
199        assert_eq!(rw1.get(), Some(1).as_ref());
200
201        let rw2 = rw1.clone();
202        assert_eq!(rw1.get(), None);
203
204        drop(rw2);
205        assert_eq!(rw1.get(), Some(1).as_ref());
206    }
207}