Skip to content

Commit

Permalink
Fix panic triggered by boxed Channel instances
Browse files Browse the repository at this point in the history
This fixes a bug that can be triggered when there's a JsBox holding an
instance of Channel at the moment the VM stops (e.g., when quitting an
Electron app). The Channel's `drop` implementation calls `self.send`,
which panics with a SendError because we're already in the middle of
tearing everything down.
  • Loading branch information
rf- committed Oct 31, 2022
1 parent 0532c31 commit 616e5f1
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 1 deletion.
4 changes: 3 additions & 1 deletion crates/neon/src/event/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,9 @@ impl Drop for Channel {
// UV thread if strong reference count goes to 0.
let state = Arc::clone(&self.state);

self.send(move |mut cx| {
// Use `try_send` instead of `send` because, if `send` fails, it means we're shutting down
// the VM and don't need to worry about cleanup.
let _ = self.try_send(move |mut cx| {
state.unref(&mut cx);
Ok(())
});
Expand Down
3 changes: 3 additions & 0 deletions test/napi/lib/workers.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ if (!isMainThread) {
// succeed even if the presence of a bug.
addon.reject_after(new Error("Oh, no!"), 200).catch(() => {});

// Reproduce another shutdown bug; this one isn't timing-dependent.
let boxed_channels = addon.box_channels();

addon.get_or_init_thread_id(threadId);
parentPort.once("message", (message) => {
try {
Expand Down
17 changes: 17 additions & 0 deletions test/napi/src/js/workers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,20 @@ pub fn reject_after(mut cx: FunctionContext) -> JsResult<JsPromise> {

Ok(promise)
}

#[allow(dead_code)]
pub struct Channels {
channel_1: Channel,
channel_2: Channel,
}
impl Finalize for Channels {}

pub fn box_channels(mut cx: FunctionContext) -> JsResult<JsBox<Channels>> {
let channel_1 = cx.channel();
let channel_2 = cx.channel();

Ok(cx.boxed(Channels {
channel_1,
channel_2,
}))
}
1 change: 1 addition & 0 deletions test/napi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,7 @@ fn main(mut cx: ModuleContext) -> NeonResult<()> {
cx.export_function("stash_global_object", js::workers::stash_global_object)?;
cx.export_function("unstash_global_object", js::workers::unstash_global_object)?;
cx.export_function("reject_after", js::workers::reject_after)?;
cx.export_function("box_channels", js::workers::box_channels)?;

// Futures
cx.export_function("lazy_async_add", js::futures::lazy_async_add)?;
Expand Down

0 comments on commit 616e5f1

Please sign in to comment.