From e50907ab7b918128a3bb94aa87fc4092e0826a75 Mon Sep 17 00:00:00 2001 From: David Alsh Date: Tue, 25 Jun 2024 15:32:51 +1000 Subject: [PATCH] Ok all working again --- crates/neon/Cargo.toml | 2 +- crates/neon/src/context/mod.rs | 67 ++++++++++++++-------------- crates/neon/src/handle/mod.rs | 7 ++- crates/neon/src/handle/root_value.rs | 42 +++++++++-------- crates/neon/src/prelude.rs | 4 +- 5 files changed, 66 insertions(+), 56 deletions(-) diff --git a/crates/neon/Cargo.toml b/crates/neon/Cargo.toml index 493498714..2d95bc26d 100644 --- a/crates/neon/Cargo.toml +++ b/crates/neon/Cargo.toml @@ -44,7 +44,7 @@ features = ["sync"] optional = true [features] -default = ["napi-8", "futures"] +default = ["napi-8"] # Enable extracting values by serializing to JSON serde = ["dep:serde", "dep:serde_json"] diff --git a/crates/neon/src/context/mod.rs b/crates/neon/src/context/mod.rs index fcc3d9fea..13cbe1600 100644 --- a/crates/neon/src/context/mod.rs +++ b/crates/neon/src/context/mod.rs @@ -149,6 +149,8 @@ pub use crate::types::buffer::lock::Lock; // use crate::async_local::{root::RootGlobal, spawn_async_local}; #[cfg(feature = "async_local")] use futures::Future; +#[cfg(feature = "async_local")] +use crate::handle::StaticHandle; use crate::{ event::TaskBuilder, @@ -291,20 +293,16 @@ pub trait Context<'a>: ContextInternal<'a> { } #[cfg(feature = "async_local")] - fn execute_async_local(&mut self, f: F) + fn spawn_local(&mut self, f: F) where Fut: Future, F: FnOnce(AsyncContext) -> Fut + 'static, { - use futures::Future; - let env = self.env(); crate::async_local::spawn_async_local(self, async move { - // let scope = unsafe { HandleScope::new(env.to_raw()) }; let future = f(AsyncContext { env }); future.await; - // drop(scope); }).unwrap(); } @@ -623,29 +621,31 @@ impl<'a> ModuleContext<'a> { F: Fn(AsyncFunctionContext) -> Fut + 'static + Copy, V: Value, { - // let wrapper = JsFunction::new(self, move |mut cx| { - // let mut args = vec![]; - - // while let Some(arg) = cx.argument_opt(args.len()) { - // let arg = arg.as_value(&mut cx); - // let arg = RootGlobal::new(&mut cx, arg); - // args.push(arg); - // } - - // let (deferred, promise) = cx.promise(); - // cx.execute_async_local(move |mut cx| async move { - // let acx = AsyncFunctionContext { - // env: cx.env(), - // arguments: args, - // }; - // deferred.resolve(&mut cx, f(acx).await.unwrap()); - // () - // }); - - // Ok(promise) - // })?; - - // self.exports.clone().set(self, key, wrapper)?; + use crate::handle::StaticHandle; + + let wrapper = JsFunction::new(self, move |mut cx| { + let mut args = vec![]; + + while let Some(arg) = cx.argument_opt(args.len()) { + let arg = arg.as_value(&mut cx); + let arg = StaticHandle::new(&mut cx, arg)?; + args.push(arg); + } + + let (deferred, promise) = cx.promise(); + cx.spawn_local(move |mut cx| async move { + let acx = AsyncFunctionContext { + env: cx.env(), + arguments: args, + }; + deferred.resolve(&mut cx, f(acx).await.unwrap()); + () + }); + + Ok(promise) + })?; + + self.exports.clone().set(self, key, wrapper)?; Ok(()) } @@ -851,16 +851,17 @@ impl<'a> Context<'a> for FunctionContext<'a> {} #[cfg(feature = "async_local")] pub struct AsyncFunctionContext { env: Env, - // arguments: Vec, + arguments: Vec>, } #[cfg(feature = "async_local")] impl<'a> AsyncFunctionContext { pub fn argument(&mut self, i: usize) -> JsResult<'a, V> { - // let arg = self.arguments.get(i).unwrap().clone(); - // let handle = arg.into_inner(self); - // Ok(handle) - todo!() + let arg = self.arguments.get(i).unwrap().clone(); + let arg = arg.from_static(self)?; + let value = unsafe { V::from_local(self.env(), arg.to_local()) }; + let handle = Handle::new_internal(value); + Ok(handle) } } diff --git a/crates/neon/src/handle/mod.rs b/crates/neon/src/handle/mod.rs index f760d7129..7974257e2 100644 --- a/crates/neon/src/handle/mod.rs +++ b/crates/neon/src/handle/mod.rs @@ -96,8 +96,11 @@ impl<'a, V: Value + 'a> Handle<'a, V> { } } - pub fn root_global(self, cx: &mut impl Context<'a>) -> NeonResult> { - RootGlobal::new(cx, self) + /// Detaches the value from the Nodejs garbage collector + /// and manages the variable lifetime via reference counting. + /// Useful when interacting with a value within async closures + pub fn to_static(self, cx: &mut impl Context<'a>) -> NeonResult> { + StaticHandle::new(cx, self) } } diff --git a/crates/neon/src/handle/root_value.rs b/crates/neon/src/handle/root_value.rs index 83aabb533..ad838c4d2 100644 --- a/crates/neon/src/handle/root_value.rs +++ b/crates/neon/src/handle/root_value.rs @@ -12,24 +12,28 @@ use crate::result::JsResult; use crate::result::NeonResult; use crate::types::JsFunction; use crate::types::JsObject; +use crate::types::JsSymbol; + +// This creates a rooted object and stores javascript +// values on it as a way to grant any JavaScript value +// a static lifetime thread_local! { - // Symbol("__neon_cache") static NEON_CACHE: OnceCell> = OnceCell::default(); } /// Reference counted JavaScript value with a static lifetime for use in async closures -pub struct RootGlobal { +pub struct StaticHandle { pub(crate) count: Rc>, - pub(crate) inner: Rc, + pub(crate) inner: Rc>, _p: PhantomData, } -impl RootGlobal { +impl StaticHandle { pub(crate) fn new<'a>( cx: &mut impl Context<'a>, value: Handle<'a, T>, - ) -> NeonResult> { + ) -> NeonResult> { Ok(Self { count: Rc::new(RefCell::new(1)), inner: Rc::new(set_ref(cx, value)?), @@ -37,7 +41,7 @@ impl RootGlobal { }) } - pub fn clone<'a>(&self) -> RootGlobal { + pub fn clone(&self) -> StaticHandle { let mut count = self.count.borrow_mut(); *count += 1; drop(count); @@ -49,8 +53,8 @@ impl RootGlobal { } } - pub fn into_inner<'a>(&self, cx: &mut impl Context<'a>) -> JsResult<'a, T> { - get_ref(cx, &*self.inner) + pub fn from_static<'a>(&self, cx: &mut impl Context<'a>) -> JsResult<'a, T> { + get_ref(cx, &self.inner) } pub fn drop<'a>(&self, cx: &mut impl Context<'a>) -> NeonResult<()> { @@ -58,7 +62,7 @@ impl RootGlobal { *count -= 1; if *count == 0 { - delete_ref(cx, &*self.inner)? + delete_ref(cx, &self.inner)? } Ok(()) @@ -81,41 +85,43 @@ fn get_cache<'a>(cx: &mut impl Context<'a>) -> JsResult<'a, JsObject> { Ok(neon_cache.into_inner(cx)) } -fn set_ref<'a, V: Value>(cx: &mut impl Context<'a>, value: Handle<'a, V>) -> NeonResult { +fn set_ref<'a, V: Value>( + cx: &mut impl Context<'a>, + value: Handle<'a, V>, +) -> NeonResult> { let neon_cache = get_cache(cx)?; - // Is this safe? - let key = format!("{:?}", value.to_local()); + let symbol = cx.symbol(format!("{:?}", value.to_local())).root(cx); get_cache(cx)? .get::(cx, "set")? .call_with(cx) .this(neon_cache) - .arg(cx.string(&key)) + .arg(symbol.clone(cx).into_inner(cx)) .arg(value) .exec(cx)?; - Ok(key) + Ok(symbol) } -fn get_ref<'a, V: Value>(cx: &mut impl Context<'a>, key: &str) -> JsResult<'a, V> { +fn get_ref<'a, V: Value>(cx: &mut impl Context<'a>, key: &Root) -> JsResult<'a, V> { let neon_cache = get_cache(cx)?; get_cache(cx)? .get::(cx, "get")? .call_with(cx) .this(neon_cache) - .arg(cx.string(&key)) + .arg(key.clone(cx).into_inner(cx)) .apply(cx) } -fn delete_ref<'a>(cx: &mut impl Context<'a>, key: &str) -> NeonResult<()> { +fn delete_ref<'a>(cx: &mut impl Context<'a>, key: &Root) -> NeonResult<()> { let neon_cache = get_cache(cx)?; get_cache(cx)? .get::(cx, "delete")? .call_with(cx) .this(neon_cache) - .arg(cx.string(&key)) + .arg(key.clone(cx).into_inner(cx)) .exec(cx)?; Ok(()) diff --git a/crates/neon/src/prelude.rs b/crates/neon/src/prelude.rs index 01b87f55e..837dc5ca1 100644 --- a/crates/neon/src/prelude.rs +++ b/crates/neon/src/prelude.rs @@ -3,8 +3,8 @@ #[doc(no_inline)] pub use crate::{ context::{ - CallKind, ComputeContext, Context, ExecuteContext, FunctionContext, ModuleContext, - TaskContext, + AsyncContext, AsyncFunctionContext, CallKind, ComputeContext, Context, ExecuteContext, + FunctionContext, ModuleContext, TaskContext, }, handle::{Handle, Root}, object::Object,