From 5bf35450cd0e03b9a6576f3d9db22b1e1ea20548 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Mon, 9 Sep 2024 22:40:40 +0200 Subject: [PATCH] Use an integer as the timer handle Port of https://github.com/bellard/quickjs/commit/9e561d5c2e986e535484c26385d6e1b8d08a238f but adapted. --- quickjs-libc.c | 108 +++++++++++++++++++------------------------------ 1 file changed, 41 insertions(+), 67 deletions(-) diff --git a/quickjs-libc.c b/quickjs-libc.c index e9e276d..3761e7a 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -94,6 +94,8 @@ extern char **environ; #include "list.h" #include "quickjs-libc.h" +#define MAX_SAFE_INTEGER (((int64_t) 1 << 53) - 1) + /* TODO: - add socket calls */ @@ -112,7 +114,7 @@ typedef struct { typedef struct { struct list_head link; - uint8_t has_object:1; + int64_t timer_id; uint8_t repeats:1; int64_t timeout; int64_t delay; @@ -150,6 +152,7 @@ typedef struct JSThreadState { struct list_head os_timers; /* list of JSOSTimer.link */ struct list_head port_list; /* list of JSWorkerMessageHandler.link */ int eval_script_recurse; /* only used in the main thread */ + int64_t next_timer_id; /* for setTimeout / setInterval */ /* not used in the main thread */ JSWorkerMessagePipe *recv_pipe, *send_pipe; } JSThreadState; @@ -2029,41 +2032,13 @@ static uint64_t js__hrtime_ms(void) return js__hrtime_ns() / (1000 * 1000); } -static void unlink_timer(JSRuntime *rt, JSOSTimer *th) -{ - if (th->link.prev) { - list_del(&th->link); - th->link.prev = th->link.next = NULL; - } -} - static void free_timer(JSRuntime *rt, JSOSTimer *th) { + list_del(&th->link); JS_FreeValueRT(rt, th->func); js_free_rt(rt, th); } -static JSClassID js_os_timer_class_id; - -static void js_os_timer_finalizer(JSRuntime *rt, JSValue val) -{ - JSOSTimer *th = JS_GetOpaque(val, js_os_timer_class_id); - if (th) { - th->has_object = FALSE; - if (!th->link.prev) - free_timer(rt, th); - } -} - -static void js_os_timer_mark(JSRuntime *rt, JSValue val, - JS_MarkFunc *mark_func) -{ - JSOSTimer *th = JS_GetOpaque(val, js_os_timer_class_id); - if (th) { - JS_MarkValue(rt, th->func, mark_func); - } -} - // TODO(bnoordhuis) accept string as first arg and eval at timer expiry // TODO(bnoordhuis) retain argv[2..] as args for callback if argc > 2 static JSValue js_os_setTimeout(JSContext *ctx, JSValue this_val, @@ -2074,7 +2049,6 @@ static JSValue js_os_setTimeout(JSContext *ctx, JSValue this_val, int64_t delay; JSValue func; JSOSTimer *th; - JSValue obj; func = argv[0]; if (!JS_IsFunction(ctx, func)) @@ -2083,42 +2057,50 @@ static JSValue js_os_setTimeout(JSContext *ctx, JSValue this_val, return JS_EXCEPTION; if (delay < 1) delay = 1; - obj = JS_NewObjectClass(ctx, js_os_timer_class_id); - if (JS_IsException(obj)) - return obj; th = js_mallocz(ctx, sizeof(*th)); - if (!th) { - JS_FreeValue(ctx, obj); + if (!th) return JS_EXCEPTION; - } - th->has_object = TRUE; + th->timer_id = ts->next_timer_id++; + if (ts->next_timer_id > MAX_SAFE_INTEGER) + ts->next_timer_id = 1; th->repeats = (magic > 0); th->timeout = js__hrtime_ms() + delay; th->delay = delay; th->func = JS_DupValue(ctx, func); list_add_tail(&th->link, &ts->os_timers); - JS_SetOpaque(obj, th); - return obj; + return JS_NewInt64(ctx, th->timer_id); +} + +static JSOSTimer *find_timer_by_id(JSThreadState *ts, int timer_id) +{ + struct list_head *el; + if (timer_id <= 0) + return NULL; + list_for_each(el, &ts->os_timers) { + JSOSTimer *th = list_entry(el, JSOSTimer, link); + if (th->timer_id == timer_id) + return th; + } + return NULL; } static JSValue js_os_clearTimeout(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { - JSOSTimer *th = JS_GetOpaque2(ctx, argv[0], js_os_timer_class_id); - if (!th) + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = JS_GetRuntimeOpaque(rt); + JSOSTimer *th; + int64_t timer_id; + + if (JS_ToInt64(ctx, &timer_id, argv[0])) return JS_EXCEPTION; - unlink_timer(JS_GetRuntime(ctx), th); - JS_FreeValue(ctx, th->func); - th->func = JS_UNDEFINED; + th = find_timer_by_id(ts, timer_id); + if (!th) + return JS_UNDEFINED; + free_timer(rt, th); return JS_UNDEFINED; } -static JSClassDef js_os_timer_class = { - "OSTimer", - .finalizer = js_os_timer_finalizer, - .gc_mark = js_os_timer_mark, -}; - /* return a promise */ static JSValue js_os_sleepAsync(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) @@ -2142,7 +2124,7 @@ static JSValue js_os_sleepAsync(JSContext *ctx, JSValueConst this_val, JS_FreeValue(ctx, resolving_funcs[1]); return JS_EXCEPTION; } - th->has_object = FALSE; + th->timer_id = -1; th->timeout = js__hrtime_ms() + delay; th->func = JS_DupValue(ctx, resolving_funcs[0]); list_add_tail(&th->link, &ts->os_timers); @@ -2185,13 +2167,10 @@ static int js_os_run_timers(JSRuntime *rt, JSContext *ctx, JSThreadState *ts) min_delay = min_int(min_delay, delay); } else { func = JS_DupValueRT(rt, th->func); - unlink_timer(rt, th); - if (th->repeats) { + if (th->repeats) th->timeout = cur_time + th->delay; - list_add_tail(&th->link, &ts->os_timers); - } else if (!th->has_object) { + else free_timer(rt, th); - } call_handler(ctx, func); JS_FreeValueRT(rt, func); return 0; @@ -3785,15 +3764,11 @@ static const JSCFunctionListEntry js_os_funcs[] = { static int js_os_init(JSContext *ctx, JSModuleDef *m) { - JSRuntime *rt = JS_GetRuntime(ctx); os_poll_func = js_os_poll; - /* OSTimer class */ - JS_NewClassID(rt, &js_os_timer_class_id); - JS_NewClass(rt, js_os_timer_class_id, &js_os_timer_class); - #ifdef USE_WORKER { + JSRuntime *rt = JS_GetRuntime(ctx); JSThreadState *ts = JS_GetRuntimeOpaque(rt); JSValue proto, obj; /* Worker class */ @@ -3819,8 +3794,7 @@ static int js_os_init(JSContext *ctx, JSModuleDef *m) } #endif /* USE_WORKER */ - return JS_SetModuleExportList(ctx, m, js_os_funcs, - countof(js_os_funcs)); + return JS_SetModuleExportList(ctx, m, js_os_funcs, countof(js_os_funcs)); } JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name) @@ -3904,6 +3878,8 @@ void js_std_init_handlers(JSRuntime *rt) init_list_head(&ts->os_timers); init_list_head(&ts->port_list); + ts->next_timer_id = 1; + JS_SetRuntimeOpaque(rt, ts); #ifdef USE_WORKER @@ -3936,9 +3912,7 @@ void js_std_free_handlers(JSRuntime *rt) list_for_each_safe(el, el1, &ts->os_timers) { JSOSTimer *th = list_entry(el, JSOSTimer, link); - unlink_timer(rt, th); - if (!th->has_object) - free_timer(rt, th); + free_timer(rt, th); } #ifdef USE_WORKER