Compare commits

...

4 Commits

Author SHA1 Message Date
David Herman
f75368a2d2 cargo fmt 2021-05-15 13:38:07 -07:00
David Herman
6221ee21a8 Update doc examples to use Channel. 2021-05-15 13:21:34 -07:00
David Herman
280fdbd42a Add EventQueue and EventQueueError back to the prelude for backwards compatibility.
I think this will result in fewer deprecation warnings for users than ideal, but it seems better to optimize for compatibility than warnings.
2021-05-15 12:58:41 -07:00
David Herman
f2d3703ab9 Rename EventQueue and EventQueueError to Channel and SendError.
The previous types are preserved for backwards compatibility.
2021-05-15 12:45:11 -07:00
7 changed files with 85 additions and 65 deletions

View File

@ -152,7 +152,7 @@ use crate::borrow::internal::Ledger;
use crate::borrow::{Borrow, BorrowMut, Ref, RefMut};
use crate::context::internal::Env;
#[cfg(all(feature = "napi-4", feature = "event-queue-api"))]
use crate::event::EventQueue;
use crate::event::Channel;
use crate::handle::{Handle, Managed};
#[cfg(feature = "legacy-runtime")]
use crate::object::class::Class;
@ -550,9 +550,16 @@ pub trait Context<'a>: ContextInternal<'a> {
}
#[cfg(all(feature = "napi-4", feature = "event-queue-api"))]
/// Creates an unbounded queue of events to be executed on a JavaScript thread
fn queue(&mut self) -> EventQueue {
EventQueue::new(self)
/// Creates an unbounded channel for scheduling events to be executed on the JavaScript thread.
fn channel(&mut self) -> Channel {
Channel::new(self)
}
#[cfg(all(feature = "napi-4", feature = "event-queue-api"))]
#[deprecated(since = "0.9.0", note = "Please use the channel() method instead")]
#[doc(hidden)]
fn queue(&mut self) -> Channel {
self.channel()
}
}

View File

