Use an integer as the timer handle

Port of
9e561d5c2e
but adapted.
This commit is contained in:
Saúl Ibarra Corretgé 2024-09-09 22:40:40 +02:00
parent 54afb19745
commit 5bf35450cd

View file

@ -94,6 +94,8 @@ extern char **environ;
#include "list.h" #include "list.h"
#include "quickjs-libc.h" #include "quickjs-libc.h"
#define MAX_SAFE_INTEGER (((int64_t) 1 << 53) - 1)
/* TODO: /* TODO:
- add socket calls - add socket calls
*/ */
@ -112,7 +114,7 @@ typedef struct {
typedef struct { typedef struct {
struct list_head link; struct list_head link;
uint8_t has_object:1; int64_t timer_id;
uint8_t repeats:1; uint8_t repeats:1;
int64_t timeout; int64_t timeout;
int64_t delay; int64_t delay;
@ -150,6 +152,7 @@ typedef struct JSThreadState {
struct list_head os_timers; /* list of JSOSTimer.link */ struct list_head os_timers; /* list of JSOSTimer.link */
struct list_head port_list; /* list of JSWorkerMessageHandler.link */ struct list_head port_list; /* list of JSWorkerMessageHandler.link */
int eval_script_recurse; /* only used in the main thread */ int eval_script_recurse; /* only used in the main thread */
int64_t next_timer_id; /* for setTimeout / setInterval */
/* not used in the main thread */ /* not used in the main thread */
JSWorkerMessagePipe *recv_pipe, *send_pipe; JSWorkerMessagePipe *recv_pipe, *send_pipe;
} JSThreadState; } JSThreadState;
@ -2029,41 +2032,13 @@ static uint64_t js__hrtime_ms(void)
return js__hrtime_ns() / (1000 * 1000); 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) static void free_timer(JSRuntime *rt, JSOSTimer *th)
{ {
list_del(&th->link);
JS_FreeValueRT(rt, th->func); JS_FreeValueRT(rt, th->func);
js_free_rt(rt, th); 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) accept string as first arg and eval at timer expiry
// TODO(bnoordhuis) retain argv[2..] as args for callback if argc > 2 // TODO(bnoordhuis) retain argv[2..] as args for callback if argc > 2
static JSValue js_os_setTimeout(JSContext *ctx, JSValue this_val, 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; int64_t delay;
JSValue func; JSValue func;
JSOSTimer *th; JSOSTimer *th;
JSValue obj;
func = argv[0]; func = argv[0];
if (!JS_IsFunction(ctx, func)) if (!JS_IsFunction(ctx, func))
@ -2083,42 +2057,50 @@ static JSValue js_os_setTimeout(JSContext *ctx, JSValue this_val,
return JS_EXCEPTION; return JS_EXCEPTION;
if (delay < 1) if (delay < 1)
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)); th = js_mallocz(ctx, sizeof(*th));
if (!th) { if (!th)
JS_FreeValue(ctx, obj);
return JS_EXCEPTION; return JS_EXCEPTION;
} th->timer_id = ts->next_timer_id++;
th->has_object = TRUE; if (ts->next_timer_id > MAX_SAFE_INTEGER)
ts->next_timer_id = 1;
th->repeats = (magic > 0); th->repeats = (magic > 0);
th->timeout = js__hrtime_ms() + delay; th->timeout = js__hrtime_ms() + delay;
th->delay = delay; th->delay = delay;
th->func = JS_DupValue(ctx, func); th->func = JS_DupValue(ctx, func);
list_add_tail(&th->link, &ts->os_timers); list_add_tail(&th->link, &ts->os_timers);
JS_SetOpaque(obj, th); return JS_NewInt64(ctx, th->timer_id);
return obj; }
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, static JSValue js_os_clearTimeout(JSContext *ctx, JSValue this_val,
int argc, JSValue *argv) int argc, JSValue *argv)
{ {
JSOSTimer *th = JS_GetOpaque2(ctx, argv[0], js_os_timer_class_id); JSRuntime *rt = JS_GetRuntime(ctx);
if (!th) JSThreadState *ts = JS_GetRuntimeOpaque(rt);
JSOSTimer *th;
int64_t timer_id;
if (JS_ToInt64(ctx, &timer_id, argv[0]))
return JS_EXCEPTION; return JS_EXCEPTION;
unlink_timer(JS_GetRuntime(ctx), th); th = find_timer_by_id(ts, timer_id);
JS_FreeValue(ctx, th->func); if (!th)
th->func = JS_UNDEFINED; return JS_UNDEFINED;
free_timer(rt, th);
return JS_UNDEFINED; return JS_UNDEFINED;
} }
static JSClassDef js_os_timer_class = {
"OSTimer",
.finalizer = js_os_timer_finalizer,
.gc_mark = js_os_timer_mark,
};
/* return a promise */ /* return a promise */
static JSValue js_os_sleepAsync(JSContext *ctx, JSValueConst this_val, static JSValue js_os_sleepAsync(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv) int argc, JSValueConst *argv)
@ -2142,7 +2124,7 @@ static JSValue js_os_sleepAsync(JSContext *ctx, JSValueConst this_val,
JS_FreeValue(ctx, resolving_funcs[1]); JS_FreeValue(ctx, resolving_funcs[1]);
return JS_EXCEPTION; return JS_EXCEPTION;
} }
th->has_object = FALSE; th->timer_id = -1;
th->timeout = js__hrtime_ms() + delay; th->timeout = js__hrtime_ms() + delay;
th->func = JS_DupValue(ctx, resolving_funcs[0]); th->func = JS_DupValue(ctx, resolving_funcs[0]);
list_add_tail(&th->link, &ts->os_timers); 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); min_delay = min_int(min_delay, delay);
} else { } else {
func = JS_DupValueRT(rt, th->func); func = JS_DupValueRT(rt, th->func);
unlink_timer(rt, th); if (th->repeats)
if (th->repeats) {
th->timeout = cur_time + th->delay; th->timeout = cur_time + th->delay;
list_add_tail(&th->link, &ts->os_timers); else
} else if (!th->has_object) {
free_timer(rt, th); free_timer(rt, th);
}
call_handler(ctx, func); call_handler(ctx, func);
JS_FreeValueRT(rt, func); JS_FreeValueRT(rt, func);
return 0; return 0;
@ -3785,15 +3764,11 @@ static const JSCFunctionListEntry js_os_funcs[] = {
static int js_os_init(JSContext *ctx, JSModuleDef *m) static int js_os_init(JSContext *ctx, JSModuleDef *m)
{ {
JSRuntime *rt = JS_GetRuntime(ctx);
os_poll_func = js_os_poll; 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 #ifdef USE_WORKER
{ {
JSRuntime *rt = JS_GetRuntime(ctx);
JSThreadState *ts = JS_GetRuntimeOpaque(rt); JSThreadState *ts = JS_GetRuntimeOpaque(rt);
JSValue proto, obj; JSValue proto, obj;
/* Worker class */ /* Worker class */
@ -3819,8 +3794,7 @@ static int js_os_init(JSContext *ctx, JSModuleDef *m)
} }
#endif /* USE_WORKER */ #endif /* USE_WORKER */
return JS_SetModuleExportList(ctx, m, js_os_funcs, return JS_SetModuleExportList(ctx, m, js_os_funcs, countof(js_os_funcs));
countof(js_os_funcs));
} }
JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name) 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->os_timers);
init_list_head(&ts->port_list); init_list_head(&ts->port_list);
ts->next_timer_id = 1;
JS_SetRuntimeOpaque(rt, ts); JS_SetRuntimeOpaque(rt, ts);
#ifdef USE_WORKER #ifdef USE_WORKER
@ -3936,8 +3912,6 @@ void js_std_free_handlers(JSRuntime *rt)
list_for_each_safe(el, el1, &ts->os_timers) { list_for_each_safe(el, el1, &ts->os_timers) {
JSOSTimer *th = list_entry(el, JSOSTimer, link); JSOSTimer *th = list_entry(el, JSOSTimer, link);
unlink_timer(rt, th);
if (!th->has_object)
free_timer(rt, th); free_timer(rt, th);
} }