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::{LockDepGuard, LockDepReadGuard, LockDepWriteGuard, MutexGuard};
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::Guard::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::Guard::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, LockDepReadGuard<'a, S>>;
249pub type WriteGuard<'a, B, S> = Guard<'a, B, LockDepWriteGuard<'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 /// Executes the given closure while temporarily dropping the lock.
255 ///
256 /// The lock is dropped before the closure executes and re-acquired
257 /// after it finishes.
258 pub fn unlocked<F, U>(s: &mut Self, f: F) -> U
259 where
260 F: FnOnce() -> U,
261 {
262 MutexGuard::unlocked(&mut s.guard, f)
263 }
264}
265
266impl<'a, B, S> Guard<'a, B, LockDepGuard<'a, S>> {
267 /// Executes the given closure while temporarily dropping the lock.
268 ///
269 /// The lock is dropped before the closure executes and re-acquired
270 /// after it finishes.
271 pub fn unlocked<F, U>(s: &mut Self, f: F) -> U
272 where
273 F: FnOnce() -> U,
274 {
275 LockDepGuard::unlocked(&mut s.guard, f)
276 }
277}
278
279impl<'guard, B, S, G: 'guard + Deref<Target = S>> Guard<'guard, B, G> {
280 pub fn new(base: &'guard B, guard: G) -> Self {
281 Self { base, guard }
282 }
283 pub fn as_ref(&self) -> StateRef<'_, B, S> {
284 Guard { base: self.base, guard: self.guard.deref() }
285 }
286}
287
288impl<'guard, B, S, G: 'guard + DerefMut<Target = S>> Guard<'guard, B, G> {
289 pub fn as_mut(&mut self) -> StateMutRef<'_, B, S> {
290 Guard { base: self.base, guard: self.guard.deref_mut() }
291 }
292}
293
294impl<'a, B, S, G: Deref<Target = S>> Deref for Guard<'a, B, G> {
295 type Target = S;
296 fn deref(&self) -> &Self::Target {
297 self.guard.deref()
298 }
299}
300
301impl<'a, B, S, G: DerefMut<Target = S>> DerefMut for Guard<'a, B, G> {
302 fn deref_mut(&mut self) -> &mut Self::Target {
303 self.guard.deref_mut()
304 }
305}
306
307// Public re-export of macros allows them to be used like regular rust items.
308pub(crate) use ordered_state_accessor;
309pub(crate) use state_accessor;
310pub(crate) use state_implementation;
311
312#[cfg(test)]
313mod test {
314 use macro_rules_attribute::apply;
315 use starnix_sync::{LockDepRwLock, lock_ordering};
316 lock_ordering! {
317 Terminal(FooLock),
318 }
319
320 pub struct FooMutableState {
321 y: i32,
322 }
323
324 pub struct Foo {
325 x: i32,
326 mutable_state: LockDepRwLock<FooMutableState, FooLock>,
327 }
328
329 impl Foo {
330 fn new() -> Self {
331 Self { x: 2, mutable_state: FooMutableState { y: 3 }.into() }
332 }
333
334 state_accessor!(Foo, mutable_state);
335 }
336
337 #[apply(state_implementation!)]
338 impl FooMutableState<Base = Foo> {
339 // Some comment
340 fn x_and_y(&self) -> i32 {
341 self.base.x + self.y
342 }
343 /// Some rustdoc.
344 pub fn pub_x_and_y(&self) -> i32 {
345 self.x_and_y()
346 }
347 fn do_something(&self) {}
348 fn set_y(&mut self, other_y: i32) {
349 self.y = other_y;
350 }
351 pub fn pub_set_y(&mut self, other_y: i32) {
352 self.set_y(other_y)
353 }
354 fn do_something_mutable(&mut self) {
355 self.do_something();
356 }
357
358 #[allow(dead_code, clippy::needless_lifetimes)]
359 pub fn with_lifecycle<'a>(&self, _n: &'a u32) {}
360 #[allow(dead_code)]
361 pub fn with_type<T>(&self, _n: &T) {}
362 #[allow(dead_code)]
363 pub fn with_type_and_where<T>(&self, _n: &T)
364 where
365 T: Copy,
366 {
367 }
368 #[allow(dead_code)]
369 pub fn with_type_and_bound<T: Copy>(&self, _n: &T) {}
370 #[allow(dead_code)]
371 pub fn with_multiple_types_and_bound_and_where<T: Copy, U>(&self, _n: &T)
372 where
373 U: Copy,
374 {
375 }
376 #[allow(dead_code, clippy::needless_lifetimes)]
377 pub fn with_lifecycle_and_type<'a, T>(&self, _n: &'a T) {}
378 #[allow(dead_code, clippy::needless_lifetimes)]
379 pub fn with_lifecycle_on_self<'a, T>(&'a self, _n: &'a T) {}
380 }
381
382 fn take_foo_state(foo_state: &FooStateRef<'_>) -> i32 {
383 foo_state.pub_x_and_y()
384 }
385
386 #[::fuchsia::test]
387 fn test_generation() {
388 let foo = Foo::new();
389
390 assert_eq!(foo.read().x_and_y(), 5);
391 assert_eq!(foo.read().pub_x_and_y(), 5);
392 assert_eq!(foo.write().pub_x_and_y(), 5);
393 foo.write().set_y(22);
394 assert_eq!(foo.read().pub_x_and_y(), 24);
395 assert_eq!(foo.write().pub_x_and_y(), 24);
396 foo.write().pub_set_y(20);
397 assert_eq!(take_foo_state(&foo.read().as_ref()), 22);
398 assert_eq!(take_foo_state(&foo.write().as_ref()), 22);
399
400 foo.read().do_something();
401 foo.write().do_something();
402 foo.write().do_something_mutable();
403 }
404}