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}