# RCU usage
## Simple
```clike=
struct myconfig { int a, b;} *curconfig;
/* Returns true if the global is not NULL */
bool get(int *cur_a, int *cur_b)
{
struct myconfig *mcp;
rcu_read_lock();
mcp = rcu_dereference(curconfig);
if (mcp) {
if (cur_a)
*cur_a = mcp->a;
if (cur_b)
*cur_b = mcp->b;
}
rcu_read_unlock();
return mcp != NULL;
}
// Typical Writer
bool set(int cur_a, int cur_b)
{
struct myconfig *mcp = kmalloc(...);
struct myconfig *oldmcp;
if (!mcp)
return false;
mcp->a = cur_a;
mcp->b = cur_b;
spin_lock(&mylock);
oldmcp = rcu_dereference_protected(curconfig, lockdep_is_held(&mylock));
rcu_assign_pointer(curconfig, mcp);
spin_unlock(&mylock);
synchronize_rcu();
kfree(oldmcp);
return true;
}
// Atomic Writer
bool set(int cur_a, int cur_b)
{
struct myconfig *mcp = kmalloc(...);
if (mcp)
return false;
mcp->a = cur_a;
mcp->b = cur_b;
mcp = xchg(&curconfig, mcp);
synchronize_rcu();
kfree(mcp);
return tru e;
}
```
## Rust version:
```rust=
/// RCU read-side critical section guards.
struct RcuGuard<'a> {
phantom: PhantomData<&'a>
}
fn rcu_read_lock<'a>() -> RcuGuard<'a> {
unsafe {
bindings::rcu_read_lock();
}
RcuGuard {
phantom: PhantomData
}
}
/// RAII: RcuGuard drop == unlock.
impl<'a> Drop for RcuGuard<'a> {
fn drop(&mut self) {
unsafe {
bindings::rcu_read_unlock();
}
}
}
/// A pointer to RCU-protected data.
struct RcuPtr<P: ForeignOwnable> {
ptr: AtomicPtr,
phantom: PhantomData<P>
}
struct RcuPtrOld<P: ForeignOwnable> {
ptr: *mut (),
phantom: PhantomData<P>
}
impl<P: ForeignOwnable> Drop for RcuPtrOld<P> {
fn drop(&mut self) {
// SAFETY: Always?
unsafe {
bindings::synchronize_rcu();
}
<P as ForeignOwnable>::from_foreign(self.ptr);
}
}
impl<P: ForeignOwnable> Drop for RcuPtr<P> {
fn drop(&mut self) {
// SAFETY: Always?
unsafe {
bindings::synchronize_rcu();
}
<P as ForeignOwnable>::from_foreign(self.ptr.load(Relaxed));
}
}
impl<P: ForeignOwnable> RcuPtr<P> {
pub fn new(p: P) -> Self {
RcuBox {
// INVARIANTS: Sets `self.ptr` to a pointer to boxed objects.
ptr: AtomicPtr::new(P::into_foreign(p)),
phantom: PhantomData
}
}
/// Update but not atomic
pub fn swap(&mut self, new: P) -> RcuBoxOld<T> {
// SAFETY: Per INVARIANTS
let old_ptr = self.ptr.load(Relaxed);
// INVARIANTS: Sets `self.ptr` to a pointer to boxed objects.
self.ptr.store(P::into_foreign(new), Release);
RcuBoxOld {
ptr: old_ptr,
phantom: PhantomData
}
}
/// Atomic update.
pub fn atomic_swap(&self, new: P) -> RcuBoxOld<T> {
// INVARIANTS: `new_ptr` always points to a boxed object.
let new_ptr = P::into_foreign(new);
let old_ptr = self.ptr.swap(new_ptr, Release);
// SAFETY: Per INVARIANTS
RcuBoxOld {
ptr: old_ptr,
phantom: PhantomData
}
}
/// rcu_dereference(), gets a immutable reference to the object.
///
/// Lifetime/borrow checking guarantee the reference cannot outlive RCU
/// read-side locks.
pub fn get<'a>(&self, guard: &RcuGuard<'a>) -> P::Borrowed<'a> {
let ptr = self.ptr.load(Acqure); // Need `Consume` but not available.
// SAFETY: Per INVARIANTS, `ptr` is a valid pointer,
// and RCU guarantees no data race.
unsafe {
P::borrow(ptr)
}
}
}
struct Global {
config: RcuBox<Config> // "struct config *", i.e. a pointer to the heap.
}
impl Global {
fn get(&self) -> (i32, i32) {
// "&self" is a reference to "Global"
// struct global *self = .. ;
let guard = rcu_read_lock();
let c = self.config.get(guard);
// struct config *c = self->config;
(c.a, c.b)
// implicit `drop(guard)` here
// `c` will not live longer then `guard`, so `c` cannot be used
// afer `guard` dropped.
}
fn atomic_set(&self, a: i32, b: i32) {
let new = Box::new(Config {a, b});
self.config.atomic_swap(new); // implicit drop the return value.
}
}
struct Config {
a: i32,
b: i32,
}
```
## List (TODO)
## Phased state change
```clike=
bool be_careful;
// Common-Case Operation
void cco(void)
{
rcu_read_lock();
if (READ_ONCE(be_careful))
cco_carefully();
else
cco_quickly();
rcu_read_unlock();
}
// Maintenance Operation
void maint(void)
{
WRITE_ONCE(be_careful, true);
synchronize_rcu();
do_maint();
synchronize_rcu();
WRITE_ONCE(be_careful, false);
}
```