Skip to main content

scheme_rs/
threads.rs

1//! Threading primitives
2
3use std::{
4    fmt,
5    sync::Arc,
6    thread::{self, ThreadId},
7    time::Duration,
8};
9
10use parking_lot::Mutex;
11use scheme_rs_macros::bridge;
12
13use crate::{
14    exceptions::Exception,
15    gc::{Gc, Trace},
16    proc::{ContBarrier, Procedure},
17    records::{RecordTypeDescriptor, SchemeCompatible, rtd},
18    value::Value,
19};
20
21#[derive(Trace)]
22pub struct JoinHandle {
23    #[trace(skip)]
24    id: ThreadId,
25    result: Gc<Mutex<Result<Vec<Value>, Exception>>>,
26}
27
28impl fmt::Debug for JoinHandle {
29    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
30        Ok(())
31    }
32}
33
34impl SchemeCompatible for JoinHandle {
35    fn rtd() -> Arc<RecordTypeDescriptor> {
36        rtd!(name: "join-handle", sealed: true, opaque: true)
37    }
38}
39
40#[bridge(name = "spawn", lib = "(threads (1))")]
41pub fn spawn(thunk: Procedure) -> Result<Vec<Value>, Exception> {
42    let cell = Gc::new(Mutex::new(Ok(Vec::new())));
43    let cell_cloned = cell.clone();
44    let join_handle = thread::spawn(move || {
45        let mut cell_write = cell_cloned.lock();
46
47        #[cfg(not(feature = "async"))]
48        {
49            *cell_write = thunk.call(&[], &mut ContBarrier::new());
50        }
51
52        #[cfg(feature = "async")]
53        {
54            *cell_write = thunk.call_sync(&[], &mut ContBarrier::new());
55        }
56    });
57    let id = join_handle.thread().id();
58    Ok(vec![Value::from_rust_type(JoinHandle { id, result: cell })])
59}
60
61#[bridge(name = "join", lib = "(threads (1))")]
62pub fn join(handle: &Value) -> Result<Vec<Value>, Exception> {
63    let handle = handle.try_to_rust_type::<JoinHandle>()?;
64    let curr_id = thread::current().id();
65    if curr_id == handle.id {
66        Err(Exception::error(format!(
67            "thread {curr_id:?} attempted to join itself"
68        )))
69    } else {
70        handle.result.lock().clone()
71    }
72}
73
74#[bridge(name = "sleep", lib = "(threads (1))")]
75pub fn sleep(ms: u64) -> Result<Vec<Value>, Exception> {
76    thread::sleep(Duration::from_millis(ms));
77    Ok(Vec::new())
78}
79
80#[bridge(name = "join-handle?", lib = "(threads (1))")]
81pub fn join_handle_pred(obj: &Value) -> Result<Vec<Value>, Exception> {
82    Ok(vec![Value::from(
83        obj.cast_to_rust_type::<JoinHandle>().is_some(),
84    )])
85}