Skip to main content

ostd/sync/
rwarc.rs

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