Skip to content

Commit

Permalink
refactor: separate threadsafe js value ref and js value ref
Browse files Browse the repository at this point in the history
  • Loading branch information
NotEvenANeko committed Apr 12, 2024
1 parent bcdaac7 commit 4c6fe7f
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 88 deletions.
24 changes: 12 additions & 12 deletions crates/rspack_binding_options/src/options/raw_module/js_loader.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
use std::{
ops::Deref,
path::{Path, PathBuf},
str::FromStr,
};
use std::{ops::Deref, path::PathBuf, str::FromStr};

use napi_derive::napi;
use rspack_core::{rspack_sources::SourceMap, Content, ResourceData};
Expand All @@ -15,9 +11,10 @@ use {
rspack_error::error,
rspack_identifier::{Identifiable, Identifier},
rspack_napi::threadsafe_function::ThreadsafeFunction,
rspack_napi::threadsafe_js_value_ref::ThreadsafeJsValueRef,
};

use crate::{get_builtin_loader, JsValueRef};
use crate::get_builtin_loader;

type ThreadsafeLoaderRunner =
ThreadsafeFunction<JsLoaderContext, Promise<LoaderThreadsafeLoaderResult>>;
Expand Down Expand Up @@ -146,7 +143,7 @@ fn sync_loader_context(
} else {
loader_context
.additional_data
.remove::<JsValueRef<Unknown>>();
.remove::<ThreadsafeJsValueRef<Unknown>>();
}
loader_context.asset_filenames = loader_result.asset_filenames.into_iter().collect();

Expand All @@ -158,7 +155,7 @@ pub struct JsLoaderContext {
/// Content maybe empty in pitching stage
pub content: Either<Null, Buffer>,
#[napi(ts_type = "any")]
pub additional_data: Option<JsValueRef<Unknown>>,
pub additional_data: Option<ThreadsafeJsValueRef<Unknown>>,
pub source_map: Option<Buffer>,
pub resource: String,
pub resource_path: String,
Expand Down Expand Up @@ -211,7 +208,10 @@ impl TryFrom<&mut rspack_core::LoaderContext<'_, rspack_core::LoaderRunnerContex
Some(c) => Either::B(c.to_owned().into_bytes().into()),
None => Either::A(Null),
},
additional_data: cx.additional_data.get::<JsValueRef<Unknown>>().cloned(),
additional_data: cx
.additional_data
.get::<ThreadsafeJsValueRef<Unknown>>()
.cloned(),
source_map: cx
.source_map
.clone()
Expand Down Expand Up @@ -357,7 +357,7 @@ pub struct JsLoaderResult {
pub build_dependencies: Vec<String>,
pub asset_filenames: Vec<String>,
pub source_map: Option<Buffer>,
pub additional_data: Option<JsValueRef<Unknown>>,
pub additional_data: Option<ThreadsafeJsValueRef<Unknown>>,
pub additional_data_external: External<AdditionalData>,
pub cacheable: bool,
/// Used to instruct how rust loaders should execute
Expand Down Expand Up @@ -431,8 +431,8 @@ impl napi::bindgen_prelude::FromNapiValue for JsLoaderResult {
)
})?;
let source_map_: Option<Buffer> = obj.get("sourceMap")?;
let additional_data_: Option<JsValueRef<Unknown>> =
obj.get::<_, JsValueRef<Unknown>>("additionalData")?;
let additional_data_: Option<ThreadsafeJsValueRef<Unknown>> =
obj.get::<_, ThreadsafeJsValueRef<Unknown>>("additionalData")?;

// change: eagerly clone this field since `External<T>` might be dropped.
let additional_data_external_: External<AdditionalData> = obj
Expand Down

This file was deleted.

2 changes: 0 additions & 2 deletions crates/rspack_binding_options/src/options/raw_module/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
mod js_loader;
mod js_value_ref;

use std::fmt::Formatter;
use std::{collections::HashMap, fmt::Debug, sync::Arc};
Expand Down Expand Up @@ -29,7 +28,6 @@ use tokio::runtime::Handle;

pub use self::js_loader::JsLoaderAdapter;
pub use self::js_loader::*;
pub use self::js_value_ref::JsValueRef;
use crate::RawResolveOptions;

pub fn get_builtin_loader(builtin: &str, options: Option<&str>) -> BoxLoader {
Expand Down
44 changes: 44 additions & 0 deletions crates/rspack_napi/src/js_values/js_value_ref.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use std::marker::PhantomData;

use napi::bindgen_prelude::*;
use napi::{Env, NapiValue, Ref};

pub struct JsValueRef<T: NapiValue> {
ref_: Ref<()>,
_phantom: PhantomData<T>,
}

impl<T: NapiValue> JsValueRef<T> {
pub fn new(env: Env, value: T) -> Result<Self> {
let ref_ = env.create_reference(value)?;

Ok(Self {
ref_,
_phantom: PhantomData,
})
}

pub fn get(&self, env: Env) -> Result<T> {
env.get_reference_value(&self.ref_)
}

pub fn unref(&mut self, env: Env) -> Result<u32> {
self.ref_.unref(env)
}
}

impl<T: NapiValue> ToNapiValue for JsValueRef<T> {
unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
val
.get(Env::from(env))
.and_then(|v| unsafe { T::to_napi_value(env, v) })
}
}

