starnix_core/mutable_state.rs
1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Macros used with struct containing an immutable state and a RwLock to a mutable state.
6//!
7//! These macros define a new type of read and write guards that allow access to both the
8//! mutable and immutable state. To use it, one must:
9//! - Define the main struct (e.g. `Foo`) for the object with the immutable state.
10//! - Define a struct for the mutable state (e.g. `FooMutableState`).
11//! - Have a RwLock<> in the main struct for the mutable state (e.g. `mutable_state:
12//! RwLock<FooMutableState>`).
13//! - In the implementation of the main struct, add a call to the `state_accessor` macros:
14//! ```
15//! impl Foo {
16//! state_accessor!(Foo, mutable_state);
17//! }
18//! ```
19//! - Write the method on the guards using the state_implementation macro:
20//! ```
21//! #[apply(state_implementation!)]
22//! impl FooMutableState<Base=Foo> {
23//! // Some comment
24//! fn do_something(&self) -> i32 {
25//! 0
26//! }
27//! }
28//! ```
29//!
30//! # Complete example:
31//!
32//! ```
33//! pub struct FooMutableState {
34//! y: i32,
35//! }
36//!
37//! pub struct Foo {
38//! x: i32,
39//! mutable_state: RwLock<FooMutableState>,
40//! }
41//!
42//! impl Foo {
43//! fn new() -> Self {
44//! Self { x: 2, mutable_state: RwLock::new(FooMutableState { y: 3 }) }
45//! }
46//!
47//! state_accessor!(Foo, mutable_state);
48//! }
49//!
50//! #[attr(state_implementation!)]
51//! impl FooMutableState<Base=Foo> {
52//! // Some comment
53//! fn x_and_y(&self) -> i32 {
54//! self.base.x + self.y
55//! }
56//! /// Some rustdoc.
57//! pub fn pub_x_and_y(&self) -> i32 {
58//! self.x_and_y()
59//! }
60//! fn do_something(&self) {}
61//! fn set_y(&mut self, other_y: i32) {
62//! self.y = other_y;
63//! }
64//! pub fn pub_set_y(&mut self, other_y: i32) {
65//! self.set_y(other_y)
66//! }
67//! fn do_something_mutable(&mut self) {
68//! self.do_something();
69//! }
70//!
71//! #[allow(dead_code)]
72//! pub fn with_lifecycle<'a>(&self, _n: &'a u32) {}
73//! #[allow(dead_code)]
74//! pub fn with_type<T>(&self, _n: &T) {}
75//! #[allow(dead_code)]
76//! pub fn with_lifecycle_and_type<'a, T>(&self, _n: &'a T) {}
77//! #[allow(dead_code)]
78//! pub fn with_lifecycle_on_self<'a, T>(&'a self, _n: &'a T) {}
79//! }
80//! ```
81//!
82//! # Generated code
83//!
84//! ```
85//! pub struct FooMutableState {
86//! y: i32,
87//! }
88//! pub struct Foo {
89//! x: i32,
90//! mutable_state: RwLock<FooMutableState>,
91//! }
92//! impl Foo {
93//! fn new() -> Self {
94//! Self {
95//! x: 2,
96//! mutable_state: RwLock::new(FooMutableState { y: 3 }),
97//! }
98//! }
99//!
100//! #[allow(dead_code)]
101//! pub fn read<'a>(self: &'a Foo) -> FooReadGuard<'a> {
102//! ReadGuard::new(self, self.mutable_state.read())
103//! }
104//! #[allow(dead_code)]
105//! pub fn write<'a>(self: &'a Foo) -> FooWriteGuard<'a> {
106//! WriteGuard::new(self, self.mutable_state.write())
107//! }
108//! }
109//!
110//! #[allow(dead_code)]
111//! pub type FooReadGuard<'guard_lifetime> = ReadGuard<'guard_lifetime, Foo, FooMutableState>;
112//! #[allow(dead_code)]
113//! pub type FooWriteGuard<'guard_lifetime> = WriteGuard<'guard_lifetime, Foo, FooMutableState>;
114//! #[allow(dead_code)]
115//! pub type FooStateRef<'ref_lifetime> = StateRef<'ref_lifetime, Foo, FooMutableState>;
116//! #[allow(dead_code)]
117//! pub type FooStateMutRef<'ref_lifetime> = StateMutRef<'ref_lifetime, Foo, FooMutableState>;
118//!
119//! impl<'guard, G: 'guard + std::ops::Deref<Target = FooMutableState>> Guard<'guard, Foo, G> {
120//! fn x_and_y(&self) -> i32 {
121//! self.base.x + self.y
122//! }
123//! /// Some rustdoc.
124//! pub fn pub_x_and_y(&self) -> i32 {
125//! self.x_and_y()
126//! }
127//! fn do_something(&self) {}
128//! #[allow(dead_code)]
129//! pub fn with_lifecycle<'a>(&self, _n: &'a u32) {}
130//! #[allow(dead_code)]
131//! pub fn with_type<T>(&self, _n: &T) {}
132//! #[allow(dead_code)]
133//! pub fn with_lifecycle_and_type<'a, T>(&self, _n: &'a T) {}
134//! #[allow(dead_code)]
135//! pub fn with_lifecycle_on_self<'a, T>(&'a self, _n: &'a T) {}
136//! }
137//!
138//! impl<'guard, G: 'guard + std::ops::DerefMut<Target = FooMutableState>> Guard<'guard, Foo, G> {
139//! fn set_y(&mut self, other_y: i32) {
140//! self.y = other_y;
141//! }
142//! pub fn pub_set_y(&mut self, other_y: i32) {
143//! self.set_y(other_y)
144//! }
145//! fn do_something_mutable(&mut self) {
146//! self.do_something();
147//! }
148//! }
149//! ```
150
151use starnix_sync::{MutexGuard, RwLockReadGuard, RwLockWriteGuard};
152use std::ops::{Deref, DerefMut};
153
154/// Create the read() and write() accessor to respectively access the read guard and write guard.
155///
156/// For a base struct named `Foo`, the read guard will be a struct named `FooReadGuard` and the
157/// write guard a struct named `FooWriteGuard`.
158macro_rules! state_accessor {
159 ($base_name:ident, $field_name:ident, $base_type:ty) => {
160 paste::paste! {
161 #[allow(dead_code)]
162 pub fn read<'a>(self: &'a $base_type) -> [<$base_name ReadGuard>]<'a> {
163 $crate::mutable_state::ReadGuard::new(self, self.$field_name.read())
164 }
165 #[allow(dead_code)]
166 pub fn write<'a>(self: &'a $base_type) -> [<$base_name WriteGuard>]<'a> {
167 $crate::mutable_state::WriteGuard::new(self, self.$field_name.write())
168 }
169 }
170 };
171 ($base_name:ident, $field_name:ident) => {
172 state_accessor!($base_name, $field_name, $base_name);
173 };
174}
175
176/// Create the read() and write() accessor to respectively access the read guard and write guard
177/// of an OrderedRwLock using a Locked context.
178///
179/// For a base struct named `Foo`, the read guard will be a struct named `FooReadGuard` and the
180/// write guard a struct named `FooWriteGuard`.
181macro_rules! ordered_state_accessor {
182 ($base_name:ident, $field_name:ident, $base_type:ty, $lock_level:ident) => {
183 paste::paste! {
184 #[allow(dead_code)]
185 pub fn read<'a, L>(self: &'a $base_type, locked: &'a mut starnix_sync::Locked<L>) -> [<$base_name ReadGuard>]<'a>
186 where
187 L: starnix_sync::LockBefore<$lock_level>
188 {
189 $crate::mutable_state::ReadGuard::new(self, self.$field_name.read(locked))
190 }
191 #[allow(dead_code)]
192 pub fn write<'a, L>(self: &'a $base_type, locked: &'a mut starnix_sync::Locked<L>) -> [<$base_name WriteGuard>]<'a>
193 where
194 L: starnix_sync::LockBefore<$lock_level>
195 {
196 $crate::mutable_state::WriteGuard::new(self, self.$field_name.write(locked))
197 }
198 }
199 };
200 ($base_name:ident, $field_name:ident, $lock_level:ident) => {
201 ordered_state_accessor!($base_name, $field_name, $base_name, $lock_level);
202 };
203}
204
205/// Create the structs for the read and write guards using the methods defined inside the macro.
206macro_rules! state_implementation {
207 (impl $mutable_name:ident<Base=$base_name:ident> {
208 $(
209 $tt:tt
210 )*
211 }) => {
212 state_implementation! {
213 impl $mutable_name<Base = $base_name, BaseType = $base_name> {
214 $($tt)*
215 }
216 }
217 };
218 (impl $mutable_name:ident<Base=$base_name:ident, BaseType = $base_type:ty> {
219 $(
220 $tt:tt
221 )*
222 }) => {
223 paste::paste! {
224 #[allow(dead_code)]
225 pub type [<$base_name ReadGuard>]<'guard_lifetime> = $crate::mutable_state::ReadGuard<'guard_lifetime, $base_type, $mutable_name>;
226 #[allow(dead_code)]
227 pub type [<$base_name WriteGuard>]<'guard_lifetime> = $crate::mutable_state::WriteGuard<'guard_lifetime, $base_type, $mutable_name>;
228 #[allow(dead_code)]
229 pub type [<$base_name StateRef>]<'ref_lifetime> = $crate::mutable_state::StateRef<'ref_lifetime, $base_type, $mutable_name>;
230 #[allow(dead_code)]
231 pub type [<$base_name StateMutRef>]<'ref_lifetime> = $crate::mutable_state::StateMutRef<'ref_lifetime, $base_type, $mutable_name>;
232
233 impl<'guard, G: 'guard + std::ops::Deref<Target=$mutable_name>> $crate::mutable_state::Guard<'guard, $base_type, G> {
234 filter_methods_macro::filter_methods!(RoMethod, $($tt)*);
235 }
236
237 impl<'guard, G: 'guard + std::ops::DerefMut<Target=$mutable_name>> $crate::mutable_state::Guard<'guard, $base_type, G> {
238 filter_methods_macro::filter_methods!(RwMethod, $($tt)*);
239 }
240 }
241 };
242}
243
244pub struct Guard<'a, B, G> {
245 pub base: &'a B,
246 guard: G,
247}
248pub type ReadGuard<'a, B, S> = Guard<'a, B, RwLockReadGuard<'a, S>>;
249pub type WriteGuard<'a, B, S> = Guard<'a, B, RwLockWriteGuard<'a, S>>;
250pub type StateRef<'a, B, S> = Guard<'a, B, &'a S>;
251pub type StateMutRef<'a, B, S> = Guard<'a, B, &'a mut S>;
252
253impl<'a, B, S> Guard<'a, B, MutexGuard<'a, S>> {
254 pub fn unlocked<F, U>(s: &mut Self, f: F) -> U
255 where
256 F: FnOnce() -> U,
257 {
258 MutexGuard::unlocked(&mut s.guard, f)
259 }
260}
261
262impl<'guard, B, S, G: 'guard + Deref<Target = S>> Guard<'guard, B, G> {
263 pub fn new(base: &'guard B, guard: G) -> Self {
264 Self { base, guard }
265 }
266 pub fn as_ref(&self) -> StateRef<'_, B, S> {
267 Guard { base: self.base, guard: self.guard.deref() }
268 }
269}
270
271impl<'guard, B, S, G: 'guard + DerefMut<Target = S>> Guard<'guard, B, G> {
272 pub fn as_mut(&mut self) -> StateMutRef<'_, B, S> {
273 Guard { base: self.base, guard: self.guard.deref_mut() }
274 }
275}
276
277impl<'a, B, S, G: Deref<Target = S>> Deref for Guard<'a, B, G> {
278 type Target = S;
279 fn deref(&self) -> &Self::Target {
280 self.guard.deref()
281 }
282}
283
284impl<'a, B, S, G: DerefMut<Target = S>> DerefMut for Guard<'a, B, G> {
285 fn deref_mut(&mut self) -> &mut Self::Target {
286 self.guard.deref_mut()
287 }
288}
289
290// Public re-export of macros allows them to be used like regular rust items.
291pub(crate) use {ordered_state_accessor, state_accessor, state_implementation};
292
293#[cfg(test)]
294mod test {
295 use macro_rules_attribute::apply;
296 use starnix_sync::RwLock;
297
298 pub struct FooMutableState {
299 y: i32,
300 }
301
302 pub struct Foo {
303 x: i32,
304 mutable_state: RwLock<FooMutableState>,
305 }
306
307 impl Foo {
308 fn new() -> Self {
309 Self { x: 2, mutable_state: RwLock::new(FooMutableState { y: 3 }) }
310 }
311
312 state_accessor!(Foo, mutable_state);
313 }
314
315 #[apply(state_implementation!)]
316 impl FooMutableState<Base = Foo> {
317 // Some comment
318 fn x_and_y(&self) -> i32 {
319 self.base.x + self.y
320 }
321 /// Some rustdoc.
322 pub fn pub_x_and_y(&self) -> i32 {
323 self.x_and_y()
324 }
325 fn do_something(&self) {}
326 fn set_y(&mut self, other_y: i32) {
327 self.y = other_y;
328 }
329 pub fn pub_set_y(&mut self, other_y: i32) {
330 self.set_y(other_y)
331 }
332 fn do_something_mutable(&mut self) {
333 self.do_something();
334 }
335
336 #[allow(dead_code, clippy::needless_lifetimes)]
337 pub fn with_lifecycle<'a>(&self, _n: &'a u32) {}
338 #[allow(dead_code)]
339 pub fn with_type<T>(&self, _n: &T) {}
340 #[allow(dead_code)]
341 pub fn with_type_and_where<T>(&self, _n: &T)
342 where
343 T: Copy,
344 {
345 }
346 #[allow(dead_code)]
347 pub fn with_type_and_bound<T: Copy>(&self, _n: &T) {}
348 #[allow(dead_code)]
349 pub fn with_multiple_types_and_bound_and_where<T: Copy, U>(&self, _n: &T)
350 where
351 U: Copy,
352 {
353 }
354 #[allow(dead_code, clippy::needless_lifetimes)]
355 pub fn with_lifecycle_and_type<'a, T>(&self, _n: &'a T) {}
356 #[allow(dead_code, clippy::needless_lifetimes)]
357 pub fn with_lifecycle_on_self<'a, T>(&'a self, _n: &'a T) {}
358 }
359
360 fn take_foo_state(foo_state: &FooStateRef<'_>) -> i32 {
361 foo_state.pub_x_and_y()
362 }
363
364 #[::fuchsia::test]
365 fn test_generation() {
366 let foo = Foo::new();
367
368 assert_eq!(foo.read().x_and_y(), 5);
369 assert_eq!(foo.read().pub_x_and_y(), 5);
370 assert_eq!(foo.write().pub_x_and_y(), 5);
371 foo.write().set_y(22);
372 assert_eq!(foo.read().pub_x_and_y(), 24);
373 assert_eq!(foo.write().pub_x_and_y(), 24);
374 foo.write().pub_set_y(20);
375 assert_eq!(take_foo_state(&foo.read().as_ref()), 22);
376 assert_eq!(take_foo_state(&foo.write().as_ref()), 22);
377
378 foo.read().do_something();
379 foo.write().do_something();
380 foo.write().do_something_mutable();
381 }
382}