threading in the browser fun
This commit is contained in:
@@ -397,23 +397,27 @@ impl Instance {
|
||||
}
|
||||
|
||||
pub fn spawn(&mut self, name: &str, func: impl FnOnce() -> MizeResult<()> + Send + 'static) -> MizeResult<()> {
|
||||
let threads_inner = self.threads.lock()?;
|
||||
let mut threads_inner = self.threads.lock()?;
|
||||
let mut next_thread_id = self.next_thread_id.lock()?;
|
||||
|
||||
threads_inner.push((next_thread_id, name.to_owned()));
|
||||
threads_inner.push((*next_thread_id, name.to_owned()));
|
||||
|
||||
let to_spawn = move || {
|
||||
debug!("spawning thread: {}", name);
|
||||
let my_thread_id = next_thread_id;
|
||||
let my_thread_id_no_mutex_guard = *next_thread_id;
|
||||
let thread_mutex = self.threads.clone();
|
||||
let name_to_move = name.to_owned();
|
||||
let to_spawn = move || -> MizeResult<()> {
|
||||
debug!("spawning thread: {}", name_to_move);
|
||||
let my_thread_id = my_thread_id_no_mutex_guard;
|
||||
|
||||
func()?;
|
||||
|
||||
let mut threads_inner = self.threads.lock()?;
|
||||
threads_inner = threads_inner.into_iter().filter(|el| match el {
|
||||
let mut threads_inner = thread_mutex.lock()?;
|
||||
*threads_inner = threads_inner.clone().into_iter().filter(|el| match el {
|
||||
(my_thread_id, _) => false,
|
||||
(_, _) => true,
|
||||
});
|
||||
debug!("thread '{}' stopped", name);
|
||||
}).collect();
|
||||
debug!("thread '{}' stopped", name_to_move);
|
||||
Ok(())
|
||||
};
|
||||
|
||||
*next_thread_id += 1;
|
||||
@@ -422,7 +426,7 @@ impl Instance {
|
||||
thread::spawn(move || to_spawn());
|
||||
|
||||
#[cfg(feature = "wasm-target")]
|
||||
crate::platform::wasm::wasm_spawn(move || to_spawn())?;
|
||||
crate::platform::wasm::wasm_spawn(to_spawn)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -452,12 +456,22 @@ impl Instance {
|
||||
return Ok(data);
|
||||
}
|
||||
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub fn spawn_async<F: Future<Output = impl Send + Sync + 'static> + Send + Sync + 'static>(&mut self, name: &str, func: F) {
|
||||
self.threads.push(name.to_owned());
|
||||
pub fn spawn_async<F: Future<Output = impl Send + Sync + 'static> + Send + Sync + 'static>(&mut self, name: &str, func: F) -> MizeResult<()> {
|
||||
let mut threads_inner = self.threads.lock()?;
|
||||
let mut next_thread_id = self.next_thread_id.lock()?;
|
||||
|
||||
threads_inner.push((*next_thread_id, name.to_owned()));
|
||||
let runtime_inner = self.runtime.lock().unwrap();
|
||||
runtime_inner.spawn(func);
|
||||
|
||||
*next_thread_id += 1;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub fn async_get_handle(&self) -> Handle {
|
||||
let runtime_inner = self.runtime.lock().unwrap();
|
||||
@@ -467,13 +481,18 @@ impl Instance {
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub fn spawn_async_blocking<F: Future<Output = impl Send + Sync + 'static> + Send + Sync + 'static>(&mut self, name: &str, func: F) -> F::Output {
|
||||
use std::process::Output;
|
||||
|
||||
self.threads.push(name.to_owned());
|
||||
let mut threads_inner = self.threads.lock().expect("mutex lock failed in spawn_async_blocking");
|
||||
let mut next_thread_id = self.next_thread_id.lock().expect("mutex lock failed in spawn_async_blocking");
|
||||
|
||||
threads_inner.push((*next_thread_id, name.to_owned()));
|
||||
let runtime_inner = self.runtime.lock().unwrap();
|
||||
let handle = runtime_inner.handle().to_owned();
|
||||
drop(runtime_inner);
|
||||
let result = handle.block_on(func);
|
||||
|
||||
*next_thread_id += 1;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
use wasm_bindgen::prelude::*;
|
||||
use std::ptr::NonNull;
|
||||
use std::panic;
|
||||
use web_sys::js_sys;
|
||||
use crate::platform::wasm::js_sys::Function;
|
||||
use web_sys::{WorkerOptions, WorkerType};
|
||||
use web_sys::Worker;
|
||||
|
||||
use crate::instance::{self, Instance};
|
||||
use crate::error::MizeResult;
|
||||
use crate::Module;
|
||||
use crate::{mize_err, Module};
|
||||
use crate::MizeError;
|
||||
|
||||
|
||||
// console_log macro
|
||||
@@ -30,28 +35,62 @@ static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
||||
|
||||
// A function imitating `std::thread::spawn`.
|
||||
// thanks to: https://www.tweag.io/blog/2022-11-24-wasm-threads-and-messages/
|
||||
pub fn wasm_spawn(f: impl FnOnce() + Send + 'static) -> MizeResult<()> {
|
||||
let worker = web_sys::Worker::new("./worker.js")?;
|
||||
// Double-boxing because `dyn FnOnce` is unsized and so `Box<dyn FnOnce()>` is a fat pointer.
|
||||
// But `Box<Box<dyn FnOnce()>>` is just a plain pointer, and since wasm has 32-bit pointers,
|
||||
// we can cast it to a `u32` and back.
|
||||
let ptr = Box::into_raw(Box::new(Box::new(f) as Box<dyn FnOnce()>));
|
||||
let msg = js_sys::Array::new();
|
||||
// Send the worker a reference to our memory chunk, so it can initialize a wasm module
|
||||
// using the same memory.
|
||||
msg.push(&wasm_bindgen::memory());
|
||||
// Also send the worker the address of the closure we want to execute.
|
||||
msg.push(&JsValue::from(ptr as u32));
|
||||
worker.post_message(&msg);
|
||||
pub fn wasm_spawn(f: impl FnOnce() -> MizeResult<()> + Send + 'static) -> MizeResult<()> {
|
||||
|
||||
|
||||
Ok(())
|
||||
// to get window.mize_worker_url, which is set by some js code, which gets "compiled" by a
|
||||
// bundler, which sets window.mize_worker_url to the url of ./npm_pkg/worker.js
|
||||
// I hate the js ecosystem....
|
||||
// those urls then become base64 data urls containing the js code...... i hate my life
|
||||
// import worker from './worker.js?worker' ..... does however crate a worker-hash.js file in
|
||||
// the dist dir......
|
||||
// this is then however a function, that creates the worker.... so put that at
|
||||
// window.mize_worker_fn and call that here
|
||||
|
||||
|
||||
//let js_fn_code = r#"
|
||||
//return window.mize_worker_url
|
||||
//"#;
|
||||
//let js_fn = Function::new_no_args(js_fn_code);
|
||||
//let res = js_fn.call0(&web_sys::wasm_bindgen::JsValue::NULL).unwrap();
|
||||
|
||||
|
||||
let window_obj = JsValue::from(web_sys::window().unwrap());
|
||||
let worker_fn: Function = js_sys::Reflect::get(&window_obj, &("mize_worker_fn".into())).unwrap().into();
|
||||
let worker: Worker = worker_fn.call0(&web_sys::wasm_bindgen::JsValue::NULL).unwrap().into();
|
||||
|
||||
/*
|
||||
let worker_url_href = js_sys::Reflect::get(&worker_url_obj, &("href".into())).unwrap();
|
||||
let worker_url = worker_url_href.as_string().unwrap();
|
||||
|
||||
let mut worker_options = WorkerOptions::new();
|
||||
worker_options.set_type(WorkerType::Module);
|
||||
let worker = web_sys::Worker::new_with_options(
|
||||
worker_url.as_str(),
|
||||
&worker_options
|
||||
).map_err(|e| mize_err!("From JsValue: {:?}", e))?;
|
||||
*/
|
||||
|
||||
// Double-boxing because `dyn FnOnce` is unsized and so `Box<dyn FnOnce()>` is a fat pointer.
|
||||
// But `Box<Box<dyn FnOnce()>>` is just a plain pointer, and since wasm has 32-bit pointers,
|
||||
// we can cast it to a `u32` and back.
|
||||
let ptr = Box::into_raw(Box::new(Box::new(f) as Box<dyn FnOnce() -> MizeResult<()>>));
|
||||
let msg = js_sys::Array::new();
|
||||
// Send the worker a reference to our memory chunk, so it can initialize a wasm module
|
||||
// using the same memory.
|
||||
msg.push(&wasm_bindgen::memory());
|
||||
// Also send the worker the address of the closure we want to execute.
|
||||
msg.push(&JsValue::from(ptr as u32));
|
||||
worker.post_message(&msg);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
// This function is here for `worker.js` to call.
|
||||
pub fn worker_entry_point(addr: u32) {
|
||||
// Interpret the address we were given as a pointer to a closure to call.
|
||||
let closure = unsafe { Box::from_raw(ptr as *mut Box<dyn FnOnce()>) };
|
||||
let closure = unsafe { Box::from_raw(addr as *mut Box<dyn FnOnce()>) };
|
||||
(*closure)();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user