-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Worker thread initialization via JsTransferable (#9861)
* Added JsTransferable and using it to init workers Update crates/parcel_napi_helpers/src/transferable.rs Co-authored-by: Monica <monica.j.olejniczak@gmail.com> pr comments small changes don't need arc * docs on register worker
- Loading branch information
Showing
13 changed files
with
188 additions
and
80 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,40 @@ | ||
use std::sync::mpsc::Sender; | ||
|
||
use napi::{Env, JsObject, JsUndefined}; | ||
use napi_derive::napi; | ||
use parcel::rpc::nodejs::NodejsWorker; | ||
use parcel_napi_helpers::JsTransferable; | ||
|
||
/// This function is run in the Nodejs worker context upon initialization | ||
/// to notify the main thread that a Nodejs worker thread has started | ||
/// | ||
/// A Rust channel is transferred to the worker via JavaScript `worker.postMessage`. | ||
/// The worker then calls `register_worker`, supplying it with an object containing | ||
/// callbacks. | ||
/// | ||
/// The callbacks are later called from the main thread to send work to the worker. | ||
/// | ||
/// |-------------| --- Init channel ----> |-------------------| | ||
/// | Main Thread | | Worker Thread (n) | | ||
/// |-------------| <-- Worker wrapper --- |-------------------| | ||
/// | ||
/// **Later During Build** | ||
/// | ||
/// -- Resolver.resolve --> | ||
/// <- DependencyResult --- | ||
/// | ||
/// -- Transf.transform --> | ||
/// <--- Array<Asset> ----- | ||
#[napi] | ||
pub fn register_worker(env: Env, worker: JsObject) -> napi::Result<JsUndefined> { | ||
pub fn register_worker( | ||
env: Env, | ||
channel: JsTransferable<Sender<NodejsWorker>>, | ||
worker: JsObject, | ||
) -> napi::Result<JsUndefined> { | ||
let worker = NodejsWorker::new(worker)?; | ||
NodejsWorker::register_worker(worker); | ||
let tx_worker = channel.take()?; | ||
if tx_worker.send(worker).is_err() { | ||
return Err(napi::Error::from_reason("Unable to register worker")); | ||
} | ||
env.get_undefined() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
use std::any; | ||
use std::any::Any; | ||
use std::collections::HashMap; | ||
use std::marker::PhantomData; | ||
use std::sync::atomic::AtomicI32; | ||
use std::sync::atomic::Ordering; | ||
use std::sync::Mutex; | ||
|
||
use napi::bindgen_prelude::FromNapiValue; | ||
use napi::bindgen_prelude::ToNapiValue; | ||
use napi::Env; | ||
use napi::JsNumber; | ||
use napi::NapiRaw; | ||
use once_cell::sync::Lazy; | ||
|
||
static COUNTER: AtomicI32 = AtomicI32::new(0); | ||
static VALUES: Lazy<Mutex<HashMap<i32, Box<dyn Any + Send + Sync>>>> = | ||
Lazy::new(|| Default::default()); | ||
|
||
/// Creates an external reference to a Rust value and | ||
/// makes it transferable across Nodejs workers | ||
/// | ||
/// This is to get around the limitations of what can be transferred | ||
/// between workers in Nodejs | ||
/// | ||
/// https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Transferable_objects | ||
pub struct JsTransferable<T> { | ||
id: i32, | ||
_value: PhantomData<T>, | ||
} | ||
|
||
impl<T: Send + Sync + 'static> JsTransferable<T> { | ||
/// Put a Rust value into a Transferable container to allow | ||
/// sending values to Nodejs workers via postMessage or workerData | ||
pub fn new(value: T) -> Self { | ||
let id = COUNTER.fetch_add(1, Ordering::Relaxed); | ||
|
||
VALUES.lock().unwrap().insert(id.clone(), Box::new(value)); | ||
Self { | ||
id, | ||
_value: Default::default(), | ||
} | ||
} | ||
|
||
/// Take the value out of Transferable, so it can no longer be accessed | ||
pub fn take(self) -> napi::Result<T> { | ||
let Some(value) = VALUES.lock().unwrap().remove(&self.id) else { | ||
return Err(napi::Error::from_reason(format!( | ||
"JsTransferableError::NotExists: id({})", | ||
self.id | ||
))); | ||
}; | ||
let Ok(val) = value.downcast::<T>() else { | ||
return Err(napi::Error::from_reason(format!( | ||
"JsTransferableError::InvalidDowncast: id({}) type({})", | ||
self.id, | ||
any::type_name::<T>() | ||
))); | ||
}; | ||
Ok(*val) | ||
} | ||
} | ||
|
||
/// Allows Transferable to be returned from a Napi functions | ||
impl<T> ToNapiValue for JsTransferable<T> { | ||
unsafe fn to_napi_value( | ||
env: napi::sys::napi_env, | ||
val: Self, | ||
) -> napi::Result<napi::sys::napi_value> { | ||
let env = Env::from_raw(env); | ||
let pointer = env.create_int32(val.id.clone())?; | ||
Ok(pointer.raw()) | ||
} | ||
} | ||
|
||
/// Allows Transferable to be accepted as an argument for a Napi function | ||
impl<T> FromNapiValue for JsTransferable<T> { | ||
unsafe fn from_napi_value( | ||
env: napi::sys::napi_env, | ||
napi_val: napi::sys::napi_value, | ||
) -> napi::Result<Self> { | ||
let pointer = JsNumber::from_napi_value(env, napi_val)?; | ||
let id = pointer.get_int32()?; | ||
Ok(Self { | ||
id, | ||
_value: Default::default(), | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.