@ -6,7 +6,7 @@ use crate::result::NeonResult;
type Callback = Box<dyn FnOnce(Env) + Send + 'static>;
/// Queue for scheduling Rust closures to execute on the JavaScript main thread.
/// Channel for scheduling Rust closures to execute on the JavaScript main thread.
///
/// # Example
///
@ -17,11 +17,11 @@ type Callback = Box<dyn FnOnce(Env) + Send + 'static>;
/// # use neon::prelude::*;
/// # fn fibonacci(_: f64) -> f64 { todo!() }
/// fn async_fibonacci(mut cx: FunctionContext) -> JsResult<JsUndefined> {
/// // These types (`f64`, `Root<JsFunction>`, `EventQueue`) may all be sent
/// // These types (`f64`, `Root<JsFunction>`, `Channel`) may all be sent
/// // across threads.
/// let n = cx.argument::<JsNumber>(0)?.value(&mut cx);
/// let callback = cx.argument::<JsFunction>(1)?.root(&mut cx);
/// let queue = cx.queue();
/// let channel = cx.channel();
///
/// // Spawn a thread to complete the execution. This will _not_ block the
/// // JavaScript event loop.
@ -29,8 +29,8 @@ type Callback = Box<dyn FnOnce(Env) + Send + 'static>;
/// let result = fibonacci(n);
///
/// // Send a closure as a task to be executed by the JavaScript event
/// // queue. This _will_ block the event queue while executing.
/// queue.send(move |mut cx| {
/// // loop. This _will_ block the event loop while executing.
/// channel.send(move |mut cx| {
/// let callback = callback.into_inner(&mut cx);
/// let this = cx.undefined();
/// let null = cx.null();
@ -49,19 +49,19 @@ type Callback = Box<dyn FnOnce(Env) + Send + 'static>;
/// }
/// ```
pub struct EventQueue {
pub struct Channel {
tsfn: ThreadsafeFunction<Callback>,
has_ref: bool,
}
impl std::fmt::Debug for EventQueue {
impl std::fmt::Debug for Channel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("EventQueue")
f.write_str("Channel")
}
}
impl EventQueue {
/// Creates an unbounded queue for scheduling closures on the JavaScript
impl Channel {
/// Creates an unbounded channel for scheduling closures on the JavaScript
/// main thread
pub fn new<'a, C: Context<'a>>(cx: &mut C) -> Self {
let tsfn = unsafe { ThreadsafeFunction::new(cx.env().to_raw(), Self::callback) };
@ -72,7 +72,7 @@ impl EventQueue {
}
}
/// Allow the Node event loop to exit while this `EventQueue` exists.
/// Allow the Node event loop to exit while this `Channel` exists.
/// _Idempotent_
pub fn unref<'a, C: Context<'a>>(&mut self, cx: &mut C) -> &mut Self {
self.has_ref = false;
@ -82,7 +82,7 @@ impl EventQueue {
self
}
/// Prevent the Node event loop from exiting while this `EventQueue` exists. (Default)
/// Prevent the Node event loop from exiting while this `Channel` exists. (Default)
/// _Idempotent_
pub fn reference<'a, C: Context<'a>>(&mut self, cx: &mut C) -> &mut Self {
self.has_ref = true;
@ -92,7 +92,7 @@ impl EventQueue {
self
}
/// Schedules a closure to execute on the JavaScript thread that created this EventQueue
/// Schedules a closure to execute on the JavaScript thread that created this Channel
/// Panics if there is a libuv error
pub fn send<F>(&self, f: F)
where
@ -101,9 +101,9 @@ impl EventQueue {
self.try_send(f).unwrap()
}
/// Schedules a closure to execute on the JavaScript thread that created this EventQueue
/// Schedules a closure to execute on the JavaScript thread that created this Channel
/// Returns an `Error` if the task could not be scheduled.
pub fn try_send<F>(&self, f: F) -> Result<(), EventQueueError>
pub fn try_send<F>(&self, f: F) -> Result<(), SendError>
where
F: FnOnce(TaskContext) -> NeonResult<()> + Send + 'static,
{
@ -117,11 +117,11 @@ impl EventQueue {
});
});
self.tsfn.call(callback, None).map_err(|_| EventQueueError)
self.tsfn.call(callback, None).map_err(|_| SendError)
}
/// Returns a boolean indicating if this `EventQueue` will prevent the Node event
/// queue from exiting.
/// Returns a boolean indicating if this `Channel` will prevent the Node event
/// loop from exiting.
pub fn has_ref(&self) -> bool {
self.has_ref
}
@ -138,19 +138,19 @@ impl EventQueue {
}
}
/// Error indicating that a closure was unable to be scheduled to execute on the event queue
pub struct EventQueueError;
/// Error indicating that a closure was unable to be scheduled to execute on the event loop.
pub struct SendError;
impl std::fmt::Display for EventQueueError {
impl std::fmt::Display for SendError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "EventQueueError")
write!(f, "SendError")
}
}
impl std::fmt::Debug for EventQueueError {
impl std::fmt::Debug for SendError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self, f)
}
}
impl std::error::Error for EventQueueError {}
impl std::error::Error for SendError {}

View File

