#[cfg(target_arch = "wasm32")]
use js_sys::{Array, Function, Promise};
#[cfg(target_arch = "wasm32")]
use serde::{Deserialize, Serialize};
#[cfg(target_arch = "wasm32")]
use std::cell::RefCell;
#[cfg(target_arch = "wasm32")]
use std::collections::HashMap;
#[cfg(target_arch = "wasm32")]
use std::rc::Rc;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
#[cfg(target_arch = "wasm32")]
use web_sys::{MessageEvent, Worker};
#[derive(Serialize, Deserialize)]
struct Task {
id: usize,
data: String, function: String, }
#[derive(Serialize, Deserialize)]
struct TaskResult {
id: usize,
result: String, error: Option<String>,
}
thread_local! {
static WORKER_POOL: RefCell<Option<Rc<WorkerPool>>> = RefCell::new(None);
}
fn get_worker_pool() -> Rc<WorkerPool> {
WORKER_POOL.with(|pool| {
let mut pool_ref = pool.borrow_mut();
if pool_ref.is_none() {
*pool_ref = Some(Rc::new(WorkerPool::new(num_workers())));
}
pool_ref.as_ref().unwrap().clone()
})
}
#[derive(Clone)]
pub struct WorkerPool {
workers: Rc<RefCell<Vec<Worker>>>,
next_task_id: Rc<RefCell<usize>>,
pending_tasks: Rc<RefCell<HashMap<usize, js_sys::Function>>>,
}
impl WorkerPool {
pub fn new(size: usize) -> Self {
let workers = (0..size)
.filter_map(|_| {
Worker::new("worker.js").ok()
})
.collect();
Self {
workers: Rc::new(RefCell::new(workers)),
next_task_id: Rc::new(RefCell::new(0)),
pending_tasks: Rc::new(RefCell::new(HashMap::new())),
}
}
pub async fn execute_task(&self, task: Task) -> Result<String, String> {
let workers = self.workers.borrow();
if workers.is_empty() {
return Err("No workers available".to_string());
}
let worker_idx = task.id % workers.len();
let worker = &workers[worker_idx];
let (promise, resolve, reject) = create_promise();
{
let mut pending = self.pending_tasks.borrow_mut();
pending.insert(task.id, resolve);
}
let task_json = serde_json::to_string(&task).unwrap();
worker
.post_message(&JsValue::from_str(&task_json))
.map_err(|e| format!("Failed to post message: {:?}", e))?;
let result = wasm_bindgen_futures::JsFuture::from(promise)
.await
.map_err(|e| format!("Task failed: {:?}", e))?;
Ok(result.as_string().unwrap_or_default())
}
}
fn create_promise() -> (Promise, js_sys::Function, js_sys::Function) {
let resolve_ref = Rc::new(RefCell::new(None));
let reject_ref = Rc::new(RefCell::new(None));
let resolve_clone = resolve_ref.clone();
let reject_clone = reject_ref.clone();
let promise = Promise::new(&mut |resolve, reject| {
*resolve_clone.borrow_mut() = Some(resolve);
*reject_clone.borrow_mut() = Some(reject);
});
let resolve = resolve_ref.borrow_mut().take().unwrap();
let reject = reject_ref.borrow_mut().take().unwrap();
(promise, resolve, reject)
}
pub fn parallel<T, R, F>(items: Vec<T>, f: F) -> Vec<R>
where
T: Serialize + for<'de> Deserialize<'de>,
R: Serialize + for<'de> Deserialize<'de>,
F: Fn(T) -> R,
{
items.into_iter().map(f).collect()
}
pub fn background<T, F>(f: F) -> BackgroundTask<T>
where
T: 'static,
F: FnOnce() -> T + 'static,
{
let result = f();
BackgroundTask {
result: Some(result),
}
}
pub fn num_workers() -> usize {
#[cfg(target_arch = "wasm32")]
{
use web_sys::window;
window()
.map(|w| {
let count = w.navigator().hardware_concurrency() as usize;
if count > 0 {
count
} else {
4
}
})
.unwrap_or(4)
}
#[cfg(not(target_arch = "wasm32"))]
{
4
}
}
pub fn join<A, B, FA, FB>(a: FA, b: FB) -> (A, B)
where
FA: FnOnce() -> A,
FB: FnOnce() -> B,
{
let result_a = a();
let result_b = b();
(result_a, result_b)
}
pub fn map_reduce<T, R, M, Red>(items: Vec<T>, map_fn: M, reduce_fn: Red, initial: R) -> R
where
T: Clone,
R: Clone,
M: Fn(T) -> R,
Red: Fn(R, R) -> R,
{
items.into_iter().map(map_fn).fold(initial, reduce_fn)
}
pub struct BackgroundTask<T> {
result: Option<T>,
}
impl<T> BackgroundTask<T> {
pub fn await_result(mut self) -> T {
self.result.take().expect("BackgroundTask already consumed")
}
pub fn is_ready(&self) -> bool {
self.result.is_some()
}
}