use wasm_bindgen::prelude::*;
#[wasm_bindgen(raw_module = "./ferroui.js")]
extern "C" {
#[wasm_bindgen(js_namespace = FerroExports, js_name = proxiedCalls)]
fn js_proxied_calls() -> f64;
#[wasm_bindgen(js_namespace = FerroExports, js_name = lastProxiedFunction)]
fn js_last_proxied_function() -> f64;
#[wasm_bindgen(js_namespace = FerroExports, js_name = reportRenderThreadPanic)]
fn js_report_render_thread_panic(message: &str);
}
pub(crate) fn report_render_thread_panic(message: &str) {
js_report_render_thread_panic(message);
}
pub(crate) fn proxied_calls() -> Option<u64> {
let count = js_proxied_calls();
(count >= 0.0).then_some(count as u64)
}
pub(crate) fn last_proxied_function() -> Option<i64> {
let index = js_last_proxied_function();
(index >= 0.0).then_some(index as i64)
}
pub(crate) type ThreadWork = Box<dyn FnOnce() + Send>;
pub(crate) type RunOnThread = fn(usize, ThreadWork) -> bool;
pub fn current_thread() -> usize {
native::current_thread()
}
pub(crate) fn run_on_thread(thread: usize, work: ThreadWork) -> bool {
native::run_on_thread(thread, work)
}
#[cfg(all(target_os = "emscripten", target_feature = "atomics"))]
mod native {
use super::ThreadWork;
use std::ffi::c_void;
use std::sync::OnceLock;
#[repr(C)]
struct ProxyingQueue {
_opaque: [u8; 0],
}
extern "C" {
fn pthread_self() -> usize;
fn em_proxying_queue_create() -> *mut ProxyingQueue;
fn emscripten_proxy_async(
queue: *mut ProxyingQueue,
target_thread: usize,
func: extern "C" fn(*mut c_void),
arg: *mut c_void,
) -> bool;
}
static QUEUE: OnceLock<usize> = OnceLock::new();
pub(super) fn current_thread() -> usize {
unsafe { pthread_self() }
}
extern "C" fn run(arg: *mut c_void) {
let work = unsafe { Box::from_raw(arg.cast::<ThreadWork>()) };
work();
}
pub(super) fn run_on_thread(thread: usize, work: ThreadWork) -> bool {
let queue = *QUEUE.get_or_init(|| unsafe { em_proxying_queue_create() } as usize) as *mut ProxyingQueue;
if queue.is_null() || thread == 0 {
return false;
}
let arg = Box::into_raw(Box::new(work));
let queued = unsafe { emscripten_proxy_async(queue, thread, run, arg.cast()) };
if !queued {
drop(unsafe { Box::from_raw(arg) });
}
queued
}
}
#[cfg(not(all(target_os = "emscripten", target_feature = "atomics")))]
mod native {
use super::ThreadWork;
pub(super) fn current_thread() -> usize {
0
}
pub(super) fn run_on_thread(_thread: usize, _work: ThreadWork) -> bool {
false
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn without_threads_there_is_one_thread_and_nothing_is_queued() {
assert_eq!(0, current_thread());
assert!(!run_on_thread(1, Box::new(|| panic!("the work must not run"))));
}
}