@@ -22,6 +22,7 @@ const enum State {
2222 * ```
2323 */
2424export const emnapiTSFN = {
25+ _liveSet : { } as Set < number > ,
2526 offset : {
2627 __size__ : 0 ,
2728 /* napi_ref */ resource : 0 ,
@@ -49,6 +50,7 @@ export const emnapiTSFN = {
4950 /* int32_t */ cond : 0
5051 } ,
5152 init ( ) {
53+ emnapiTSFN . _liveSet = new Set < number > ( )
5254// #if MEMORY64
5355 emnapiTSFN . offset . __size__ = NapiTSFNOffset64 . __size__
5456 emnapiTSFN . offset . resource = NapiTSFNOffset64 . async_resource_resource
@@ -514,6 +516,7 @@ export const emnapiTSFN = {
514516 }
515517 } ,
516518 destroy ( func : number ) {
519+ emnapiTSFN . _liveSet . delete ( func )
517520 emnapiTSFN . destroyQueue ( func )
518521 emnapiTSFN . releaseResources ( func )
519522 _free ( to64 ( 'func' ) as number )
@@ -735,6 +738,9 @@ export const emnapiTSFN = {
735738 // actual TSFN drain after nearby Send/Signal calls have had a chance to
736739 // collapse into the shared AsyncProgressWorker state.
737740 emnapiCtx . feature . setImmediate ( ( ) => {
741+ if ( ! emnapiTSFN . _liveSet . has ( func ) ) {
742+ return
743+ }
738744 if ( Atomics . load ( i32a , pending >>> 2 ) === 0 ) {
739745 Atomics . store ( i32a , scheduled >>> 2 , 0 )
740746 return
@@ -747,13 +753,20 @@ export const emnapiTSFN = {
747753 if ( Atomics . exchange ( i32a , pending >>> 2 , 0 ) === 0 ) {
748754 return
749755 }
756+ // After destroy(), the func address is freed. Skip dispatch
757+ // to avoid use-after-free (JS-side lifecycle check).
758+ if ( ! emnapiTSFN . _liveSet . has ( func ) ) {
759+ return
760+ }
750761 emnapiTSFN . dispatch ( func )
751762 } finally {
752763 // Allow a later wakeup to schedule a new drain chain. If another
753764 // worker-thread send raced with this drain, enqueue one more turn.
754- Atomics . store ( i32a , scheduled >>> 2 , 0 )
755- if ( Atomics . load ( i32a , pending >>> 2 ) !== 0 ) {
756- emnapiTSFN . enqueue ( func )
765+ if ( emnapiTSFN . _liveSet . has ( func ) ) {
766+ Atomics . store ( i32a , scheduled >>> 2 , 0 )
767+ if ( Atomics . load ( i32a , pending >>> 2 ) !== 0 ) {
768+ emnapiTSFN . enqueue ( func )
769+ }
757770 }
758771 }
759772 } )
@@ -882,6 +895,7 @@ export function napi_create_threadsafe_function (
882895 makeSetValue ( 'tsfn' , 'emnapiTSFN.offset.finalize_cb' , 'thread_finalize_cb' , '*' )
883896 makeSetValue ( 'tsfn' , 'emnapiTSFN.offset.call_js_cb' , 'call_js_cb' , '*' )
884897 emnapiCtx . addCleanupHook ( envObject , emnapiTSFN . cleanup , tsfn )
898+ emnapiTSFN . _liveSet . add ( tsfn as number )
885899 envObject . ref ( )
886900
887901 _emnapi_runtime_keepalive_push ( )
0 commit comments