Previously, the timer event was removed from the pending events after
its callback was called. When the callback called clearTimeout() for
its own timer, the event was removed twice, which corrupted the
events tree. In QuickJS, the timer function and arguments were also
freed while the function was still running, and then freed again.
Now, the timer event is removed from the pending events before the
callback is called, and its data is freed after the callback returns.
The issue was introduced in
9de30135 (0.8.3).
njs_opaque_value_t *value, ngx_str_t *str);
static void ngx_qjs_clear_events(ngx_js_ctx_t *ctx);
static void ngx_qjs_detach_ctx(ngx_js_ctx_t *ctx, JSContext *cx);
njs_opaque_value_t *value, ngx_str_t *str);
static void ngx_qjs_clear_events(ngx_js_ctx_t *ctx);
static void ngx_qjs_detach_ctx(ngx_js_ctx_t *ctx, JSContext *cx);
+static void ngx_qjs_clear_timer(ngx_qjs_event_t *event);
static JSValue ngx_qjs_process_getter(JSContext *ctx, JSValueConst this_val);
static JSValue ngx_qjs_ext_set_timeout(JSContext *cx, JSValueConst this_val,
static JSValue ngx_qjs_process_getter(JSContext *ctx, JSValueConst this_val);
static JSValue ngx_qjs_ext_set_timeout(JSContext *cx, JSValueConst this_val,
external = JS_GetContextOpaque(cx);
ctx = ngx_qjs_external_ctx(cx, external);
external = JS_GetContextOpaque(cx);
ctx = ngx_qjs_external_ctx(cx, external);
+ njs_rbtree_delete(&ctx->waiting_events, &event->node);
+
rc = ngx_qjs_call(cx, event->function, event->args, event->nargs);
rc = ngx_qjs_call(cx, event->function, event->args, event->nargs);
- ngx_js_del_event(ctx, event);
+ ngx_qjs_clear_timer(event);
ngx_qjs_external_event_finalize(cx)(external, rc);
}
ngx_qjs_external_event_finalize(cx)(external, rc);
}
+ ctx = ngx_external_ctx(vm, njs_vm_external_ptr(vm));
+ njs_rbtree_delete(&ctx->waiting_events, &event->node);
+
rc = ngx_js_call(vm, njs_value_function(njs_value_arg(&event->function)),
event->args, event->nargs);
rc = ngx_js_call(vm, njs_value_function(njs_value_arg(&event->function)),
event->args, event->nargs);
- ctx = ngx_external_ctx(vm, njs_vm_external_ptr(vm));
- ngx_js_del_event(ctx, event);
-
ngx_external_event_finalize(vm)(njs_vm_external_ptr(vm), rc);
}
ngx_external_event_finalize(vm)(njs_vm_external_ptr(vm), rc);
}
js_content test.set_timeout_many;
}
js_content test.set_timeout_many;
}
+ location /clear_timeout {
+ js_content test.clear_timeout;
+ }
+
+ location /clear_immediate {
+ js_content test.clear_immediate;
+ }
+
location /clear_timeout_fetch {
js_content test.clear_timeout_fetch;
}
location /clear_timeout_fetch {
js_content test.clear_timeout_fetch;
}
setTimeout(timeout_cb_reply, 10, r);
}
setTimeout(timeout_cb_reply, 10, r);
}
+ async function clear_timeout(r) {
+ let ids = [], fired = [];
+
+ await new Promise(resolve => {
+ for (let i = 0; i < 8; i++) {
+ ids.push(setTimeout((arg) => {
+ clearTimeout(ids[arg]);
+
+ if (arg == 2) {
+ clearTimeout(ids[5]);
+ clearTimeout(ids[6]);
+ clearTimeout(ids[7]);
+ }
+
+ fired.push(arg);
+
+ if (fired.length == 5) {
+ setTimeout(resolve, 1);
+ }
+ }, 10 * i, i));
+ }
+ });
+
+ clearTimeout(ids[0]);
+ clearTimeout(2147483647);
+ r.return(200, fired.join(','));
+ }
+
+ async function clear_immediate(r) {
+ let result = await new Promise(resolve => {
+ let id = setImmediate(arg => {
+ clearTimeout(id);
+ resolve(arg);
+ }, 'immediate');
+ });
+
+ r.return(200, result);
+ }
+
async function clear_timeout_fetch(r) {
let id = setTimeout(() => {}, 1);
clearTimeout(id);
async function clear_timeout_fetch(r) {
let id = setTimeout(() => {}, 1);
clearTimeout(id);
export default {njs:test_njs, set_timeout, set_timeout_data,
set_timeout_many, context_var, shared_ctx, limit_rate,
export default {njs:test_njs, set_timeout, set_timeout_data,
set_timeout_many, context_var, shared_ctx, limit_rate,
- clear_timeout_fetch, clear_timeout_unknown,
+ clear_timeout, clear_immediate, clear_timeout_fetch,
+ clear_timeout_unknown,
async_content, set_rv_var, set_promise_var, await_reject};
EOF
async_content, set_rv_var, set_promise_var, await_reject};
EOF
-$t->try_run('no njs available')->plan(13);
+$t->try_run('no njs available')->plan(15);
###############################################################################
like(http_get('/set_timeout'), qr/Content-Type: foo/, 'setTimeout');
like(http_get('/set_timeout_many'), qr/Content-Type: reply/, 'setTimeout many');
###############################################################################
like(http_get('/set_timeout'), qr/Content-Type: foo/, 'setTimeout');
like(http_get('/set_timeout_many'), qr/Content-Type: reply/, 'setTimeout many');
+like(http_get('/clear_timeout'), qr/0,1,2,3,4$/,
+ 'clearTimeout self and sibling timers');
+like(http_get('/clear_immediate'), qr/immediate$/, 'clearTimeout immediate');
like(http_get('/clear_timeout_fetch'), qr/fetch$/,
'clearTimeout fetch id');
like(http_get('/clear_timeout_unknown'), qr/cleared$/,
like(http_get('/clear_timeout_fetch'), qr/fetch$/,
'clearTimeout fetch id');
like(http_get('/clear_timeout_unknown'), qr/cleared$/,