← black metal kernel — series index

// black metal kernel — episode 06 of 08

the black_mutex:
a lock that earns its existence

// kernel programming in rust — zero-cost abstractions — no gc — no mercy

UnsafeCell RAII Drop DerefMut

// 06 — the black mutex: bounding mutability safely

interior mutability in rust breaks the strict mutable borrowing rules by tunneling through a shared reference (`&T`). in kernel context, it is indispensable for sharing state like framebuffers and thread allocators across multiple cores. true interior mutability is built upon `UnsafeCell`—the only core primitive that legally opts out of standard immutability invariants.

our `BlackMutex` wraps an `UnsafeCell` alongside the `BlackSpinlock` from our foundation. we utilize the RAII (Resource Acquisition Is Initialization) pattern to guarantee release upon scope exit. access is rigidly bound by a `BlackMutexGuard`, avoiding dangling locks during mid-function panics or early returns.

use core::cell::UnsafeCell;
use core::ops::{Deref, DerefMut};

pub struct BlackMutex<T> {
    black_lock: crate::sync::BlackSpinlock,
    black_data: UnsafeCell<T>,
}

unsafe impl<T: Send> Sync for BlackMutex<T> {}
unsafe impl<T: Send> Send for BlackMutex<T> {}

impl<T> BlackMutex<T> {
    pub const fn black_new(black_val: T) -> Self {
        Self {
            black_lock: crate::sync::BlackSpinlock::black_new(),
            black_data: UnsafeCell::new(black_val),
        }
    }

    pub fn black_lock(&self) -> BlackMutexGuard<'_, T> {
        self.black_lock.black_acquire();
        BlackMutexGuard {
            black_mutex: self,
        }
    }
}

pub struct BlackMutexGuard<'a, T> {
    black_mutex: &'a BlackMutex<T>,
}

impl<T> Deref for BlackMutexGuard<'_, T> {
    type Target = T;
    fn deref(&self) -> &T {
        unsafe { &*self.black_mutex.black_data.get() }
    }
}

impl<T> DerefMut for BlackMutexGuard<'_, T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { &mut *self.black_mutex.black_data.get() }
    }
}

impl<T> Drop for BlackMutexGuard<'_, T> {
    fn drop(&mut self) {
        self.black_mutex.black_lock.black_release();
    }
}
// expanded — UnsafeCell, sync constraints, and drop trait semantics

in conventional programming, a developer remembers to call `unlock()` at the bottom of the function. if an unexpected error handles a return midway, or the thread panics, the lock hangs globally. this creates fatal deadlocks. rust’s Drop trait intercepts the structure's lifetime end. whenever the `BlackMutexGuard` moves out of namespace scope, whether gracefully or through execution unwind, drop() fires and invokes our spinlock release. this shifts correctness out of human hands into structural guarantee.

UnsafeCell forms the heart of this pattern. you cannot pass a mutable reference `&mut T` between threads. you pass a shared reference `&BlackMutex`. inside `DerefMut`, `UnsafeCell::get()` produces a raw pointer `*mut T` matching the inner value, which we manually reconstruct into a safe `&mut T`. we guarantee memory safety here specifically because we mathematically surround this access with the atomic acquisition of our `black_lock`. if you bypass the guard, you bypass lock safety.

// 06 / 08 — black_ptr owns its truth — BLACK0X80