diff --git a/crates/napi/src/threadsafe_function.rs b/crates/napi/src/threadsafe_function.rs index 2e9e54fb..857d2e76 100644 --- a/crates/napi/src/threadsafe_function.rs +++ b/crates/napi/src/threadsafe_function.rs @@ -382,7 +382,7 @@ unsafe extern "C" fn call_js_cb( ); } Err(e) if ES::VALUE == ErrorStrategy::Fatal::VALUE => { - panic!("{}", e); + status = sys::napi_fatal_exception(raw_env, JsError::from(e).into_value(raw_env)); } Err(e) => { status = sys::napi_call_function( diff --git a/examples/napi/__test__/tsfn-error.js b/examples/napi/__test__/tsfn-error.js new file mode 100644 index 00000000..a91d5d85 --- /dev/null +++ b/examples/napi/__test__/tsfn-error.js @@ -0,0 +1,3 @@ +const { threadsafeFunctionFatalModeError } = require('../index.node') + +threadsafeFunctionFatalModeError(() => {}) diff --git a/examples/napi/__test__/typegen.spec.ts.md b/examples/napi/__test__/typegen.spec.ts.md index 2428c0e5..32cb0ed4 100644 --- a/examples/napi/__test__/typegen.spec.ts.md +++ b/examples/napi/__test__/typegen.spec.ts.md @@ -109,6 +109,7 @@ Generated by [AVA](https://avajs.dev). export function callThreadsafeFunction(callback: (...args: any[]) => any): void␊ export function threadsafeFunctionThrowError(cb: (...args: any[]) => any): void␊ export function threadsafeFunctionFatalMode(cb: (...args: any[]) => any): void␊ + export function threadsafeFunctionFatalModeError(cb: (...args: any[]) => any): void␊ export function getBuffer(): Buffer␊ export function convertU32Array(input: Uint32Array): Array␊ export function createExternalTypedArray(): Uint32Array␊ diff --git a/examples/napi/__test__/typegen.spec.ts.snap b/examples/napi/__test__/typegen.spec.ts.snap index 75241bd8..29418c0f 100644 Binary files a/examples/napi/__test__/typegen.spec.ts.snap and b/examples/napi/__test__/typegen.spec.ts.snap differ diff --git a/examples/napi/__test__/values.spec.ts b/examples/napi/__test__/values.spec.ts index dfa1a192..7a98e09a 100644 --- a/examples/napi/__test__/values.spec.ts +++ b/examples/napi/__test__/values.spec.ts @@ -1,3 +1,4 @@ +import { exec } from 'child_process' import { join } from 'path' import test from 'ava' @@ -390,13 +391,34 @@ Napi4Test('throw error from thread safe function', async (t) => { t.is(err.message, 'ThrowFromNative') }) -Napi4Test('throw error from thread safe function fatal mode', async (t) => { +Napi4Test('resolve value from thread safe function fatal mode', async (t) => { const tsfnFatalMode = new Promise((resolve) => { threadsafeFunctionFatalMode(resolve) }) t.true(await tsfnFatalMode) }) +Napi4Test('throw error from thread safe function fatal mode', (t) => { + const p = exec('node ./tsfn-error.js', { + cwd: __dirname, + }) + let stderr = Buffer.from([]) + p.stderr?.on('data', (data) => { + stderr = Buffer.concat([stderr, Buffer.from(data)]) + }) + return new Promise((resolve) => { + p.on('exit', (code) => { + t.is(code, 1) + t.true( + stderr + .toString('utf8') + .includes(`[Error: Generic tsfn error] { code: 'GenericFailure' }`), + ) + resolve() + }) + }) +}) + Napi4Test('await Promise in rust', async (t) => { const fx = 20 const result = await asyncPlus100( diff --git a/examples/napi/index.d.ts b/examples/napi/index.d.ts index 865303f2..3f9cdc46 100644 --- a/examples/napi/index.d.ts +++ b/examples/napi/index.d.ts @@ -99,6 +99,7 @@ export function withAbortController(a: number, b: number, signal: AbortSignal): export function callThreadsafeFunction(callback: (...args: any[]) => any): void export function threadsafeFunctionThrowError(cb: (...args: any[]) => any): void export function threadsafeFunctionFatalMode(cb: (...args: any[]) => any): void +export function threadsafeFunctionFatalModeError(cb: (...args: any[]) => any): void export function getBuffer(): Buffer export function convertU32Array(input: Uint32Array): Array export function createExternalTypedArray(): Uint32Array diff --git a/examples/napi/package.json b/examples/napi/package.json index a08fcce1..b306dc9e 100644 --- a/examples/napi/package.json +++ b/examples/napi/package.json @@ -1,6 +1,7 @@ { "name": "napi-examples", "private": true, + "version": "0.0.0", "main": "./index.node", "types": "./index.d.ts", "scripts": { @@ -10,5 +11,9 @@ "build-i686": "node ../../cli/scripts/index.js build --js false --target i686-pc-windows-msvc", "build-i686-release": "node ../../cli/scripts/index.js build --js false --release --target i686-pc-windows-msvc", "build-release": "node ../../cli/scripts/index.js build --js false --release" + }, + "dependencies": { + "@types/lodash": "^4.14.178", + "lodash": "4.17.21" } } diff --git a/examples/napi/src/threadsafe_function.rs b/examples/napi/src/threadsafe_function.rs index 9a238b88..f0cc24c3 100644 --- a/examples/napi/src/threadsafe_function.rs +++ b/examples/napi/src/threadsafe_function.rs @@ -3,6 +3,7 @@ use std::thread; use napi::{ bindgen_prelude::*, threadsafe_function::{ErrorStrategy, ThreadsafeFunction, ThreadsafeFunctionCallMode}, + JsBoolean, }; #[napi] @@ -45,3 +46,18 @@ pub fn threadsafe_function_fatal_mode(cb: JsFunction) -> Result<()> { }); Ok(()) } + +#[napi] +pub fn threadsafe_function_fatal_mode_error(cb: JsFunction) -> Result<()> { + let tsfn: ThreadsafeFunction = + cb.create_threadsafe_function(0, |_ctx| { + Err::, Error>(Error::new( + Status::GenericFailure, + "Generic tsfn error".to_owned(), + )) + })?; + thread::spawn(move || { + tsfn.call(true, ThreadsafeFunctionCallMode::Blocking); + }); + Ok(()) +} diff --git a/package.json b/package.json index 64e774ba..861bed1c 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "workspaces": [ "cli", "triples", - "memory-testing" + "memory-testing", + "examples/napi" ], "repository": { "type": "git", diff --git a/yarn.lock b/yarn.lock index 8ebcf62d..0ec1de38 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1189,6 +1189,11 @@ resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.177.tgz#f70c0d19c30fab101cad46b52be60363c43c4578" integrity sha512-0fDwydE2clKe9MNfvXHBHF9WEahRuj+msTuQqOmAApNORFvhMYZKNGGJdCzuhheVjMps/ti0Ak/iJPACMaevvw== +"@types/lodash@^4.14.178": + version "4.14.178" + resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.178.tgz#341f6d2247db528d4a13ddbb374bcdc80406f4f8" + integrity sha512-0d5Wd09ItQWH1qFbEyQ7oTQ3GZrMfth5JkbN3EvTKLXcHLRDSXeLnlvlOn0wvxVIwK5o2M8JzP/OWz7T3NRsbw== + "@types/minimatch@^3.0.3": version "3.0.5" resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" @@ -4388,7 +4393,7 @@ lodash.truncate@^4.4.2: resolved "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= -lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0: +lodash@4.17.21, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0: version "4.17.21" resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==