-
Notifications
You must be signed in to change notification settings - Fork 287
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Reuse ThreadsafeFunction in EventQueue
Node.js optimizes subsequent ThreadsafeFunction invocations to happen during the same event loop tick, but only if the same instance of ThreadsafeFunction is used. The performance improvement is most noticeable when used in Electron, because scheduling a new UV tick in Electron is very costly. With this change EventQueue will use an existing instance of ThreadsafeTrampoline (wrapper around ThreadsafeFunction) if compiled with napi-6 feature, or it will fallback to creating a new ThreadsafeFunction per EventQueue instance. Fix: #727
- Loading branch information
1 parent
f3a96aa
commit b9864a4
Showing
4 changed files
with
143 additions
and
33 deletions.
There are no files selected for viewing
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 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,72 @@ | ||
use neon_runtime::raw::Env; | ||
use neon_runtime::tsfn::{ThreadsafeFunction, CallError}; | ||
|
||
use crate::context::TaskContext; | ||
use crate::result::NeonResult; | ||
|
||
pub(crate) type Callback = Box<dyn FnOnce(Env) + Send + 'static>; | ||
|
||
pub(crate) struct ThreadsafeTrampoline { | ||
tsfn: ThreadsafeFunction<Callback>, | ||
} | ||
|
||
impl ThreadsafeTrampoline { | ||
/// Creates an unbounded queue for scheduling closures on the JavaScript | ||
/// main thread | ||
pub(crate) fn new(env: Env) -> Self { | ||
let tsfn = unsafe { | ||
ThreadsafeFunction::new(env, Self::callback) | ||
}; | ||
|
||
Self { | ||
tsfn: tsfn, | ||
} | ||
} | ||
|
||
/// Schedules a closure to execute on the JavaScript thread that created | ||
/// this ThreadsafeTrampoline. | ||
/// Returns an `Error` if the task could not be scheduled. | ||
pub(crate) fn try_send<F>(&self, f: F) -> Result<(), CallError<Callback>> | ||
where | ||
F: FnOnce(TaskContext) -> NeonResult<()> + Send + 'static, | ||
{ | ||
let callback = Box::new(move |env| { | ||
let env = unsafe { std::mem::transmute(env) }; | ||
|
||
// Note: It is sufficient to use `TaskContext`'s `InheritedHandleScope` because | ||
// N-API creates a `HandleScope` before calling the callback. | ||
TaskContext::with_context(env, move |cx| { | ||
let _ = f(cx); | ||
}); | ||
}); | ||
|
||
self.tsfn.call(callback, None) | ||
} | ||
|
||
/// References a trampoline to prevent exiting the event loop until it has been dropped. (Default) | ||
/// Safety: `Env` must be valid for the current thread | ||
pub(crate) fn reference(&mut self, env: Env) { | ||
unsafe { | ||
self.tsfn.reference(env); | ||
} | ||
} | ||
|
||
/// Unreferences a trampoline to allow exiting the event loop before it has been dropped. | ||
/// Safety: `Env` must be valid for the current thread | ||
pub(crate) fn unref(&mut self, env: Env) { | ||
unsafe { | ||
self.tsfn.unref(env); | ||
} | ||
} | ||
|
||
// Monomorphized trampoline funciton for calling the user provided closure | ||
fn callback(env: Option<Env>, callback: Callback) { | ||
if let Some(env) = env { | ||
callback(env); | ||
} else { | ||
crate::context::internal::IS_RUNNING.with(|v| { | ||
*v.borrow_mut() = false; | ||
}); | ||
} | ||
} | ||
} |