#[cfg(not(feature = "host-port"))]
use crate::sync::atomic::{AtomicUsize, Ordering};
#[cfg(not(feature = "host-port"))]
extern "C" {
static __task_stacks_start: u8;
static __task_stacks_end: u8;
}
#[cfg(not(feature = "host-port"))]
static NEXT: AtomicUsize = AtomicUsize::new(0);
#[cfg(not(feature = "host-port"))]
static ALLOC_COUNT: AtomicUsize = AtomicUsize::new(0);
#[cfg(not(feature = "host-port"))]
fn guard_size() -> usize {
crate::port::arch::min_guard_size()
}
#[cfg(not(feature = "host-port"))]
fn pool_base() -> usize {
core::ptr::addr_of!(__task_stacks_start) as usize
}
#[cfg(not(feature = "host-port"))]
fn pool_len() -> usize {
core::ptr::addr_of!(__task_stacks_end) as usize
- core::ptr::addr_of!(__task_stacks_start) as usize
}
pub fn alloc_stack(size: usize) -> Option<&'static mut [u8]> {
debug_assert!(
size.is_power_of_two(),
"rivet: task stack size {size} must be a power of two (MPU/PMP region alignment)"
);
#[cfg(not(feature = "host-port"))]
{
let guard_size = guard_size();
debug_assert!(
size >= guard_size,
"rivet: task stack size {size} is smaller than this hardware's minimum PMP/MPU \
guard band ({guard_size} bytes) — the guard alignment math below assumes a stack \
is always at least as large as its own guard band"
);
let base = pool_base();
let len = pool_len();
if let Some((off, sz)) = FREE_LIST.pop() {
if sz == size {
let stack_base = base + off;
return Some(unsafe {
core::slice::from_raw_parts_mut(stack_base as *mut u8, size)
});
}
FREE_LIST.push(off, sz);
}
let next = NEXT.load(Ordering::Relaxed);
let stack_base = (base + next + guard_size + size - 1) & !(size - 1);
let guard_base = stack_base - guard_size;
let offset = guard_base - base;
if offset + guard_size + size > len {
return None;
}
NEXT.store(offset + guard_size + size, Ordering::Relaxed);
let entry = ALLOC_COUNT.fetch_add(1, Ordering::Relaxed);
crate::port::arch::guard_register(guard_base, entry);
Some(unsafe { core::slice::from_raw_parts_mut(stack_base as *mut u8, size) })
}
#[cfg(feature = "host-port")]
{
let _ = size;
None
}
}
#[cfg(not(feature = "host-port"))]
const FREE_LIST_CAP: usize = 16;
#[cfg(not(feature = "host-port"))]
struct FreeList {
count: AtomicUsize,
entries: [AtomicUsize; FREE_LIST_CAP * 2],
}
#[cfg(not(feature = "host-port"))]
impl FreeList {
const fn new() -> Self {
Self {
count: AtomicUsize::new(0),
entries: [const { AtomicUsize::new(0) }; FREE_LIST_CAP * 2],
}
}
fn push(&self, offset: usize, size: usize) {
let n = self.count.load(Ordering::Relaxed);
if n < FREE_LIST_CAP {
self.entries[n * 2].store(offset, Ordering::Relaxed);
self.entries[n * 2 + 1].store(size, Ordering::Relaxed);
self.count.store(n + 1, Ordering::Relaxed);
}
}
fn pop(&self) -> Option<(usize, usize)> {
let n = self.count.load(Ordering::Relaxed);
if n == 0 {
return None;
}
let i = n - 1;
let off = self.entries[i * 2].load(Ordering::Relaxed);
let sz = self.entries[i * 2 + 1].load(Ordering::Relaxed);
self.count.store(i, Ordering::Relaxed);
Some((off, sz))
}
}
#[cfg(not(feature = "host-port"))]
static FREE_LIST: FreeList = FreeList::new();
pub fn release_stack(stack: &'static mut [u8]) {
#[cfg(not(feature = "host-port"))]
{
let base = pool_base();
let offset = stack.as_mut_ptr() as usize - base;
let size = stack.len();
if offset + size > pool_len() {
return; }
crate::critical::enter(|| {
crate::port::arch::scratch_open(stack.as_ptr() as usize, size);
for b in stack.iter_mut() {
*b = 0xAA;
}
crate::port::arch::scratch_close();
});
FREE_LIST.push(offset, size);
}
#[cfg(feature = "host-port")]
{
let _ = stack;
}
}
pub fn pool_bounds() -> (usize, usize) {
#[cfg(not(feature = "host-port"))]
{
(pool_base(), pool_len())
}
#[cfg(feature = "host-port")]
{
(0, 0)
}
}
pub fn contains(addr: usize) -> bool {
#[cfg(not(feature = "host-port"))]
{
addr >= pool_base() && addr < pool_base() + pool_len()
}
#[cfg(feature = "host-port")]
{
let _ = addr;
false
}
}
#[cfg(feature = "test-support")]
pub(crate) fn reset_for_test() {
#[cfg(not(feature = "host-port"))]
{
NEXT.store(0, Ordering::Relaxed);
ALLOC_COUNT.store(0, Ordering::Relaxed);
}
}