|
1 | 1 | use napi::{Either, Env, JsFunction, JsObject, JsUnknown}; |
| 2 | +use std::collections::HashMap; |
| 3 | +use std::sync::RwLock; |
2 | 4 |
|
3 | 5 | pub trait PromiseHandler: Clone + Send + Sync + 'static { |
4 | 6 | type Output: Send + 'static; |
@@ -69,3 +71,72 @@ pub fn await_promise<T, H>( |
69 | 71 | _ = outer_tx.send(res); |
70 | 72 | }); |
71 | 73 | } |
| 74 | + |
| 75 | +/// EnvMap is a thread-safe map that stores values associated with Env objects. |
| 76 | +/// It is intended for storing one value per napi_env. We need the map to work with |
| 77 | +/// worker pooling, where we can have multiple napi envs that each need their own copy. |
| 78 | +/// It uses a vector under the hood since the number of values is small (one per worker). |
| 79 | +pub struct EnvMap<T> { |
| 80 | + map: RwLock<Vec<(usize, T)>>, |
| 81 | +} |
| 82 | + |
| 83 | +impl<T> EnvMap<T> { |
| 84 | + pub const fn new() -> Self { |
| 85 | + Self { |
| 86 | + map: RwLock::new(Vec::new()), |
| 87 | + } |
| 88 | + } |
| 89 | + |
| 90 | + pub fn get(&self, env: Env) -> Option<T> |
| 91 | + where |
| 92 | + T: Clone, |
| 93 | + { |
| 94 | + let elems = self.map.read().unwrap(); |
| 95 | + for (addr, value) in elems.iter() { |
| 96 | + if *addr == env.raw().addr() { |
| 97 | + return Some(value.clone()); |
| 98 | + } |
| 99 | + } |
| 100 | + None |
| 101 | + } |
| 102 | + |
| 103 | + pub fn get_or_init<F>(&self, env: Env, init: F) -> T |
| 104 | + where |
| 105 | + T: Clone, |
| 106 | + F: FnOnce() -> T, |
| 107 | + { |
| 108 | + let addr = env.raw().addr(); |
| 109 | + |
| 110 | + // First try to read |
| 111 | + let num_scanned = { |
| 112 | + let map = self.map.read().unwrap(); |
| 113 | + for (key, value) in map.iter() { |
| 114 | + if *key == addr { |
| 115 | + return value.clone(); |
| 116 | + } |
| 117 | + } |
| 118 | + map.len() |
| 119 | + }; |
| 120 | + |
| 121 | + // If not found, get write lock and initialize |
| 122 | + let mut map = self.map.write().unwrap(); |
| 123 | + |
| 124 | + // Double-check in case another thread initialized it. |
| 125 | + // We only need to check from the last scanned index |
| 126 | + for (key, value) in map[num_scanned..].iter() { |
| 127 | + if *key == addr { |
| 128 | + return value.clone(); |
| 129 | + } |
| 130 | + } |
| 131 | + |
| 132 | + let value = init(); |
| 133 | + map.push((addr, value.clone())); |
| 134 | + value |
| 135 | + } |
| 136 | +} |
| 137 | + |
| 138 | +impl<T> Default for EnvMap<T> { |
| 139 | + fn default() -> Self { |
| 140 | + Self::new() |
| 141 | + } |
| 142 | +} |
0 commit comments