96 lines
2.5 KiB
Rust
96 lines
2.5 KiB
Rust
use std::fmt::{self, Debug, Formatter};
|
|
use std::marker::PhantomData;
|
|
use std::mem;
|
|
use std::sync::OnceLock;
|
|
|
|
use wasm_bindgen::prelude::wasm_bindgen;
|
|
use wasm_bindgen::{JsCast, JsValue};
|
|
|
|
use super::r#async::{self, Sender};
|
|
|
|
thread_local! {
|
|
static MAIN_THREAD: bool = {
|
|
#[wasm_bindgen]
|
|
extern "C" {
|
|
#[derive(Clone)]
|
|
type Global;
|
|
|
|
#[wasm_bindgen(method, getter, js_name = Window)]
|
|
fn window(this: &Global) -> JsValue;
|
|
}
|
|
|
|
let global: Global = js_sys::global().unchecked_into();
|
|
!global.window().is_undefined()
|
|
};
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub struct MainThreadMarker(PhantomData<*const ()>);
|
|
|
|
impl MainThreadMarker {
|
|
pub fn new() -> Option<Self> {
|
|
MAIN_THREAD.with(|is| is.then_some(Self(PhantomData)))
|
|
}
|
|
}
|
|
|
|
pub struct MainThreadSafe<T: 'static>(Option<T>);
|
|
|
|
impl<T> MainThreadSafe<T> {
|
|
pub fn new(_: MainThreadMarker, value: T) -> Self {
|
|
DROP_HANDLER.get_or_init(|| {
|
|
let (sender, receiver) = r#async::channel();
|
|
wasm_bindgen_futures::spawn_local(
|
|
async move { while receiver.next().await.is_ok() {} },
|
|
);
|
|
|
|
sender
|
|
});
|
|
|
|
Self(Some(value))
|
|
}
|
|
|
|
pub fn into_inner(mut self, _: MainThreadMarker) -> T {
|
|
self.0.take().expect("already taken or dropped")
|
|
}
|
|
|
|
pub fn get(&self, _: MainThreadMarker) -> &T {
|
|
self.0.as_ref().expect("already taken or dropped")
|
|
}
|
|
}
|
|
|
|
impl<T: Debug> Debug for MainThreadSafe<T> {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
if MainThreadMarker::new().is_some() {
|
|
f.debug_tuple("MainThreadSafe").field(&self.0).finish()
|
|
} else {
|
|
f.debug_struct("MainThreadSafe").finish_non_exhaustive()
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T> Drop for MainThreadSafe<T> {
|
|
fn drop(&mut self) {
|
|
if let Some(value) = self.0.take() {
|
|
if mem::needs_drop::<T>() && MainThreadMarker::new().is_none() {
|
|
DROP_HANDLER
|
|
.get()
|
|
.expect("drop handler not initialized when setting canvas")
|
|
.send(DropBox(Box::new(value)))
|
|
.expect("sender dropped in main thread")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
unsafe impl<T> Send for MainThreadSafe<T> {}
|
|
unsafe impl<T> Sync for MainThreadSafe<T> {}
|
|
|
|
static DROP_HANDLER: OnceLock<Sender<DropBox>> = OnceLock::new();
|
|
|
|
struct DropBox(#[allow(dead_code)] Box<dyn Any>);
|
|
|
|
unsafe impl Send for DropBox {}
|
|
unsafe impl Sync for DropBox {}
|
|
|
|
trait Any {}
|
|
impl<T> Any for T {}
|