use crate::stack::Stack;
use crate::tagged_stack::StackValue;
use may::coroutine;
use std::sync::atomic::{AtomicU64, Ordering};
use super::{
ACTIVE_STRANDS, PEAK_STRANDS, SHUTDOWN_CONDVAR, SHUTDOWN_MUTEX, TOTAL_COMPLETED, TOTAL_SPAWNED,
};
static NEXT_STRAND_ID: AtomicU64 = AtomicU64::new(1);
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_strand_spawn(
entry: extern "C" fn(Stack) -> Stack,
initial_stack: Stack,
) -> i64 {
unsafe { patch_seq_strand_spawn_with_base(entry, initial_stack, std::ptr::null_mut()) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_strand_spawn_with_base(
entry: extern "C" fn(Stack) -> Stack,
initial_stack: Stack,
stack_base: Stack,
) -> i64 {
let strand_id = NEXT_STRAND_ID.fetch_add(1, Ordering::Relaxed);
let new_count = ACTIVE_STRANDS.fetch_add(1, Ordering::Release) + 1;
TOTAL_SPAWNED.fetch_add(1, Ordering::Relaxed);
let mut peak = PEAK_STRANDS.load(Ordering::Acquire);
while new_count > peak {
match PEAK_STRANDS.compare_exchange_weak(
peak,
new_count,
Ordering::Release,
Ordering::Relaxed,
) {
Ok(_) => break,
Err(current) => peak = current,
}
}
#[cfg(feature = "diagnostics")]
let _ = super::registry::strand_registry().register(strand_id);
let entry_fn = entry;
let stack_addr = initial_stack as usize;
let base_addr = stack_base as usize;
unsafe {
coroutine::spawn(move || {
let stack_ptr = stack_addr as *mut StackValue;
let base_ptr = base_addr as *mut StackValue;
debug_assert!(
stack_ptr.is_null()
|| stack_addr.is_multiple_of(std::mem::align_of::<StackValue>()),
"Stack pointer must be null or properly aligned"
);
debug_assert!(
stack_ptr.is_null() || stack_addr > 0x1000,
"Stack pointer appears to be in invalid memory region (< 0x1000)"
);
if !base_ptr.is_null() {
crate::stack::patch_seq_set_stack_base(base_ptr);
}
let final_stack = entry_fn(stack_ptr);
free_stack(final_stack);
#[cfg(feature = "diagnostics")]
super::registry::strand_registry().unregister(strand_id);
let prev_count = ACTIVE_STRANDS.fetch_sub(1, Ordering::AcqRel);
TOTAL_COMPLETED.fetch_add(1, Ordering::Release);
if prev_count == 1 {
let _guard = SHUTDOWN_MUTEX.lock()
.expect("strand_spawn: shutdown mutex poisoned - strand panicked during shutdown notification");
SHUTDOWN_CONDVAR.notify_all();
}
});
}
strand_id as i64
}
pub(super) fn free_stack(_stack: Stack) {
crate::arena::arena_reset();
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_spawn_strand(entry: extern "C" fn(Stack) -> Stack) {
unsafe {
patch_seq_strand_spawn(entry, std::ptr::null_mut());
}
}