scheme-rs 0.2.0

Embedded scheme for the Rust ecosystem
Documentation
//! Threading primitives

use std::{
    fmt,
    sync::Arc,
    thread::{self, ThreadId},
    time::Duration,
};

use parking_lot::Mutex;
use scheme_rs_macros::bridge;

use crate::{
    exceptions::Exception,
    gc::{Gc, Trace},
    proc::{ContBarrier, Procedure},
    records::{RecordTypeDescriptor, SchemeCompatible, rtd},
    value::Value,
};

#[derive(Trace)]
pub struct JoinHandle {
    #[trace(skip)]
    id: ThreadId,
    result: Gc<Mutex<Result<Vec<Value>, Exception>>>,
}

impl fmt::Debug for JoinHandle {
    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
        Ok(())
    }
}

impl SchemeCompatible for JoinHandle {
    fn rtd() -> Arc<RecordTypeDescriptor> {
        rtd!(name: "join-handle", sealed: true, opaque: true)
    }
}

#[bridge(name = "spawn", lib = "(threads (1))")]
pub fn spawn(thunk: Procedure) -> Result<Vec<Value>, Exception> {
    let cell = Gc::new(Mutex::new(Ok(Vec::new())));
    let cell_cloned = cell.clone();
    let join_handle = thread::spawn(move || {
        let mut cell_write = cell_cloned.lock();

        #[cfg(not(feature = "async"))]
        {
            *cell_write = thunk.call(&[], &mut ContBarrier::new());
        }

        #[cfg(feature = "async")]
        {
            *cell_write = thunk.call_sync(&[], &mut ContBarrier::new());
        }
    });
    let id = join_handle.thread().id();
    Ok(vec![Value::from_rust_type(JoinHandle { id, result: cell })])
}

#[bridge(name = "join", lib = "(threads (1))")]
pub fn join(handle: &Value) -> Result<Vec<Value>, Exception> {
    let handle = handle.try_to_rust_type::<JoinHandle>()?;
    let curr_id = thread::current().id();
    if curr_id == handle.id {
        Err(Exception::error(format!(
            "thread {curr_id:?} attempted to join itself"
        )))
    } else {
        handle.result.lock().clone()
    }
}

#[bridge(name = "sleep", lib = "(threads (1))")]
pub fn sleep(ms: u64) -> Result<Vec<Value>, Exception> {
    thread::sleep(Duration::from_millis(ms));
    Ok(Vec::new())
}

#[bridge(name = "join-handle?", lib = "(threads (1))")]
pub fn join_handle_pred(obj: &Value) -> Result<Vec<Value>, Exception> {
    Ok(vec![Value::from(
        obj.cast_to_rust_type::<JoinHandle>().is_some(),
    )])
}