@ -32,20 +32,20 @@
//! # #[cfg(feature = "napi-1")] {
//! # use neon::prelude::*;
//! #
//! # fn parse(filename: String, callback: Root<JsFunction>, queue: EventQueue) { }
//! # fn parse(filename: String, callback: Root<JsFunction>, channel: Channel) { }
//! #
//! fn parse_async(mut cx: FunctionContext) -> JsResult<JsUndefined> {
//! // The types `String`, `Root<JsFunction>`, and `EventQueue` can all be
//! // The types `String`, `Root<JsFunction>`, and `Channel` can all be
//! // sent across threads.
//! let filename = cx.argument::<JsString>(0)?.value(&mut cx);
//! let callback = cx.argument::<JsFunction>(1)?.root(&mut cx);
//! let queue = cx.queue();
//! let channel = cx.channel();
//!
//! // Spawn a thread to complete the execution. This will _not_ block the
//! // JavaScript event loop.
//! // Spawn a background thread to complete the execution. The background
//! // execution will _not_ block the JavaScript event loop.
//! std::thread::spawn(move || {
//! // Do the heavy lifting inside the background thread.
//! parse(filename, callback, queue);
//! parse(filename, callback, channel);
//! });
//!
//! Ok(cx.undefined())
@ -58,7 +58,7 @@
//! thread.)
//!
//! Upon completion of its task, the background thread can use the JavaScript
//! callback and the event queue to notify the main thread of the result:
//! callback and the channel to notify the main thread of the result:
//!
//! ```
//! # #[cfg(feature = "napi-1")] {
@ -70,12 +70,12 @@
//! Psd::from_bytes(&std::fs::read(&filename)?)
//! }
//!
//! fn parse(filename: String, callback: Root<JsFunction>, queue: EventQueue) {
//! fn parse(filename: String, callback: Root<JsFunction>, channel: Channel) {
//! let result = psd_from_filename(filename);
//!
//! // Send a closure as a task to be executed by the JavaScript event
//! // queue. This _will_ block the event queue while executing.
//! queue.send(move |mut cx| {
//! // loop. This _will_ block the event loop while executing.
//! channel.send(move |mut cx| {
//! let callback = callback.into_inner(&mut cx);
//! let this = cx.undefined();
//! let null = cx.null();
@ -127,7 +127,17 @@
mod event_queue;
#[cfg(all(feature = "napi-4", feature = "event-queue-api"))]
pub use self::event_queue::{EventQueue, EventQueueError};
pub use self::event_queue::{Channel, SendError};
#[cfg(all(feature = "napi-4", feature = "event-queue-api"))]
#[deprecated(since = "0.9.0", note = "Please use the Channel type instead")]
#[doc(hidden)]
pub type EventQueue = self::event_queue::Channel;
#[cfg(all(feature = "napi-4", feature = "event-queue-api"))]
#[deprecated(since = "0.9.0", note = "Please use the SendError type instead")]
#[doc(hidden)]
pub type EventQueueError = self::event_queue::SendError;
#[cfg(all(not(feature = "napi-1"), feature = "event-handler-api"))]
mod event_handler;
@ -138,5 +148,5 @@ pub use self::event_handler::EventHandler;
#[cfg(all(feature = "napi-1", feature = "event-handler-api"))]
compile_error!(
"The `EventHandler` API is not supported with the N-API \
backend. Use `EventQueue` instead."
backend. Use `Channel` instead."
);

View File

@ -10,6 +10,9 @@ pub use crate::declare_types;
#[cfg(all(not(feature = "napi-1"), feature = "event-handler-api"))]
pub use crate::event::EventHandler;
#[cfg(all(feature = "napi-4", feature = "event-queue-api"))]
pub use crate::event::{Channel, SendError};
#[cfg(all(feature = "napi-4", feature = "event-queue-api"))]
#[allow(deprecated)]
pub use crate::event::{EventQueue, EventQueueError};
pub use crate::handle::Handle;
#[cfg(feature = "legacy-runtime")]

View File

