use std::sync::Mutex;
use boc_sys as ffi;
use ffi::scheduler_get;
use crate::log;
fn get() -> ffi::Scheduler {
unsafe { ffi::scheduler_get() }
}
static SCHED_LOCK: Mutex<()> = Mutex::new(());
struct DropGuard;
impl Drop for DropGuard {
fn drop(&mut self) {
unsafe { ffi::scheduler_run(get()) }
}
}
pub fn with_scheduler<T: Send>(f: impl FnOnce() -> T + Send) -> T {
with_inner(f, true, 1)
}
pub fn with_leak_detector<T>(f: impl FnOnce() -> T) -> T {
with_inner(f, true, 1)
}
pub fn with_n_threads<T: Send>(n_threads: usize, f: impl FnOnce() -> T + Send) -> T {
with_inner(f, false, n_threads)
}
fn with_inner<T, F: FnOnce() -> T>(f: F, detect_leaks: bool, n_threads: usize) -> T {
let lock = SCHED_LOCK.lock();
unsafe {
ffi::scheduler_init(scheduler_get(), n_threads);
if detect_leaks {
ffi::schedular_set_detect_leaks(true);
}
}
let dg = DropGuard;
let result = f();
drop(dg);
if detect_leaks {
unsafe {
log(c"running leak detector");
if ffi::schedular_has_leaks() {
panic!("leaks detected");
}
ffi::schedular_set_detect_leaks(false)
}
}
drop(lock);
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn basic_run() {
with_scheduler(|| {});
}
#[test]
fn run_from_multi_threads() {
stdx::thread::scope(|s| {
for _ in 0..10 {
s.spawn(|| {
for _ in 0..100 {
with_scheduler(|| {});
}
});
}
});
}
#[test]
#[ignore = "https://github.com/aDotInTheVoid/boxcars/issues/4"]
fn panic_safe() {
stdx::thread::scope(|s| {
for _ in 0..10 {
s.spawn(|| {
for _ in 0..10 {
let r = std::panic::catch_unwind(|| with_scheduler(|| panic!("lol lmao")));
let r_err = r.unwrap_err();
let s = r_err.downcast::<&str>().unwrap();
assert_eq!(&**s, "lol lmao");
}
});
}
})
}
}