use crate::stack::{Stack, pop, push};
use crate::value::Value;
use std::collections::HashMap;
use std::sync::{LazyLock, Mutex};
type ClosureEntry = (usize, Box<[Value]>);
static SPAWN_CLOSURE_REGISTRY: LazyLock<Mutex<HashMap<i64, ClosureEntry>>> =
LazyLock::new(|| Mutex::new(HashMap::new()));
struct SpawnRegistryGuard {
closure_spawn_id: i64,
should_cleanup: bool,
}
impl SpawnRegistryGuard {
fn new(closure_spawn_id: i64) -> Self {
Self {
closure_spawn_id,
should_cleanup: true,
}
}
fn disarm(&mut self) {
self.should_cleanup = false;
}
}
impl Drop for SpawnRegistryGuard {
fn drop(&mut self) {
if self.should_cleanup {
let mut registry = SPAWN_CLOSURE_REGISTRY.lock().unwrap();
if let Some((_, env)) = registry.remove(&self.closure_spawn_id) {
drop(env);
}
}
}
}
extern "C" fn closure_spawn_trampoline(stack: Stack) -> Stack {
unsafe {
let (stack, closure_spawn_id_val) = pop(stack);
let closure_spawn_id = match closure_spawn_id_val {
Value::Int(id) => id,
_ => panic!(
"closure_spawn_trampoline: expected Int (closure_spawn_id), got {:?}",
closure_spawn_id_val
),
};
let (fn_ptr, env) = {
let mut registry = SPAWN_CLOSURE_REGISTRY.lock().unwrap();
registry.remove(&closure_spawn_id).unwrap_or_else(|| {
panic!(
"closure_spawn_trampoline: no data for closure_spawn_id {}",
closure_spawn_id
)
})
};
let env_ptr = env.as_ptr();
let env_len = env.len();
let fn_ref: unsafe extern "C" fn(Stack, *const Value, usize) -> Stack =
std::mem::transmute(fn_ptr);
fn_ref(stack, env_ptr, env_len)
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_push_quotation(
stack: Stack,
wrapper: usize,
impl_: usize,
) -> Stack {
debug_assert!(
wrapper != 0,
"push_quotation: wrapper function pointer is null"
);
debug_assert!(impl_ != 0, "push_quotation: impl function pointer is null");
unsafe { push(stack, Value::Quotation { wrapper, impl_ }) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_peek_is_quotation(stack: Stack) -> i64 {
use crate::stack::peek;
unsafe {
let value = peek(stack);
match value {
Value::Quotation { .. } => 1,
_ => 0,
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_peek_quotation_fn_ptr(stack: Stack) -> usize {
use crate::stack::peek;
unsafe {
let value = peek(stack);
match value {
Value::Quotation { impl_, .. } => {
debug_assert!(
impl_ != 0,
"peek_quotation_fn_ptr: impl function pointer is null"
);
impl_
}
_ => {
debug_assert!(
false,
"peek_quotation_fn_ptr: expected Quotation, got {:?}",
value
);
0
}
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_call(stack: Stack) -> Stack {
unsafe {
let (stack, value) = pop(stack);
match value {
Value::Quotation { wrapper, .. } => {
if wrapper == 0 {
panic!("call: quotation wrapper function pointer is null");
}
let fn_ref: unsafe extern "C" fn(Stack) -> Stack = std::mem::transmute(wrapper);
fn_ref(stack)
}
Value::Closure { fn_ptr, env } => {
if fn_ptr == 0 {
panic!("call: closure function pointer is null");
}
let env_data = env.as_ptr();
let env_len = env.len();
let fn_ref: unsafe extern "C" fn(Stack, *const Value, usize) -> Stack =
std::mem::transmute(fn_ptr);
fn_ref(stack, env_data, env_len)
}
_ => panic!(
"call: expected Quotation or Closure on stack, got {:?}",
value
),
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_spawn(stack: Stack) -> Stack {
use crate::scheduler::patch_seq_strand_spawn_with_base;
use crate::stack::clone_stack_with_base;
unsafe {
let (stack, value) = pop(stack);
match value {
Value::Quotation { wrapper, .. } => {
if wrapper == 0 {
panic!("spawn: quotation wrapper function pointer is null");
}
let fn_ref: extern "C" fn(Stack) -> Stack = std::mem::transmute(wrapper);
let (child_stack, child_base) = clone_stack_with_base(stack);
let strand_id = patch_seq_strand_spawn_with_base(fn_ref, child_stack, child_base);
push(stack, Value::Int(strand_id))
}
Value::Closure { fn_ptr, env } => {
if fn_ptr == 0 {
panic!("spawn: closure function pointer is null");
}
use std::sync::atomic::{AtomicI64, Ordering};
static NEXT_CLOSURE_SPAWN_ID: AtomicI64 = AtomicI64::new(1);
let closure_spawn_id = NEXT_CLOSURE_SPAWN_ID.fetch_add(1, Ordering::Relaxed);
{
let env_box: Box<[Value]> = env.iter().cloned().collect();
let mut registry = SPAWN_CLOSURE_REGISTRY.lock().unwrap();
registry.insert(closure_spawn_id, (fn_ptr, env_box));
}
let mut guard = SpawnRegistryGuard::new(closure_spawn_id);
let stack_base = crate::stack::alloc_stack();
let initial_stack = push(stack_base, Value::Int(closure_spawn_id));
let strand_id = patch_seq_strand_spawn_with_base(
closure_spawn_trampoline,
initial_stack,
stack_base,
);
guard.disarm();
push(stack, Value::Int(strand_id))
}
_ => panic!("spawn: expected Quotation or Closure, got {:?}", value),
}
}
}
#[inline]
pub unsafe fn invoke_callable(stack: Stack, callable: &Value) -> Stack {
unsafe {
match callable {
Value::Quotation { wrapper, .. } => {
let fn_ref: unsafe extern "C" fn(Stack) -> Stack = std::mem::transmute(*wrapper);
fn_ref(stack)
}
Value::Closure { fn_ptr, env } => {
let fn_ref: unsafe extern "C" fn(Stack, *const Value, usize) -> Stack =
std::mem::transmute(*fn_ptr);
fn_ref(stack, env.as_ptr(), env.len())
}
_ => panic!(
"invoke_callable: expected Quotation or Closure, got {:?}",
callable
),
}
}
}
pub use patch_seq_call as call;
pub use patch_seq_push_quotation as push_quotation;
pub use patch_seq_spawn as spawn;
#[cfg(test)]
mod tests;