blob: 17239a7d2018499890229ded29e287b1501e5a95 [file] [view]
# KMutex: Technical Design Document
This document covers the technical architecture, safety invariants, and macro
code generation details of the `ksync` crate.
## 1. Technical Architecture
`ksync` implements a **token-based synchronization pattern** (also known as
the "Ghost Token" pattern). Instead of encapsulating the protected data inside
the mutex struct itself (like `std::sync::Mutex<T>`), it separates the lock
state (`KMutex`) from the actual data storage (`KCell`).
```
+-------------------------------------------------------------+
| Parent Struct |
| |
| +----------------+ +------------------------+ |
| | KMutex<Class> | | KCell<T, Class> | |
| | (Raw Lock) | | (UnsafeCell wrapper) | |
| +--------+-------+ +-----------+------------+ |
+------------|-------------------------------|----------------+
| lock() | get(token)
v v
+--------+------------+ +--------+-------+
| KMutexGuard | | &T / &mut T |
| |-------->| (Safe Access) |
| - LockToken | +----------------+
+---------------------+
```
### The Three Core Types
1. **`LockToken<'a, Class>`**: A zero-sized type (ZST) that serves as
compile-time proof that the exclusive lock for `Class` is currently held by
the current thread. It has a lifetime `'a` bound to the active
`KMutexGuard`.
2. **`KMutex<Class>`**: The lock state representation. It wraps a raw mutex
(e.g., `fuchsia_sync::RawMutex`). Locking it acquires the raw lock and
returns a `KMutexGuard` holding the ZST `LockToken`.
3. **`KCell<T, Class>`**: A wrapper around `core::cell::UnsafeCell<T>`. It
provides token-gated accessors (`get(&self, token)` and `get_mut(&self,
token)`).
### Guard Types & The Instance-Bound Soundness Model
While the `LockToken` and `KCell` share a compile-time `Class` type parameter
to prevent mixing locks of different types, this type-level association is
insufficient to guarantee memory safety.
To provide safety, the macro generates Guard structures (`MyStructMuGuard`)
that provide a safe wrapper that provides the instance-level association. The
Guard holds a reference to the specific parent struct instance (`parent: &'a
MyStruct`) and the active lock state (`inner: KMutexGuard<'a,
MyStructMuClass>`), providing the link needed to make accessing each `KCell`
safe.
## 2. Safe Exclusive Access
If a thread has unique exclusive access to the `KCell` container itself
(either by holding ownership `self` or an exclusive borrow `&mut self`), it
doesn't need a runtime lock token to access the data safely. The Rust borrow
checker already guarantees compile-time thread exclusivity:
* **`get_inner_mut(&mut self) -> &mut T`**: Accesses the inner value mutably
via `UnsafeCell::get_mut()`. This is safe because the `&mut self` borrow
ensures no other borrows of the cell are active.
* **`into_inner(self) -> T`**: Consumes the `KCell` container and returns the
inner value `T` safely via `UnsafeCell::into_inner()`.
## 3. Macro Code Generation
The `#[guarded]` attribute proc-macro parses a struct definition, rewrites its
fields, and generates the accompanying safe Guard and Split Accessor
structures.
### 3.1 Input Struct
```rust
#[guarded]
pub struct MyStruct {
#[mutex]
pub mu: KMutex,
#[guarded_by(mu)]
pub data1: u32,
#[guarded_by(mu)]
data2: i32,
}
```
### 3.2 Expanded Code Output (Simplified)
```rust
// 1. Unique Lock Class marker struct generated automatically
pub struct MyStructMuClass;
// 2. Struct fields rewritten to KMutex<Class> and KCell<T, Class>
pub struct MyStruct {
pub mu: ::ksync::KMutex<MyStructMuClass>,
pub data1: ::ksync::KCell<u32, MyStructMuClass>,
data2: ::ksync::KCell<i32, MyStructMuClass>,
}
// 3. Custom Guard generated with safe target accessors and lifetime bindings
pub struct MyStructMuGuard<'a> {
parent: &'a MyStruct,
inner: ::ksync::KMutexGuard<'a, MyStructMuClass>,
}
impl<'a> MyStructMuGuard<'a> {
// Individual Accessors (matching original field visibilities)
#[inline]
pub fn data1(&self) -> &u32 {
// SAFETY: The token is obtained from the same parent instance (self.parent)
// that contains the cell, satisfying the KCell safety invariant.
unsafe { self.parent.data1.get(self.inner.token()) }
}
#[inline]
pub fn data1_mut(&mut self) -> &mut u32 {
// SAFETY: The token is obtained from the same parent instance (self.parent)
// that contains the cell, satisfying the KCell safety invariant.
unsafe { self.parent.data1.get_mut(self.inner.token_mut()) }
}
#[inline]
fn data2(&self) -> &i32 {
// SAFETY: The token is obtained from the same parent instance (self.parent)
// that contains the cell, satisfying the KCell safety invariant.
unsafe { self.parent.data2.get(self.inner.token()) }
}
#[inline]
fn data2_mut(&mut self) -> &mut i32 {
// SAFETY: The token is obtained from the same parent instance (self.parent)
// that contains the cell, satisfying the KCell safety invariant.
unsafe { self.parent.data2.get_mut(self.inner.token_mut()) }
}
#[inline]
pub fn fields<'b>(&'b self) -> MyStructMuFields<'b> {
MyStructMuFields {
// SAFETY: The token is from the same parent instance as the cell.
data1: unsafe { self.parent.data1.get(self.inner.token()) },
data2: unsafe { self.parent.data2.get(self.inner.token()) },
_marker: ::core::marker::PhantomData,
}
}
#[inline]
pub fn fields_mut<'b>(&'b mut self) -> MyStructMuFieldsMut<'b> {
let token = self.inner.token_mut();
// SAFETY:
// 1. We have exclusive access to the Guard (&mut self).
// 2. The fields in the struct are disjoint.
// 3. The returned references are bound to the lifetime 'b of the guard borrow,
// preventing them from outliving the guard.
unsafe {
MyStructMuFieldsMut {
data1: &mut *self.parent.data1.as_mut_ptr(token),
data2: &mut *self.parent.data2.as_mut_ptr(token),
_marker: ::core::marker::PhantomData,
}
}
}
}
// 4. Helper split structs generated to hold the disjoint borrows
pub struct MyStructMuFields<'b> {
pub data1: &'b u32,
data2: &'b i32,
_marker: ::core::marker::PhantomData<(&'b (), )>,
}
pub struct MyStructMuFieldsMut<'b> {
pub data1: &'b mut u32,
data2: &'b mut i32,
_marker: ::core::marker::PhantomData<(&'b (), )>,
}
// 5. Lock method impl on the parent struct
impl MyStruct {
#[inline]
pub fn lock_mu(&self) -> MyStructMuGuard<'_> {
MyStructMuGuard {
parent: self,
inner: self.mu.lock(),
}
}
}
```