Merge pull request #929 from napi-rs/async-error-handling

fix(napi): ThreadsafeFunction with ErrorStrategy::Fatal should throw fatal exception
This commit is contained in:
LongYinan 2021-12-14 12:59:07 +08:00 committed by GitHub
commit 5860d8ce4f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 58 additions and 4 deletions

View file

@ -382,7 +382,7 @@ unsafe extern "C" fn call_js_cb<T: 'static, V: NapiRaw, R, ES>(
); );
} }
Err(e) if ES::VALUE == ErrorStrategy::Fatal::VALUE => { 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) => { Err(e) => {
status = sys::napi_call_function( status = sys::napi_call_function(

View file

@ -0,0 +1,3 @@
const { threadsafeFunctionFatalModeError } = require('../index.node')
threadsafeFunctionFatalModeError(() => {})

View file

@ -109,6 +109,7 @@ Generated by [AVA](https://avajs.dev).
export function callThreadsafeFunction(callback: (...args: any[]) => any): void␊ export function callThreadsafeFunction(callback: (...args: any[]) => any): void␊
export function threadsafeFunctionThrowError(cb: (...args: any[]) => any): void␊ export function threadsafeFunctionThrowError(cb: (...args: any[]) => any): void␊
export function threadsafeFunctionFatalMode(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 getBuffer(): Buffer␊
export function convertU32Array(input: Uint32Array): Array<number> export function convertU32Array(input: Uint32Array): Array<number>
export function createExternalTypedArray(): Uint32Array␊ export function createExternalTypedArray(): Uint32Array␊

View file

@ -1,3 +1,4 @@
import { exec } from 'child_process'
import { join } from 'path' import { join } from 'path'
import test from 'ava' import test from 'ava'
@ -390,13 +391,34 @@ Napi4Test('throw error from thread safe function', async (t) => {
t.is(err.message, 'ThrowFromNative') 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<boolean>((resolve) => { const tsfnFatalMode = new Promise<boolean>((resolve) => {
threadsafeFunctionFatalMode(resolve) threadsafeFunctionFatalMode(resolve)
}) })
t.true(await tsfnFatalMode) 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<void>((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) => { Napi4Test('await Promise in rust', async (t) => {
const fx = 20 const fx = 20
const result = await asyncPlus100( const result = await asyncPlus100(

View file

@ -99,6 +99,7 @@ export function withAbortController(a: number, b: number, signal: AbortSignal):
export function callThreadsafeFunction(callback: (...args: any[]) => any): void export function callThreadsafeFunction(callback: (...args: any[]) => any): void
export function threadsafeFunctionThrowError(cb: (...args: any[]) => any): void export function threadsafeFunctionThrowError(cb: (...args: any[]) => any): void
export function threadsafeFunctionFatalMode(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 getBuffer(): Buffer
export function convertU32Array(input: Uint32Array): Array<number> export function convertU32Array(input: Uint32Array): Array<number>
export function createExternalTypedArray(): Uint32Array export function createExternalTypedArray(): Uint32Array

View file

@ -1,6 +1,7 @@
{ {
"name": "napi-examples", "name": "napi-examples",
"private": true, "private": true,
"version": "0.0.0",
"main": "./index.node", "main": "./index.node",
"types": "./index.d.ts", "types": "./index.d.ts",
"scripts": { "scripts": {
@ -10,5 +11,9 @@
"build-i686": "node ../../cli/scripts/index.js build --js false --target i686-pc-windows-msvc", "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-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" "build-release": "node ../../cli/scripts/index.js build --js false --release"
},
"dependencies": {
"@types/lodash": "^4.14.178",
"lodash": "4.17.21"
} }
} }

View file

@ -3,6 +3,7 @@ use std::thread;
use napi::{ use napi::{
bindgen_prelude::*, bindgen_prelude::*,
threadsafe_function::{ErrorStrategy, ThreadsafeFunction, ThreadsafeFunctionCallMode}, threadsafe_function::{ErrorStrategy, ThreadsafeFunction, ThreadsafeFunctionCallMode},
JsBoolean,
}; };
#[napi] #[napi]
@ -45,3 +46,18 @@ pub fn threadsafe_function_fatal_mode(cb: JsFunction) -> Result<()> {
}); });
Ok(()) Ok(())
} }
#[napi]
pub fn threadsafe_function_fatal_mode_error(cb: JsFunction) -> Result<()> {
let tsfn: ThreadsafeFunction<bool, ErrorStrategy::Fatal> =
cb.create_threadsafe_function(0, |_ctx| {
Err::<Vec<JsBoolean>, Error>(Error::new(
Status::GenericFailure,
"Generic tsfn error".to_owned(),
))
})?;
thread::spawn(move || {
tsfn.call(true, ThreadsafeFunctionCallMode::Blocking);
});
Ok(())
}

View file

@ -6,7 +6,8 @@
"workspaces": [ "workspaces": [
"cli", "cli",
"triples", "triples",
"memory-testing" "memory-testing",
"examples/napi"
], ],
"repository": { "repository": {
"type": "git", "type": "git",

View file

@ -1189,6 +1189,11 @@
resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.177.tgz#f70c0d19c30fab101cad46b52be60363c43c4578" resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.177.tgz#f70c0d19c30fab101cad46b52be60363c43c4578"
integrity sha512-0fDwydE2clKe9MNfvXHBHF9WEahRuj+msTuQqOmAApNORFvhMYZKNGGJdCzuhheVjMps/ti0Ak/iJPACMaevvw== 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": "@types/minimatch@^3.0.3":
version "3.0.5" version "3.0.5"
resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" 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" resolved "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193"
integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= 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" version "4.17.21"
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==