# Get familiar with Rust
```rust=
/*
* struct bar {
* unsigned long x;
* }
*
*/
struct Bar {
x: usize,
}
/*
* struct foo {
* struct bar b;
* int c;
* }
*/
struct Foo {
b: Bar
c: i32
}
/* Generic types
*
* in C we usuall do
*
* struct foo {
* spinlock_t lock;
* struct bar b;
* int c;
* }
*/
struct Lock<T> {
lock: SpinLock,
data: UnsafeCell<T>,
}
// struct foo foo = ...;
let foo: SpinLock<Foo> = ...;
```
## Notations
* `T1` -> `T2`: describe a function type, sometimes it's useful to say we want to have a function with a certain type. For example `kzalloc()` is `size_t` -> `void *`
## References and borrows
* `&T`: immutable reference: Read-only, can have multiple immutable references
* `&expr`: immutable borrow: gives an immutable reference.
```rust=
// struct foo *foo = &some_foo;
let foo: &Foo = &some_foo;
let b: &Foo = &some_foo;
// int c = foo->c;
let c: i32 = foo.c;
```
* `&mut T`: mutable reference: Exclusive, can only have one.
* `&mut expr`: mutable borrow: gives a mutable reference.
```rust=
// struct foo *foo = &some_foo;
let foo: &mut Foo = &mut some_foo;
// let b: &i32 = &some_foo; // won't compile
// let b: &mut i32 = &mut some_foo; // won't compile either
// foo->b->x = 1
foo.b.x = 1;
// *foo = struct foo { ... };
*foo = Foo { b: Bar {x : 1}, c: 12};
```
## Pointer and `unsafe`
* `*const T`: const pointer
* `*mut T`: mutable pointer
* `*mut T` <-> `*const T`, can cast to each other freely.
* dereference of a pointer is `unsafe`, since compiler cannot guarantee no-UB.
```rust=
let foo: *const Foo = &some_foo; // borrow expr can also provide a pointer
let c = unsafe { *foo }; // only work if Foo is Copy.
let reborrow: &Foo = unsafe { &*foo };
```
Note that having a `&mut` and `&` reference to the same location is generally considered as undefined behavior (bad bad):
```rust=
let global_ptr: *const i32 = &something;
<thread 1> <thread 2>
let x = unsafe { &mut *(global_ptr as *mut i32)};
let y = unsafe { &*global_ptr };
```
## Method and `&self`/`&mut self`
```rust=
impl Foo {
// void set_c(struct foo *self, int new)
pub fn set_c(&mut self, new: i32) {
self.c = new;
}
// int get_c(struct foo *self)
pub fn get_c(&self) -> i32 {
self.c
}
}
```
## interior mutable and `UnsafeCell`
How do I design a data type that support accesses from multiple threads?
* Attempt #1
```rust=
impl Foo {
pub fn set_c(&mut self, new: i32) { ... }
}
```
It doesn't work since previously we said having multiple mutable references is UB.
* Attempt #2
```rust=
impl Foo {
pub fn set_c(&self, new: i32) {
self.c = new; // ERROR, we cannot modify self.
}
}
```
* Building block: interior mutable, for example atomics
```rust=
impl<T> AtomicPtr<T> {
pub fn load(&self, ...) -> *mut T
pub fn store(&self, new: *mut T, ...);
}
```
The "read" and "write" to the data must be done via some API taking `&self` (and there is some synchronization behind it)
* Try interior mutable
```rust=
struct Foo {
lock: SpinLock,
b: Bar;
c: i32;
}
impl Foo {
pub fn set_c(&self, new: i32) {
self.lock.lock();
self.c = new; // STILL not working
self.lock.unlock();
}
}
```
* Building block: `UnsafeCell<T>`
`&T` and `&UnsafeCell<T>` are treated differently in the language.
```rust=
impl<T> UnsafeCell<T> {
fn get(&self) -> *mut T;
}
struct Foo {
lock: SpinLock,
b: Bar;
c: UnsafeCell<i32>;
}
impl Foo {
pub fn set_c(&self, new: i32) {
self.lock.lock();
unsafe { *self.c.get() } = new;
self.lock.unlock();
}
}
```
## Lock and Guard
```rust=
struct Lock<T> {
lock: SpinLock;
data: UnsafeCell<T>;
}
impl<T> Lock<T> {
fn lock(&self) -> Guard<'_, T> {
// spin_lock(&self->lock);
self.lock.lock();
return Guard { locked: self };
}
}
struct Guard<'a, T> {
lock: &'a Lock<T>
}
impl<'a, T> Drop for Guard<'a, T> {
fn drop(&mut self) {
// spin_unlock(&self->lock)
self.locked.unlock();
}
}
let foo: &'1 Lock<Foo> = ..;
let guard: &'2 Guard<'_, Foo> = foo.lock(); // '1 outlives '2
// use guard to access the locked data;
drop(guard); // unlock
```
## `Deref` and `DerefMut`
Define pointer/reference-like type
```rust=
impl<'a, T> Deref for Guard<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
let ptr = self
.locked // => &Lock<T>
.data // => &UnsafeCell<T>
.get(); // *mut T
// SAFETY: the existence of the Guard means
// we hold the lock.
unsafe { &*ptr }
}
}
impl<'a, T> DerefMut for Guard<'a, T> {
fn deref_mut(&self) -> &mut Self::Target {
let ptr = self
.locked // => &Lock<T>
.data // => &UnsafeCell<T>
.get(); // *mut T
// SAFETY: the existence of the Guard means
// we hold the lock.
unsafe { &mut *ptr }
}
}
let foo: &'1 Lock<Foo> = ..;
let guard: &'2 Guard<'_, Foo> = foo.lock(); // '1 outlives '2
let foo_ref: &'3 Foo = guard.deref();
drop(foo_ref); // '3 ends here
let foo_mut: &'4 Foo = guard.deref_mut();
foo_mut.c = 11;
drop(foo_mut); // '4 ends here
guard.c = 11; // works as well
// use guard to access the locked data;
drop(guard); // '2 ends here, unlock
```
# Here comes RCU
```rust=
struct RcuReadGuard;
fn read_lock() -> RcuReadGuard {
// rcu_read_lock()
rcu_read_lock();
return RcuReadGuard;
}
impl Drop for RcuReadGuard {
fn drop(&mut self) {
rcu_read_unlock();
}
}
let reader: RcuReadGuard = read_lock();
// RCU read-side critical section
//drop(reader);
```
## RCU pointer API
```rust=
/// A RCU protected pointer
/*
* struct rcu {
* void *ptr;
* }
*/
struct Rcu<T> {
ptr: AtomicPtr<T>
}
impl<T> Rcu<T> {
// rcu_dereference()
fn dereference<'a, 'rcu: 'a>(&'a self, &'rcu RcuReadGuard) -> &'a T {
unsafe { &*ptr.load(Acquire) };
}
// rcu_assign_pointer()
fn swap(&self, new: *mut T) -> RcuOld<T> {
// xchg_acqrel(self->ptr, new)
let old_ptr = ptr.swap(new, AcqRel);
return RcuOld { old_ptr };
}
}
struct RcuOld<T> {
ptr: *mut T
}
impl<T> Drop for RcuOld<T> {
fn drop(&mut self) {
synchronize_rcu();
// and then we can free the object
}
}
let r: &Rcu<i32> = ..;
let rcu_guard = read_lock();
let x: &'2 i32 = r.dereference(r, &'1 rcu_guard); // '1 outlives than '2
// drop(rcu_guard); ERROR
let v: i32 = *x;
```
## RCU pointer in a type
```rust=
/*
* struct foo {
* int a;
* struct bar __rcu *b;
* int __rcu *c;
* }
*/
struct Foo {
lock: SpinLock,
a: i32,
//a_: AtomicI32,
b: Rcu<Bar>,
c: Rcu<i32>,
}
/*
* struct foo {
* spinlock_t lock;
* int a;
* struct bar __rcu *b;
* int __rcu *c;
* }
*/
Lock<Foo>
NoLock<Foo>
```
We can certainly have a function do
* `Guard<'_, Foo>` -> `&Rcu<Bar>`
but we also want a:
* `&Lock<Foo>` -> `&Rcu<Bar>` for lockless reader.
`Guard<'_, Foo>` can be treated as `&mut Foo`, and `&Lock<Foo>` can be treated as `&Foo`. And the above two functions can run in the same time. Remember `&mut` and `&` at the same time is consider UB?
## Problem recap
* If we have a `Lock<Foo>`, we can get a `Guard<Foo>`
* `Guard<Foo>` can produce a `&mut Foo`, which allows us to access `Foo` freely (read and write)
* However, `&mut Foo` assumes no concurrent access, which is not true for RCU.
* We need to extend Rust Lock types to allow locklessly accesses.
## A magical way
```rust=
struct Foo {
a: i32,
// int a;
b: Rcu<Bar>,
// struct bar __rcu *b;
c: Rcu<i32>,
// int __rcu *c;
// int d; // access locklessly
d: AtomicU32;
}
let lock: Lock<Foo> = ...;
// struct foo {
// spinlock_t lock;
// int a;
// ...
// }
let rcu = rcu_read_lock();
// We magically get a reference to `b` from lock
let b_ref: &Bar = lock->b. // <= &Rcu<Bar>
dereference(&rcu);
let guard = lock.lock();
guard.d //
lock->d // &AtomicU32
let b_ref: &Bar =
lock.project::<field_of!(Foo, b)>().dereference(&rcu);
```
If `->` is possible, it can solve half of our problem. The other half is
* `Guard<Foo>` shouldn't be able to generate `&mut Foo`, but
* `Guard<Foo>` should provide a way to modify `Foo::a`, i.e.
projection just gives us a way to access a field without holding lock.
`Container<T>`, we cannot get a reference to `T`, and projection provides a way to get a reference to a field of `T` through the `Container<T>`.
`Pin<&mut T>`: a poitner to T, the it cannot be moved, and you cannot directly get a `Pin<&mut Bar>`
```rust=
let guard: Guard<Foo> = lock.lock();
let a_ref: &mut i32 = guard->a;
```
Another magic operator `->` for `Guard<Foo>`.
Actualy let's rename `Guard` to `Updater` since it make more sense for RCU
```rust=
struct Updater<T> {
lock: &Lock<T>
}
```
## Projections
Let's first try to implement `->b`
```rust=
impl Lock<Foo> {
pub fn project_b(&self) -> &Rcu<Bar> {
// struct Lock<T> {
// lock: SpinLock;
// data: UnsafeCell<T>;
// }
let ptr = self.lock.data.get(); // the address of `Foo`.
// return foo->b;
unsafe { & (*ptr).b }
}
}
```
This is not hard, right? We can also do that for `->c`
```rust=
impl Lock<Foo> {
pub fn project_c(&self) -> &Rcu<i32> {
// struct Lock<T> {
// lock: SpinLock;
// data: UnsafeCell<T>;
// }
let ptr = self.data.get(); // the address of `Foo`.
// return foo->c;
unsafe { & (*ptr).c }
}
}
```
Generify this
```rust=
impl<T> Lock<T> {
pub fn project<???>(&self) -> &Rcu<???> {
let ptr = self.data.get();
unsafe { & (*ptr).??? }
}
}
```
Introduce `Field` trait
```rust=
/// Describe a `Base` type has a field whose name is `name`
// and type is `Type`
// struct Base {
// ...
// name: Type
// ...
// }
pub trait Field<Base> {
type Type;
const name: &'static str;
fn field_of(base: *const Base) -> *const Self::Type;
}
```
Figure out the `???` above:
```rust=
impl<T> Lock<T> {
pub fn project<S, F: Field<T, Type=Rcu<S>>>(&self) -> &Rcu<S> {
let ptr = self.data.get();
unsafe { &* F::field_of(ptr) }
}
}
```
And we impl a type for `foo->b`
```rust=
struct FooB; // need to define an empty struct
impl Field<Foo> for FooB {
type Type = Bar;
const name: &'static str = "b";
fn field_of(base: *const Foo) -> *const Self::Type {
unsafe { core::ptr::addr_of!((*base).b) }
}
}
#[rcu_project]
struct Foo {
a: i32,
b: Rcu<Bar>
next: Rcu<Foo>,
...
}
let b_ref: &Bar =
lock.project::<field_of!(Foo, b)>().dereference(&rcu);
```
We can generate all `impl` for `Field<Foo>` with macros.
## The other half
```rust=
impl<T> Updater<T> {
pub fn project_mut<S, F: Field<T, Type=S>>(&self) -> &mut S {
let ptr = self.lock.data.get();
unsafe { &* F::field_of(ptr) }
}
}
```
## Rest of the API
```rust=
impl<T> Updater<T> {
// similar as
// spin_lock(&foo->lock);
// struct bar* b = foo->b;
// int x = b->x;
// spin_unlock(&foo->lock);
pub fn rcu_read<S, F: Field<T, Type=Rcu<S>>>(&self) -> &S {
let ptr = self.lock.data.get();
let rcu_ptr: &Rcu<S> = unsafe { &* F::field_of(ptr) };
&* rcu_ptr.load(Relaxed); // since we hold the lock
}
// similar as
// spin_lock(&foo->lock);
//
// struct bar *old = rcu_replace_pointer(foo->b, new);
// spin_unlock(&foo->lock);
// synchronze_rcu();
// kfree(old);
pub fn update<S, F: Field<T, Type=Rcu<S>>>(&self, new: Rcu<S>) -> RcuOld<S> {
let ptr = self.lock.data.get();
let rcu_ptr: &Rcu<S> = unsafe { &* F::field_of(ptr) };
rcu_ptr.swap(new) // swap can be non-atomic since we hold the lock.
}
}
impl Field<T> {
type Type = RcuHead<>;
}
```