realloc 0.1.0

A re-implementation of various ::alloc features
Documentation
//! The framework for global allocators.
//!
//! When feature flag `system` is enabled, the global allocator is initially set
//! to that. In this case, since there's no way to clear the global allocator,
//! [`get`] will always return `Some`.

use core::cell::UnsafeCell;
use core::sync::atomic::{AtomicBool, Ordering};

use crate::Allocator;

struct Global {
    ptr: UnsafeCell<Option<&'static dyn Allocator>>,
    locked: AtomicBool,
}

unsafe impl Sync for Global {}

impl Global {
    #[allow(dead_code)]
    const fn new(allocator: &'static dyn Allocator) -> Self {
        Self {
            ptr: UnsafeCell::new(Some(allocator)),
            locked: AtomicBool::new(false),
        }
    }

    #[allow(dead_code)]
    const fn none() -> Self {
        Self {
            ptr: UnsafeCell::new(None),
            locked: AtomicBool::new(false),
        }
    }

    fn get(&self) -> Option<&'static dyn Allocator> {
        while self.locked.swap(true, Ordering::SeqCst) {
            core::hint::spin_loop();
        }

        // SAFETY:
        // - It's not being aliased, as the lock is currently held
        // - `Option<&T>` is always `Copy`
        let allocator = unsafe { *self.ptr.get() };
        self.locked.swap(false, Ordering::SeqCst);

        allocator
    }

    fn set(&self, allocator: Option<&'static dyn Allocator>) {
        while self.locked.swap(true, Ordering::SeqCst) {
            core::hint::spin_loop();
        }

        // SAFETY: it's not being aliased, as the lock is currently held
        unsafe { self.ptr.get().write(allocator) };
        self.locked.swap(false, Ordering::SeqCst);
    }
}

#[cfg(feature = "platform")]
static GLOBAL: Global = Global::new(&crate::platform::PLATFORM_ALLOCATOR);

#[cfg(not(feature = "platform"))]
static GLOBAL: Global = Global::none();

/// Retrieve the global allocator, if any has been set.
pub fn get() -> Option<&'static dyn Allocator> {
    GLOBAL.get()
}

/// Overwrite the global allocator.
///
/// Note that this doesn't take ownership, since it can't know how large the
/// allocator is, and can't allocate it itself; rather, it must take an
/// `'static` reference to it. Consider using a `static` and
/// [`Static`](crate::Static), or [`Box::leak`](crate::Box::leak).
pub fn set(allocator: &'static dyn Allocator) {
    GLOBAL.set(Some(allocator))
}