Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use an integer as the timer handle #513

Merged
merged 1 commit into from
Sep 9, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 41 additions & 67 deletions quickjs-libc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -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))
Expand All @@ -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)
Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 */
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Loading