Skip to content

Commit 6ba7289

Browse files
committed
runtimes/js: add per-env type constructors
1 parent 53dbddc commit 6ba7289

File tree

2 files changed

+80
-6
lines changed

2 files changed

+80
-6
lines changed

runtimes/js/src/napi_util.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
use napi::{Either, Env, JsFunction, JsObject, JsUnknown};
2+
use std::collections::HashMap;
3+
use std::sync::RwLock;
24

35
pub trait PromiseHandler: Clone + Send + Sync + 'static {
46
type Output: Send + 'static;
@@ -69,3 +71,72 @@ pub fn await_promise<T, H>(
6971
_ = outer_tx.send(res);
7072
});
7173
}
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+
}

runtimes/js/src/runtime.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::api::{new_api_handler, APIRoute, Request};
22
use crate::gateway::{Gateway, GatewayConfig};
33
use crate::log::Logger;
4+
use crate::napi_util::EnvMap;
45
use crate::pubsub::{PubSubSubscription, PubSubSubscriptionConfig, PubSubTopic};
56
use crate::pvalue::{parse_pvalues, transform_pvalues_request, PVals};
67
use crate::secret::Secret;
@@ -22,7 +23,7 @@ use std::thread;
2223
static RUNTIME: OnceLock<napi::Result<Arc<encore_runtime_core::Runtime>>> = OnceLock::new();
2324

2425
// Type constructors registered from javascript so we can create those type from rust
25-
static TYPE_CONSTRUCTORS: OnceLock<TypeConstructorRefs> = OnceLock::new();
26+
static TYPE_CONSTRUCTORS: EnvMap<Arc<TypeConstructorRefs>> = EnvMap::new();
2627

2728
struct TypeConstructorRefs {
2829
decimal: Ref<()>,
@@ -36,7 +37,7 @@ pub struct Runtime {
3637
#[napi]
3738
impl Runtime {
3839
pub fn create_decimal(env: Env, val: &str) -> napi::Result<JsUnknown> {
39-
let constructors = TYPE_CONSTRUCTORS.get().ok_or_else(|| {
40+
let constructors = TYPE_CONSTRUCTORS.get(env).ok_or_else(|| {
4041
Error::new(Status::GenericFailure, "Type constructors not initialized")
4142
})?;
4243

@@ -103,10 +104,12 @@ impl Runtime {
103104
.get_or_init(|| Ok(Arc::new(init_runtime(false)?)))
104105
.clone()?;
105106

106-
TYPE_CONSTRUCTORS.get_or_init(|| TypeConstructorRefs {
107-
decimal: env
108-
.create_reference(options.type_constructors.decimal)
109-
.expect("couldn't create reference to Decimal"),
107+
TYPE_CONSTRUCTORS.get_or_init(env, || {
108+
Arc::new(TypeConstructorRefs {
109+
decimal: env
110+
.create_reference(options.type_constructors.decimal)
111+
.expect("couldn't create reference to Decimal"),
112+
})
110113
});
111114

112115
Ok(Self { runtime })

0 commit comments

Comments
 (0)