sync-arena 0.2.0

A simple, thread-safe arena allocator.
Documentation
use lock_api::{GetThreadId, GuardSend, RawMutex};
use std::{
    num::NonZero,
    ops::Deref,
    ptr::NonNull,
    sync::atomic::{AtomicBool, Ordering},
};

// 1. Define our raw lock type
pub struct RawSpinlock(AtomicBool);

// 2. Implement RawMutex for this type
unsafe impl RawMutex for RawSpinlock {
    #[allow(clippy::declare_interior_mutable_const)]
    const INIT: RawSpinlock = RawSpinlock(AtomicBool::new(false));

    // A spinlock guard can be sent to another thread and unlocked there
    type GuardMarker = GuardSend;

    fn lock(&self) {
        // Note: This isn't the best way of implementing a spinlock, but it
        // suffices for the sake of this example.
        while !self.try_lock() {}
    }

    fn try_lock(&self) -> bool {
        self.0
            .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
            .is_ok()
    }

    unsafe fn unlock(&self) {
        self.0.store(false, Ordering::Release);
    }
}

/// Get an address that is unique per running thread.
///
/// This can be used as a non-null usize-sized ID.
pub(crate) fn current_thread_unique_ptr() -> NonZero<usize> {
    // Use a non-drop type to make sure it's still available during thread destruction.
    thread_local! { static X: u8 = const { 0 } }
    X.with(|x| <NonNull<_>>::addr(<&u8>::into(x)))
}

pub(crate) struct G;

unsafe impl GetThreadId for G {
    const INIT: Self = G;
    fn nonzero_thread_id(&self) -> std::num::NonZeroUsize {
        current_thread_unique_ptr()
    }
}

pub(crate) type Mutex<T> = lock_api::ReentrantMutex<RawSpinlock, G, T>;

impl<'s, T: 's> super::Reentrant<'s, T> for Mutex<T> {
    fn create(data: T) -> Self {
        Mutex::new(data)
    }
    fn reentrant_lock(&'s self) -> impl Deref<Target = T> + 's {
        self.lock()
    }
}