dioxus_motion/animations/
closure_pool.rs1#[cfg(feature = "web")]
7use std::cell::RefCell;
8#[cfg(feature = "web")]
9use std::collections::{HashMap, HashSet};
10#[cfg(feature = "web")]
11use wasm_bindgen::prelude::*;
12
13#[cfg(feature = "web")]
15pub struct WebClosurePool {
16 available_count: usize,
18 in_use_ids: HashSet<u32>,
20 callback_registry: HashMap<u32, Box<dyn FnOnce() + Send>>,
22 next_id: u32,
24 max_pool_size: usize,
26}
27
28#[cfg(feature = "web")]
29impl WebClosurePool {
30 pub fn new() -> Self {
32 Self {
33 available_count: 0,
34 in_use_ids: HashSet::new(),
35 callback_registry: HashMap::new(),
36 next_id: 1,
37 max_pool_size: 16, }
39 }
40
41 pub fn register_callback(&mut self, callback: Box<dyn FnOnce() + Send>) -> u32 {
49 let callback_id = self.next_id;
50 self.next_id += 1;
51
52 self.callback_registry.insert(callback_id, callback);
54
55 self.in_use_ids.insert(callback_id);
57
58 if self.available_count > 0 {
60 self.available_count -= 1;
61 }
62
63 callback_id
64 }
65
66 pub fn execute_callback(&mut self, callback_id: u32) {
71 if let Some(callback) = self.callback_registry.remove(&callback_id) {
72 callback();
73
74 self.in_use_ids.remove(&callback_id);
76
77 if self.available_count < self.max_pool_size {
79 self.available_count += 1;
80 }
81 }
82 }
83
84 pub fn create_js_closure(&self, callback_id: u32) -> Closure<dyn FnMut()> {
92 Closure::new(move || {
93 execute_and_return_pooled_closure(callback_id);
95 })
96 }
97
98 pub fn available_count(&self) -> usize {
100 self.available_count
101 }
102
103 pub fn in_use_count(&self) -> usize {
105 self.in_use_ids.len()
106 }
107
108 pub fn clear(&mut self) {
110 self.available_count = 0;
111 self.in_use_ids.clear();
112 self.callback_registry.clear();
113 }
114}
115
116#[cfg(feature = "web")]
117impl Default for WebClosurePool {
118 fn default() -> Self {
119 Self::new()
120 }
121}
122
123#[cfg(feature = "web")]
124thread_local! {
125 static CLOSURE_POOL: RefCell<WebClosurePool> = RefCell::new(WebClosurePool::new());
127}
128
129#[cfg(feature = "web")]
131pub fn register_pooled_callback(callback: Box<dyn FnOnce() + Send>) -> u32 {
132 CLOSURE_POOL.with(|pool| {
133 let mut pool = pool.borrow_mut();
134 pool.register_callback(callback)
135 })
136}
137
138#[cfg(feature = "web")]
140pub fn create_pooled_closure(callback_id: u32) -> Closure<dyn FnMut()> {
141 CLOSURE_POOL.with(|pool| {
142 let pool = pool.borrow();
143 pool.create_js_closure(callback_id)
144 })
145}
146
147#[cfg(feature = "web")]
149pub fn execute_and_return_pooled_closure(closure_id: u32) {
150 CLOSURE_POOL.with(|pool| {
151 let mut pool = pool.borrow_mut();
152 pool.execute_callback(closure_id);
153 });
154}
155
156#[cfg(feature = "web")]
158pub fn closure_pool_stats() -> (usize, usize) {
159 CLOSURE_POOL.with(|pool| {
160 let pool = pool.borrow();
161 (pool.available_count(), pool.in_use_count())
162 })
163}
164
165#[cfg(not(feature = "web"))]
167pub fn register_pooled_callback(_callback: Box<dyn FnOnce() + Send>) -> u32 {
168 0
169}
170
171#[cfg(not(feature = "web"))]
172pub fn execute_and_return_pooled_closure(_closure_id: u32) {}
173
174#[cfg(not(feature = "web"))]
175pub fn closure_pool_stats() -> (usize, usize) {
176 (0, 0)
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182
183 #[cfg(feature = "web")]
184 #[test]
185 fn test_closure_pool_creation() {
186 let pool = WebClosurePool::new();
187 assert_eq!(pool.available_count(), 0);
188 assert_eq!(pool.in_use_count(), 0);
189 }
190
191 #[cfg(feature = "web")]
192 #[test]
193 fn test_callback_registration() {
194 let mut pool = WebClosurePool::new();
195
196 let callback = Box::new(|| {});
198 let id = pool.register_callback(callback);
199 assert!(id > 0);
200
201 pool.execute_callback(id);
203
204 pool.execute_callback(id); }
207
208 #[cfg(feature = "web")]
209 #[test]
210 fn test_multiple_callbacks() {
211 let mut pool = WebClosurePool::new();
212
213 let callback1 = Box::new(|| {});
215 let callback2 = Box::new(|| {});
216 let id1 = pool.register_callback(callback1);
217 let id2 = pool.register_callback(callback2);
218
219 assert_ne!(id1, id2);
221
222 pool.execute_callback(id1);
224 pool.execute_callback(id2);
225 }
226
227 #[cfg(feature = "web")]
228 #[test]
229 fn test_closure_pool_clear() {
230 let mut pool = WebClosurePool::new();
231
232 let callback1 = Box::new(|| {});
234 let callback2 = Box::new(|| {});
235 let _id1 = pool.register_callback(callback1);
236 let _id2 = pool.register_callback(callback2);
237
238 pool.clear();
240 assert_eq!(pool.available_count(), 0);
241 assert_eq!(pool.in_use_count(), 0);
242 }
243
244 #[test]
245 fn test_non_web_stubs() {
246 #[cfg(not(feature = "web"))]
248 {
249 let callback = Box::new(|| {});
250 let id = register_pooled_callback(callback);
251 execute_and_return_pooled_closure(id);
252 let (available, in_use) = closure_pool_stats();
253 assert_eq!(available, 0);
254 assert_eq!(in_use, 0);
255 }
256
257 #[cfg(feature = "web")]
259 {
260 CLOSURE_POOL.with(|pool| {
262 pool.borrow_mut().clear();
263 });
264
265 let callback = Box::new(|| {});
266 let id = register_pooled_callback(callback);
267
268 let (_available, in_use) = closure_pool_stats();
270 assert_eq!(in_use, 1);
271
272 execute_and_return_pooled_closure(id);
274 let (available, in_use) = closure_pool_stats();
275 assert_eq!(available, 1);
276 assert_eq!(in_use, 0);
277 }
278 }
279}