From: Dmitry Volyntsev Date: Thu, 12 Nov 2026 01:37:06 +0000 (-0800) Subject: Modules: clear QuickJS hooks before a context is reused X-Git-Url: https://git.kaiwu.me/web//%22?a=commitdiff_plain;ds=sidebyside;p=njs.git Modules: clear QuickJS hooks before a context is reused Previously, an exit hook installed during context teardown, for example by a promise job queued by the exit hook, was kept in the context. When the context was reused, the hook was called during the teardown of a later request. Now hooks are cleared together with the pending events, after the last user code of the request has run. The issue was introduced in 04f6dfb9 (0.9.2). --- diff --git a/nginx/ngx_js.c b/nginx/ngx_js.c index a7ec295e..d3ffd4bc 100644 --- a/nginx/ngx_js.c +++ b/nginx/ngx_js.c @@ -1406,6 +1406,7 @@ ngx_engine_qjs_destroy(ngx_engine_t *e, ngx_js_ctx_t *ctx, ngx_qjs_log_exception(e, ctx->log, "unhandled rejection"); } + qjs_clear_hooks(cx); ngx_qjs_clear_events(ctx); ngx_qjs_detach_ctx(ctx, cx); diff --git a/nginx/t/js_teardown.t b/nginx/t/js_teardown.t index f8c2f7ac..193bac31 100644 --- a/nginx/t/js_teardown.t +++ b/nginx/t/js_teardown.t @@ -87,6 +87,13 @@ $t->write_file('test.js', <<'EOF'); ngx.log(ngx.ERR, 'exit hook ' + r.uri); }); + } else if (mode === 'reinstall') { + njs.on('exit', () => { + Promise.resolve().then(() => { + njs.on('exit', late('reinstalled exit hook')); + }); + }); + } r.return(200, 'ok'); @@ -95,12 +102,12 @@ $t->write_file('test.js', <<'EOF'); export default {content}; EOF -$t->try_run('no njs available')->plan(9); +$t->try_run('no njs available')->plan(11); ############################################################################### for my $scope ('zero', 'reuse') { - for my $mode ('timer', 'fetch', 'exit') { + for my $mode ('timer', 'fetch', 'exit', 'reinstall') { like(http_get("/$scope?mode=$mode"), qr/ok$/, "$scope $mode"); } } diff --git a/src/qjs.c b/src/qjs.c index 8de2ebe4..43930657 100644 --- a/src/qjs.c +++ b/src/qjs.c @@ -401,6 +401,24 @@ qjs_call_exit_hook(JSContext *ctx) } +void +qjs_clear_hooks(JSContext *ctx) +{ + unsigned i; + qjs_njs_t *njs; + + njs = qjs_njs(ctx); + if (njs == NULL) { + return; + } + + for (i = 0; i < njs_nitems(njs->hooks); i++) { + JS_FreeValue(ctx, njs->hooks[i]); + njs->hooks[i] = JS_UNDEFINED; + } +} + + static int qjs_add_intrinsic_njs(JSContext *cx, JSValueConst global) { diff --git a/src/qjs.h b/src/qjs.h index a24c8a80..4336455b 100644 --- a/src/qjs.h +++ b/src/qjs.h @@ -62,6 +62,7 @@ typedef struct { JSContext *qjs_new_context(JSRuntime *rt, qjs_module_t **addons); JSValue qjs_call_exit_hook(JSContext *ctx); +void qjs_clear_hooks(JSContext *ctx); void *qjs_chb_ctx_alloc(void *ctx, size_t size); void qjs_chb_ctx_free(void *ctx, void *p); void *qjs_chb_rt_alloc(void *rt, size_t size);