impl<T: NapiValue> FromNapiValue for JsValueRef<T> {
unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
JsValueRef::<T>::new(Env::from(env), unsafe {
T::from_napi_value(env, napi_val)
}?)
}
}
1 change: 1 addition & 0 deletions crates/rspack_napi/src/js_values/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod js_reg_exp;
pub mod js_value_ref;
1 change: 1 addition & 0 deletions crates/rspack_napi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ mod callback;
pub(crate) use callback::JsCallback;

pub mod threadsafe_function;
pub mod threadsafe_js_value_ref;

pub mod regexp {
pub use crate::ext::js_reg_exp_ext::JsRegExpExt;
Expand Down
149 changes: 149 additions & 0 deletions crates/rspack_napi/src/threadsafe_js_value_ref.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
use std::ffi::{c_void, CString};
use std::marker::PhantomData;
use std::ptr;
use std::sync::{Arc, Mutex};

use napi::bindgen_prelude::*;
use napi::sys::napi_threadsafe_function;
use napi::NapiValue;

use crate::js_values::js_value_ref::JsValueRef;

pub struct ThreadsafeJsValueRef<T: NapiValue> {
inner: Arc<(Mutex<JsValueRef<T>>, DropJsValueRefFn<T>)>,
}

unsafe impl<T: NapiValue> Send for ThreadsafeJsValueRef<T> {}
unsafe impl<T: NapiValue> Sync for ThreadsafeJsValueRef<T> {}

impl<T: NapiValue> Clone for ThreadsafeJsValueRef<T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}

impl<T: NapiValue> Drop for ThreadsafeJsValueRef<T> {
fn drop(&mut self) {
if Arc::strong_count(&self.inner) == 1 {
let (_, drop_fn) = self.inner.as_ref();

drop_fn.call(self.inner.clone());
}
}
}

impl<T: NapiValue> FromNapiValue for ThreadsafeJsValueRef<T> {
unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
Self::new(Env::from(env), unsafe {
T::from_napi_value(env, napi_val)
}?)
}
}

impl<T: NapiValue> ToNapiValue for ThreadsafeJsValueRef<T> {
unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
val
.get(Env::from(env))
.and_then(|v| unsafe { T::to_napi_value(env, v) })
}
}

impl<T: NapiValue> ThreadsafeJsValueRef<T> {
pub fn new(env: Env, value: T) -> Result<Self> {
let js_ref = JsValueRef::new(env, value)?;

Ok(Self {
inner: Arc::new((Mutex::new(js_ref), DropJsValueRefFn::new(env)?)),
})
}

pub fn get(&self, env: Env) -> Result<T> {
let (ref_, _) = self.inner.as_ref();

ref_
.lock()
.map_err(|e| {
Error::new(
Status::GenericFailure,
format!("Failed to lock mutex: {}", e.to_string()),

Check failure on line 70 in crates/rspack_napi/src/threadsafe_js_value_ref.rs

View workflow job for this annotation

GitHub Actions / Rust check

`to_string` applied to a type that implements `Display` in `format!` args
)
})?
.get(env)
}
}

struct DropJsValueRefFn<T: NapiValue> {
inner: napi_threadsafe_function,
_phantom: PhantomData<T>,
}

impl<T: NapiValue> DropJsValueRefFn<T> {
pub fn new(env: Env) -> Result<Self> {
let mut raw_cb = std::ptr::null_mut();

let mut async_resource_name = ptr::null_mut();
let s = "napi_rs_js_value_ref_drop";
let len = s.len();
let s = CString::new(s)?;
check_status!(unsafe {
sys::napi_create_string_utf8(env.raw(), s.as_ptr(), len, &mut async_resource_name)
})?;

check_status! {unsafe {
sys::napi_create_threadsafe_function(
env.raw(),
ptr::null_mut(),
ptr::null_mut(),
async_resource_name,
0,
1,
ptr::null_mut(),
None,
ptr::null_mut(),
Some(call_js_cb::<T>),
&mut raw_cb,
)
}}?;

Ok(Self {
inner: raw_cb,
_phantom: PhantomData,
})
}

pub fn call(&self, value: Arc<(Mutex<JsValueRef<T>>, DropJsValueRefFn<T>)>) {
check_status! {
unsafe {
sys::napi_call_threadsafe_function(self.inner, Arc::into_raw(value) as *mut _, sys::ThreadsafeFunctionCallMode::nonblocking)
}
}.expect("Failed to call threadsafe function");
}
}

impl<T: NapiValue> Drop for DropJsValueRefFn<T> {
fn drop(&mut self) {
check_status! {
unsafe {
sys::napi_release_threadsafe_function(self.inner, sys::ThreadsafeFunctionReleaseMode::release)
}
}.expect("Failed to release threadsafe function");
}
}

unsafe extern "C" fn call_js_cb<T: NapiValue>(
raw_env: sys::napi_env,
_: sys::napi_value,
_: *mut c_void,
data: *mut c_void,
) {
let arc = unsafe { Arc::<(Mutex<JsValueRef<T>>, DropJsValueRefFn<T>)>::from_raw(data.cast()) };
let (ref_, _) = arc.as_ref();

ref_
.lock()
.expect("Failed to lock")
.unref(Env::from(raw_env))
.expect("Failed to unref");
}

0 comments on commit 4c6fe7f

Please sign in to comment.