From 91d07810a27b502bf1375d5af59e22c5a83e38e6 Mon Sep 17 00:00:00 2001 From: LongYinan Date: Tue, 14 Dec 2021 00:37:46 +0800 Subject: [PATCH] fix(napi): ThreadsafeFunction with ErrorStrategy::Fatal should throw fatal exception --- crates/napi/src/threadsafe_function.rs | 2 +- examples/napi/__test__/tsfn-error.js | 3 +++ examples/napi/__test__/typegen.spec.ts.md | 1 + examples/napi/__test__/typegen.spec.ts.snap | Bin 1954 -> 1958 bytes examples/napi/__test__/values.spec.ts | 24 +++++++++++++++++++- examples/napi/index.d.ts | 1 + examples/napi/package.json | 5 ++++ examples/napi/src/threadsafe_function.rs | 16 +++++++++++++ package.json | 3 ++- yarn.lock | 7 +++++- 10 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 examples/napi/__test__/tsfn-error.js 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 75241bd8e649620fbe3b2d586fbf437a884fdd80..29418c0f92083bdbb54323db185f91c9ecb9fe9e 100644 GIT binary patch delta 1844 zcmV-42g~@P52g=)K~_N^Q*L2!b7*gLAa*he0su&oSJR_QxKk|BU8FN5=5Qa22mk;8 z00003tys-(95oc*QX!<=xRk2}ht5XZG+zW*wW&5~Qqqr2MK-i3il}GCyK&>Ow_{I| zr9|S+okK4i;g8^kkhpW^UqGBV^PWAk>)n}2qN-KZdhGXq<@bJmA4?jI#q@*@zL1BRXzQ48I*?-*oc5`>H*CsMcLY5X3 z{Y*xND@g{-JX2|`gJzp_(v*(wXp?d|Shp%3Fr$f93Cr2Fr^FZ0%@R+7qg^i%>MXH@ zaIR-)o3#d_cec4;9X*m!Fr?-P++j@wo~NlwxMp{MHnM)dN+U(4Cz&w3cd7!K4hl6M zZ=JOvA3%INaU9S#+2Dg6X<8&Kmd#>O;)}py!`xwTHL{(x)e9)WzniCV5K$pQ8XdO@ zUSx6X1#)+toG2cH0Q}F!P0}GJcgbsV0x?4wcgb>#^g&~mG7Vn`A8VGz?AA-IX%=%oixJBS#>0V)ze*|lp2~je0RvY8yh^2d$e>d&gpafhF zGvi4e2d@?^a6!3cMHS3ro*5}M7Od1Lg+-{8U{sdW158-jrx7FY*6pJ{DQi2g(YJc3 zK3IX=iB=3SIF|yf34=XyLPZ8qr76RgZ@A2V6eNa~Wq7)VqDDYESuEuT%lnAS*qV$| zH)winpEx1+)c|0!*vd~HaUnE33CI-ge3KeDUn(RIqcYH!iI{`{ zsAB?m2D8|ZP*B_l7{LW4I~`k|X?LH6YI`>`Z4{=FE&M{mcO@$f+V8%2A(u z6FS@-;wk`C-fK@DXDPv5a8|BMuBtc&TU9R0Y<(UoNu!7d@^WIqwE@3AVj~@>@MW7Q z>*P|2uLclsB2FDo6)YYI6+$e5#*Btc^~u0)RFUo%o~T8>?H+`c_E2{{XHBv#YDWkc zg90t~-DCJKsyIMG2kfGdRXp#MMU0=Yl16kRh$>rB2?YP5Uhe8V-jgH*B7eVfcZhQl z0rpzHv=(8xsJfWgw&|(J^wQ#DFf6r770uIBKPtU@V1^$;?*O4#z-GhC>Jy@;j^H7l z7oja8TND^58$~k%h{Gz9i*;2eKl8X^He?VQ8F8i|bl?@}b93lJzamHL)u4&l$^Ext ztwIxxy}KdCz0azr;2n1{y_3ZRG60gYo# zX1;7-KxtPI)gqb?Yz?&3d-mfBlRyP0fA2uvHZ@c{Q89bR;@;XscmS^8+i}aTsvru< z#O$vu+hSQX3j2_*@~uk;-RVZG2SqY=vYJzJYMLdFKG(f16!nxBW8NH@CogP$!L+W@ z7eozR4S=Nk!tK6*B=jodIQ0Qepp9SxT6m*K2njR`1&>-NM&T%O3h0A0F(5n{e*!7i z0pf{Ax=#*pv<$r6=HZ;*soS4EI+7<;jK>N)V?ffTZrkST@sA$IaM-pa<4o_$2&Q;X zk7M&Tf(d79s$w)}1W^J-1{g*pVesB#CkaL)!z24>QG_QA!)q-CDgca?9W=elqKnN6 zt@x9Tr`mlc_zT1>ffGigk_6guf47gyv0So5z2xNIpWpmo_tCu5BSjNFrPjZbvmU@d zbs4Z0<~DnsX6{orgzY8j7`r6Hpcg@OVB-ellBLulW;TKwf*!NwjW4MNT-ZSR{$iux zn2p*wRS4hB_B6|+7*5FpN@&+-&M;lVTwt{%bQH2KWAVMVH~v;sX&(s9e`)jXLz)O; z_{S?RxenA|G2l7&yb8O|P>!3iH$4niG$#%>c{?_3mrvQ6B5clT-Nb)U>2?m)3QgV> zyCw9sOClUlt0)bCXkq{!>LIpaFdeN}#Z#>-bczqUPqw-}@0JyF)ylVPt93(_!!8gl zEq^{?vdFEF1Vk4LdX9EFLcjSk+|Kjj5@n1y?raFy$h$_8f0gvv6pQCEAHbUld=t)V ie7EfT&cDzyw$hi(d(}RDe(Ah=KKDOls#iF=761TS0c|M& delta 1839 zcmV+~2hjMY526o$K~_N^Q*L2!b7*gLAa*he0szPw{~qX8en}z-K6(4hD_-Ff8qj9tGfnDEtzwzOF_io*tCrk@2&7GKQ8VW|{@6OG! z^Px%&i3HV}Y@HjHN-B24H!L#ud+X%g93d&Av65m$KKZ0=H)ggn$=_y-=ur|XVUG`< zw)4-!HtE5C*2Ub#+}-(k(mUpwz+XxtCAC2d3Ipu-qpj`E{qHYt~bb*thrGn#0Xu$*0cN_-LBEb$~b+Vv8l&Js%q z=X!>=S!*DAXPXPw(IXiJLu!t}9o9tPd6BAwYj%HsBkT98G*V=Go(aQyXDXoSpitxS z)>#|!0mQcx#{q4V4L;bBrbWVH*(??%z6dNf%pC^TBimV9y@V3{yLlD|5fvh&(Mg-& zMHa_iAP?5bsp2sR!2fLABpq_{fV?B85HpnVfGoF2A2enu)9`h0TeCD~ciw0vZy%D2 zs<;+^o-b?ON>i1RZvm_^>vPHC7J-AMhlNf45v*k1Axh5D?fS4h0yRMAXB*WO={qLsW|UZZALj~7M&p8@+|dQWuPw;F$n=s zCj{;cX0aimptuh(f(uG^Hnu#|?mi3E_HJg{C`=<;_=Se=N>*3`kK7_1m&Yl?{T2Lw z*j8Bz>s4Iu@pD+N;c|-)j`0?s;YXKSIiFi*hA(Dmu_|bwwx@PhmIKzFHl`kx<~UVn z_RR!!=y%zmC>ep~-!KokGo_?1V@(TjPbomZGHJY|IU}ve_Z4YpY!Ay9b*9wcJ;{uJ zv8x2N5DQ=8`S|jc{xbR{lQdzNo8JL{j8wruF2*nY;IPDYetx{R+7X=U;?=pzQJ;Jh zI@}%NDgabIY)>9%DZyQGR<28~syGH)Rj$fxeI6=FqlgFcYGT2)0lz+FBOR#lO`9m| zHDqB(s1plI5?&>_-lO+Tqe_y&g#JPw7 zdo5pDi?Cc&T}*7-^g?8MX>lyCKHC&#I^39d|Lklg0!x2%r2Jg;KwC9g+ncB@8++m6=WmyPM#*R0#o%V@+nh zYG6QV*Adksnh$IZwA6d{;~JAe1t)+1gS>5OsCceo_Fu#SSY2_pqmf-z!3iF|*&+`;c1ltt*Gp>87a%1ub^6np1LW8XJ#3*S#$i^^_MQ*&LZCN46GV3RdY6QA3xn zAweFw-6KdCZ!?Y)8pISzJ`>Px8%07$c3CKR)Iu=|$A&XN3Owln;mHt4F=u}hPdw6n ze1KzM;O#aK=lrhP{!G!4JfUJd0@!%~5-fGwHn)y{pg4iUwj~*7WtZ|X9ea8lo3{~6 zI9pTMy*VR@5-143Fd_+qw;4N0Fvb`j*$0LqJZTtSYbj6xV65z*=~V$;Y*uK+pI1E9 z?lZw(3+@P<1|pRt&^o()RE~e8jV0<0Cx8F=!*_Nc%{x0*G~rWf{X0490sK>!0c&B# zve#+mK6L}rUZPI0qcIHn21ExoSwJpXN-bh$Be)^x30vOyf@;8p4W#ccHVRJIsGU=V z@ZD@rvrG!tlsuq>)_Ud)QycW~R!c%hA?q?0KWuxWYDGo$fzX^b?-74wh#-c4yi$-wou=*bDO*#7%~`FR_%ABm&Y@bN$-82=guZr3 zgac|7r2!C448TJ@#FhyrnH8&es?CJ1?s50|R=4NfvSJ2W`Fd@&ZUA!F1)`?g;Hh~002G2XV(A# 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==