use crate::error::{Error, Result};
use crate::sys;
use std::os::raw::c_void;
use std::panic::{self, AssertUnwindSafe};
use std::ptr;
struct BlockingEntry<F> {
f: Option<F>,
}
pub fn call<F, T>(f: F) -> Result<std::thread::Result<T>>
where
F: FnOnce() -> T + Send + 'static,
T: Send + 'static,
{
let entry = Box::new(BlockingEntry { f: Some(f) });
let arg = Box::into_raw(entry) as *mut c_void;
let mut out = ptr::null_mut();
let rc = unsafe { sys::llam_call_blocking_result(trampoline::<F, T>, arg, &mut out) };
if rc != 0 {
unsafe {
drop(Box::from_raw(arg as *mut BlockingEntry<F>));
}
return Err(Error::last());
}
if out.is_null() {
return Err(Error::from_errno(libc::EIO));
}
Ok(unsafe { *Box::from_raw(out as *mut std::thread::Result<T>) })
}
unsafe extern "C" fn trampoline<F, T>(arg: *mut c_void) -> *mut c_void
where
F: FnOnce() -> T + Send + 'static,
T: Send + 'static,
{
let mut entry = Box::from_raw(arg as *mut BlockingEntry<F>);
let f = entry.f.take().expect("LLAM blocking closure missing");
Box::into_raw(Box::new(panic::catch_unwind(AssertUnwindSafe(f)))) as *mut c_void
}
pub struct BlockingRegion {
active: bool,
}
impl BlockingRegion {
pub fn enter() -> Result<Self> {
let rc = unsafe { sys::llam_enter_blocking() };
if rc == 0 {
Ok(Self { active: true })
} else {
Err(Error::last())
}
}
}
impl Drop for BlockingRegion {
fn drop(&mut self) {
if self.active {
unsafe {
let _ = sys::llam_leave_blocking();
}
self.active = false;
}
}
}