@ -6,7 +6,7 @@ const assert = require('chai').assert;
return typeof global.gc === 'function' ? describe : describe.skip;
})()('sync', function() {
afterEach(() => {
// Force garbage collection to shutdown `EventQueue`
// Force garbage collection to shutdown `Channel`
global.gc();
});
@ -60,9 +60,9 @@ const assert = require('chai').assert;
global.gc();
});
it('should be able to unref event queue', function () {
// If the EventQueue is not unreferenced, the test runner will not cleanly exit
addon.leak_event_queue();
it('should be able to unref channel', function () {
// If the Channel is not unreferenced, the test runner will not cleanly exit
addon.leak_channel();
});
it('should drop leaked Root from the global queue', function (cb) {

View File

@ -13,10 +13,10 @@ pub fn useless_root(mut cx: FunctionContext) -> JsResult<JsObject> {
pub fn thread_callback(mut cx: FunctionContext) -> JsResult<JsUndefined> {
let callback = cx.argument::<JsFunction>(0)?.root(&mut cx);
let queue = cx.queue();
let channel = cx.channel();
std::thread::spawn(move || {
queue.send(move |mut cx| {
channel.send(move |mut cx| {
let callback = callback.into_inner(&mut cx);
let this = cx.undefined();
let args = Vec::<Handle<JsValue>>::new();
@ -33,14 +33,14 @@ pub fn thread_callback(mut cx: FunctionContext) -> JsResult<JsUndefined> {
pub fn multi_threaded_callback(mut cx: FunctionContext) -> JsResult<JsUndefined> {
let n = cx.argument::<JsNumber>(0)?.value(&mut cx);
let callback = cx.argument::<JsFunction>(1)?.root(&mut cx);
let queue = Arc::new(cx.queue());
let channel = Arc::new(cx.channel());
for i in 0..(n as usize) {
let callback = callback.clone(&mut cx);
let queue = Arc::clone(&queue);
let channel = Arc::clone(&channel);
std::thread::spawn(move || {
queue.send(move |mut cx| {
channel.send(move |mut cx| {
let callback = callback.into_inner(&mut cx);
let this = cx.undefined();
let args = vec![cx.number(i as f64)];
@ -63,17 +63,17 @@ pub struct AsyncGreeter {
greeting: String,
callback: Root<JsFunction>,
shutdown: Option<Root<JsFunction>>,
queue: Arc<EventQueue>,
channel: Arc<Channel>,
}
impl AsyncGreeter {
fn greet<'a, C: Context<'a>>(&self, mut cx: C) -> JsResult<'a, JsUndefined> {
let greeting = self.greeting.clone();
let callback = self.callback.clone(&mut cx);
let queue = Arc::clone(&self.queue);
let channel = Arc::clone(&self.channel);
std::thread::spawn(move || {
queue.send(|mut cx| {
channel.send(|mut cx| {
let callback = callback.into_inner(&mut cx);
let this = cx.undefined();
let args = vec![cx.string(greeting)];
@ -110,7 +110,7 @@ pub fn greeter_new(mut cx: FunctionContext) -> JsResult<BoxedGreeter> {
let callback = cx.argument::<JsFunction>(1)?.root(&mut cx);
let shutdown = cx.argument_opt(2);
let queue = cx.queue();
let channel = cx.channel();
let shutdown = shutdown
.map(|v| v.downcast_or_throw::<JsFunction, _>(&mut cx))
.transpose()?
@ -120,7 +120,7 @@ pub fn greeter_new(mut cx: FunctionContext) -> JsResult<BoxedGreeter> {
greeting,
callback,
shutdown,
queue: Arc::new(queue),
channel: Arc::new(channel),
}));
Ok(greeter)
@ -133,14 +133,14 @@ pub fn greeter_greet(mut cx: FunctionContext) -> JsResult<JsUndefined> {
greeter.greet(cx)
}
pub fn leak_event_queue(mut cx: FunctionContext) -> JsResult<JsUndefined> {
let queue = Box::new({
let mut queue = cx.queue();
queue.unref(&mut cx);
queue
pub fn leak_channel(mut cx: FunctionContext) -> JsResult<JsUndefined> {
let channel = Box::new({
let mut channel = cx.channel();
channel.unref(&mut cx);
channel
});
Box::leak(queue);
Box::leak(channel);
Ok(cx.undefined())
}
@ -148,7 +148,7 @@ pub fn leak_event_queue(mut cx: FunctionContext) -> JsResult<JsUndefined> {
pub fn drop_global_queue(mut cx: FunctionContext) -> JsResult<JsUndefined> {
struct Wrapper {
callback: Option<Root<JsFunction>>,
queue: EventQueue,
channel: Channel,
}
impl Finalize for Wrapper {}
@ -158,7 +158,7 @@ pub fn drop_global_queue(mut cx: FunctionContext) -> JsResult<JsUndefined> {
impl Drop for Wrapper {
fn drop(&mut self) {
if let Some(callback) = self.callback.take() {
self.queue.send(|mut cx| {
self.channel.send(|mut cx| {
let callback = callback.into_inner(&mut cx);
let this = cx.undefined();
let args = vec![cx.undefined()];
@ -172,11 +172,11 @@ pub fn drop_global_queue(mut cx: FunctionContext) -> JsResult<JsUndefined> {
}
let callback = cx.argument::<JsFunction>(0)?.root(&mut cx);
let queue = cx.queue();
let channel = cx.channel();
let wrapper = cx.boxed(Wrapper {
callback: Some(callback),
queue,
channel,
});
// Put the `Wrapper` instance in a `Root` and drop it

View File

@ -255,7 +255,7 @@ fn main(mut cx: ModuleContext) -> NeonResult<()> {
cx.export_function("multi_threaded_callback", multi_threaded_callback)?;
cx.export_function("greeter_new", greeter_new)?;
cx.export_function("greeter_greet", greeter_greet)?;
cx.export_function("leak_event_queue", leak_event_queue)?;
cx.export_function("leak_channel", leak_channel)?;
cx.export_function("drop_global_queue", drop_global_queue)?;
Ok(())