-
Notifications
You must be signed in to change notification settings - Fork 2
Open
Description
Edit: Put this on hold, as Netstack3 does indeed have a tree, obfuscated as a long linear list. This needs to be made more powerful!
Since using this crate is very boilerplatey (and typo-prone as per issue #1) I’ve wrapped all that – inspired by just your holder struct. I see no tree, so I opted to name this “ladder” (could be staircase, elevator…) All it takes is
lock_ladder! {
pub(crate) Locks {
LockA: Mutex<u8>,
LockB: Mutex<u32>,
LockC: RwLock<MyStruct> = MyStruct(true),
}
}
// Accessing locked state looks like this:
let state = Locks::new();
// Create a new lock session with the "root" lock level (empty tuple).
let mut locked_unlocked = state.locked();
// Access locked state.
let (a, mut locked_a) = locked_unlocked.lock_and::<LockA>();
let (b, mut locked_b) = locked_a.lock_and::<LockB>();
{
let cw = locked_b.write_lock::<LockC>();
}
// This would not compile with locked_a, but, after dropping cw, locked_b is again the top
let cr = locked_b.read_lock::<LockC>();or
lock_ladder! {
const Locks {
LockA: Mutex<u8> = 0,
LockB: Mutex<u32> = 0,
LockC: RwLock<MyStruct> = MyStruct(true),
}
}
static STATE: Locks = Locks::new();
let mut locked_unlocked = STATE.locked();For now it’s a PoC. It needs some tweaking to make it exportable from your crate and maybe split it into 2 macros. For a PR to add it, I’d need to know what file-name you’d like to have and what changes you’d like to see.
/**
Wrap many locks in a typestate ladder, so they can only be obtained in that order.
Syntax is an optional visibility & `const` ans a name and a block. The block has any number of comma-separated
lock-specs. Each lock-spec is a handle-type-name ":" lock-pseudotype, and optionally: "=" initial value. If an initial
value, which is only for the inner Data type, is not given, the inner Data type must implement `Default`. That currently
prevents it from being `const`.
```
lock_ladder! {
pub(crate) Locks {
LockA: Mutex<u8>,
LockB: Mutex<u32>,
LockC: RwLock<MyStruct> = MyStruct(true),
}
}
let state = Locks::new();
let mut locked_unlocked = state.locked();
```
or
```
lock_ladder! {
const Locks {
LockA: Mutex<u8> = 0,
LockB: Mutex<u32> = 0,
LockC: RwLock<MyStruct> = MyStruct(true),
}
}
static STATE: Locks = Locks::new();
let mut locked_unlocked = STATE.locked();
```
`Mutex` and `RwLock` are macro-names here. They instruct this macro about those types.
For other locks you need to provide similar macros, like `TokioMutex` or `SnapshotLock`.
*/
macro_rules! lock_ladder {
($vis:vis const $holder:ident $spec:tt) => {
lock_ladder!(@ $vis const, $holder $spec);
};
($vis:vis $holder:ident $spec:tt) => {
lock_ladder!(@ $vis, $holder $spec);
};
(@ $vis:vis $($const:ident)?, $holder:ident { $($handle:ident: $lock:ident<$ty:ty> $(= $default:expr)?,)+ $(,)? }) => {
$($vis enum $handle {})+
lock_ladder!(< $($handle)+);
// reuse $handle+ as field names, but they don’t follow the convention
#[allow(non_snake_case)]
$vis struct $holder {
$($handle: $lock!($ty)),+
}
impl $holder {
$vis $($const)? fn new() -> Self {
Self {
// Are there any such types without a 1-param new()?
// Also use Default::default() for Data only, as some crates don’t impl Default.
$($handle: <$lock!($ty)>::new(lock_ladder!(= $($default)?))),+
}
}
$vis fn locked(&self) -> lock_tree::Locked<&Self, lock_tree::Unlocked> {
lock_tree::Locked::new(self)
}
}
$($lock!($holder, $handle, $ty);)+
};
(= $default:expr) => { $default };
(=) => { Default::default() };
(<< $handle1:ident $handle2:ident $($handle:ident)*) => {
lock_tree::impl_lock_after!($handle1 => $handle2);
lock_ladder!(<< $handle2 $($handle)*);
};
(<< $handle:ident) => {};
(< $handle1:ident $($handle:ident)*) => {
impl lock_tree::relation::LockAfter<lock_tree::Unlocked> for $handle1 {}
lock_ladder!(<< $handle1 $($handle)*);
};
}
// Have one macro like this for each similar type: TokioMutex, OrderedMutex…
macro_rules! Mutex {
($ty:ty) => {
std::sync::Mutex<$ty>
};
($holder:ident, $handle:ident, $ty:ty) => {
impl lock_tree::lock::LockFor<$handle> for $holder {
type Data = $ty;
type Guard<'l>
= std::sync::MutexGuard<'l, $ty>
where
Self: 'l;
fn lock(&self) -> Self::Guard<'_> {
self.$handle.lock().unwrap()
}
}
};
}
// Have one macro like this for each similar type: TokioRwLock, SnapshotLock…
macro_rules! RwLock {
($ty:ty) => {
std::sync::RwLock<$ty>
};
($holder:ident, $handle:ident, $ty:ty) => {
impl lock_tree::lock::RwLockFor<$handle> for $holder {
type Data = $ty;
type ReadGuard<'l>
= std::sync::RwLockReadGuard<'l, $ty>
where
Self: 'l;
type WriteGuard<'l>
= std::sync::RwLockWriteGuard<'l, $ty>
where
Self: 'l;
fn read_lock(&self) -> Self::ReadGuard<'_> {
self.$handle.read().unwrap()
}
fn write_lock(&self) -> Self::WriteGuard<'_> {
self.$handle.write().unwrap()
}
}
};
}Metadata
Metadata
Assignees
Labels
No labels