Skip to content

Commit 01275cc

Browse files
committed
rust: add revocalbe mutex
Based on sync/revokable.rs from c9be2e0 Signed-off-by: Danilo Krummrich <[email protected]>
1 parent a7d2265 commit 01275cc

File tree

2 files changed

+100
-0
lines changed

2 files changed

+100
-0
lines changed

rust/kernel/sync.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@ mod condvar;
1212
pub mod lock;
1313
mod locked_by;
1414
pub mod rcu;
15+
mod revocable;
1516

1617
pub use arc::{Arc, ArcBorrow, UniqueArc};
1718
pub use condvar::CondVar;
1819
pub use lock::{mutex::Mutex, spinlock::SpinLock};
1920
pub use locked_by::LockedBy;
21+
pub use revocable::RevocableMutex;
2022

2123
/// Represents a lockdep class. It's a wrapper around C's `lock_class_key`.
2224
#[repr(transparent)]

rust/kernel/sync/revocable.rs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! Synchronisation primitives where access to their contents can be revoked at runtime.
4+
5+
use macros::pin_data;
6+
7+
use crate::{
8+
init::PinInit,
9+
pin_init,
10+
str::CStr,
11+
sync::{lock, lock::Lock, LockClassKey},
12+
};
13+
use core::mem::MaybeUninit;
14+
15+
use super::lock::Guard;
16+
17+
/// The state within the revocable synchronisation primitive.
18+
///
19+
/// We don't use simply `Option<T>` because we need to drop in-place because the contents are
20+
/// implicitly pinned.
21+
///
22+
/// # Invariants
23+
///
24+
/// The `is_available` field determines if `data` is initialised.
25+
struct Inner<T> {
26+
is_available: bool,
27+
data: MaybeUninit<T>,
28+
}
29+
30+
impl<T> Inner<T> {
31+
fn new(data: T) -> Self {
32+
// INVARIANT: `data` is initialised and `is_available` is `true`, so the state matches.
33+
Self {
34+
is_available: true,
35+
data: MaybeUninit::new(data),
36+
}
37+
}
38+
39+
fn drop_in_place(&mut self) {
40+
if !self.is_available {
41+
// Already dropped.
42+
return;
43+
}
44+
45+
// INVARIANT: `data` is being dropped and `is_available` is set to `false`, so the state
46+
// matches.
47+
self.is_available = false;
48+
49+
// SAFETY: By the type invariants, `data` is valid because `is_available` was true.
50+
unsafe { self.data.assume_init_drop() };
51+
}
52+
}
53+
54+
impl<T> Drop for Inner<T> {
55+
fn drop(&mut self) {
56+
self.drop_in_place();
57+
}
58+
}
59+
60+
#[pin_data]
61+
pub struct Revocable<T, B: lock::Backend> {
62+
#[pin]
63+
inner: Lock<Inner<T>, B>,
64+
}
65+
66+
/// Safely initialises a [`Revocable`] instance with the given name, generating a new lock class.
67+
// #[macro_export]
68+
// macro_rules! revocable_init {
69+
// ($mutex:expr, $name:literal) => {
70+
// $crate::init_with_lockdep!($mutex, $name)
71+
// };
72+
// }
73+
74+
impl<T, B> Revocable<T, B>
75+
where
76+
B: lock::Backend,
77+
{
78+
/// Creates a new revocable instance of the given lock.
79+
pub fn new(data: T, name: &'static CStr, key: &'static LockClassKey) -> impl PinInit<Self> {
80+
pin_init!(Self {
81+
inner <- Lock::new(Inner::new(data), name, key) ,
82+
})
83+
}
84+
85+
/// Revokes access to and drops the wrapped object.
86+
///
87+
/// Revocation and dropping happen after ongoing accessors complete.
88+
pub fn revoke(&self) {
89+
self.lock().drop_in_place();
90+
}
91+
92+
fn lock(&self) -> Guard<'_, Inner<T>, B> {
93+
self.inner.lock()
94+
}
95+
}
96+
97+
/// Type alias for a `Revocable` with a `MutexBackend`.
98+
pub type RevocableMutex<T> = Revocable<T, super::lock::mutex::MutexBackend>;

0 commit comments

Comments
 (0)