#[cfg(feature = "web")]
use std::cell::RefCell;
#[cfg(feature = "web")]
use std::collections::{HashMap, HashSet};
#[cfg(feature = "web")]
use wasm_bindgen::prelude::*;
#[cfg(feature = "web")]
pub struct WebClosurePool {
available_count: usize,
in_use_ids: HashSet<u32>,
callback_registry: HashMap<u32, Box<dyn FnOnce() + Send>>,
next_id: u32,
max_pool_size: usize,
}
#[cfg(feature = "web")]
impl WebClosurePool {
pub fn new() -> Self {
Self {
available_count: 0,
in_use_ids: HashSet::new(),
callback_registry: HashMap::new(),
next_id: 1,
max_pool_size: 16, }
}
pub fn register_callback(&mut self, callback: Box<dyn FnOnce() + Send>) -> u32 {
let callback_id = self.next_id;
self.next_id += 1;
self.callback_registry.insert(callback_id, callback);
self.in_use_ids.insert(callback_id);
if self.available_count > 0 {
self.available_count -= 1;
}
callback_id
}
pub fn execute_callback(&mut self, callback_id: u32) {
if let Some(callback) = self.callback_registry.remove(&callback_id) {
callback();
self.in_use_ids.remove(&callback_id);
if self.available_count < self.max_pool_size {
self.available_count += 1;
}
}
}
pub fn create_js_closure(&self, callback_id: u32) -> Closure<dyn FnMut()> {
Closure::new(move || {
execute_and_return_pooled_closure(callback_id);
})
}
pub fn available_count(&self) -> usize {
self.available_count
}
pub fn in_use_count(&self) -> usize {
self.in_use_ids.len()
}
pub fn clear(&mut self) {
self.available_count = 0;
self.in_use_ids.clear();
self.callback_registry.clear();
}
}
#[cfg(feature = "web")]
impl Default for WebClosurePool {
fn default() -> Self {
Self::new()
}
}
#[cfg(feature = "web")]
thread_local! {
static CLOSURE_POOL: RefCell<WebClosurePool> = RefCell::new(WebClosurePool::new());
}
#[cfg(feature = "web")]
pub fn register_pooled_callback(callback: Box<dyn FnOnce() + Send>) -> u32 {
CLOSURE_POOL.with(|pool| {
let mut pool = pool.borrow_mut();
pool.register_callback(callback)
})
}
#[cfg(feature = "web")]
pub fn create_pooled_closure(callback_id: u32) -> Closure<dyn FnMut()> {
CLOSURE_POOL.with(|pool| {
let pool = pool.borrow();
pool.create_js_closure(callback_id)
})
}
#[cfg(feature = "web")]
pub fn execute_and_return_pooled_closure(closure_id: u32) {
CLOSURE_POOL.with(|pool| {
let mut pool = pool.borrow_mut();
pool.execute_callback(closure_id);
});
}
#[cfg(feature = "web")]
pub fn closure_pool_stats() -> (usize, usize) {
CLOSURE_POOL.with(|pool| {
let pool = pool.borrow();
(pool.available_count(), pool.in_use_count())
})
}
#[cfg(not(feature = "web"))]
pub fn register_pooled_callback(_callback: Box<dyn FnOnce() + Send>) -> u32 {
0
}
#[cfg(not(feature = "web"))]
pub fn execute_and_return_pooled_closure(_closure_id: u32) {}
#[cfg(not(feature = "web"))]
pub fn closure_pool_stats() -> (usize, usize) {
(0, 0)
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(feature = "web")]
#[test]
fn test_closure_pool_creation() {
let pool = WebClosurePool::new();
assert_eq!(pool.available_count(), 0);
assert_eq!(pool.in_use_count(), 0);
}
#[cfg(feature = "web")]
#[test]
fn test_callback_registration() {
let mut pool = WebClosurePool::new();
let callback = Box::new(|| {});
let id = pool.register_callback(callback);
assert!(id > 0);
pool.execute_callback(id);
pool.execute_callback(id); }
#[cfg(feature = "web")]
#[test]
fn test_multiple_callbacks() {
let mut pool = WebClosurePool::new();
let callback1 = Box::new(|| {});
let callback2 = Box::new(|| {});
let id1 = pool.register_callback(callback1);
let id2 = pool.register_callback(callback2);
assert_ne!(id1, id2);
pool.execute_callback(id1);
pool.execute_callback(id2);
}
#[cfg(feature = "web")]
#[test]
fn test_closure_pool_clear() {
let mut pool = WebClosurePool::new();
let callback1 = Box::new(|| {});
let callback2 = Box::new(|| {});
let _id1 = pool.register_callback(callback1);
let _id2 = pool.register_callback(callback2);
pool.clear();
assert_eq!(pool.available_count(), 0);
assert_eq!(pool.in_use_count(), 0);
}
#[test]
fn test_non_web_stubs() {
#[cfg(not(feature = "web"))]
{
let callback = Box::new(|| {});
let id = register_pooled_callback(callback);
execute_and_return_pooled_closure(id);
let (available, in_use) = closure_pool_stats();
assert_eq!(available, 0);
assert_eq!(in_use, 0);
}
#[cfg(feature = "web")]
{
CLOSURE_POOL.with(|pool| {
pool.borrow_mut().clear();
});
let callback = Box::new(|| {});
let id = register_pooled_callback(callback);
let (_available, in_use) = closure_pool_stats();
assert_eq!(in_use, 1);
execute_and_return_pooled_closure(id);
let (available, in_use) = closure_pool_stats();
assert_eq!(available, 1);
assert_eq!(in_use, 0);
}
}
}