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

Report async failures via exit code #514

Merged
merged 1 commit into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
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
7 changes: 6 additions & 1 deletion qjs.c
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@ int main(int argc, char **argv)
{
JSRuntime *rt;
JSContext *ctx;
JSValue ret;
struct trace_malloc_data trace_data = { NULL };
int optind;
char *expr = NULL;
Expand Down Expand Up @@ -531,7 +532,11 @@ int main(int argc, char **argv)
if (interactive) {
js_std_eval_binary(ctx, qjsc_repl, qjsc_repl_size, 0);
}
js_std_loop(ctx);
ret = js_std_loop(ctx);
if (!JS_IsUndefined(ret)) {
js_std_dump_error1(ctx, ret);
goto fail;
}
}

if (dump_memory) {
Expand Down
65 changes: 40 additions & 25 deletions quickjs-libc.c
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ typedef struct JSThreadState {
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 */
JSValue exc; /* current exception from one of our handlers */
/* not used in the main thread */
JSWorkerMessagePipe *recv_pipe, *send_pipe;
} JSThreadState;
Expand Down Expand Up @@ -2133,51 +2134,61 @@ static JSValue js_os_sleepAsync(JSContext *ctx, JSValueConst this_val,
return promise;
}

static void call_handler(JSContext *ctx, JSValue func)
static int call_handler(JSContext *ctx, JSValue func)
{
int r;
JSValue ret, func1;
/* 'func' might be destroyed when calling itself (if it frees the
handler), so must take extra care */
func1 = JS_DupValue(ctx, func);
ret = JS_Call(ctx, func1, JS_UNDEFINED, 0, NULL);
JS_FreeValue(ctx, func1);
if (JS_IsException(ret))
js_std_dump_error(ctx);
r = 0;
if (JS_IsException(ret)) {
JSRuntime *rt = JS_GetRuntime(ctx);
JSThreadState *ts = JS_GetRuntimeOpaque(rt);
ts->exc = JS_GetException(ctx);
r = -1;
}
JS_FreeValue(ctx, ret);
return r;
}

static int js_os_run_timers(JSRuntime *rt, JSContext *ctx, JSThreadState *ts)
static int js_os_run_timers(JSRuntime *rt, JSContext *ctx, JSThreadState *ts, int *min_delay)
{
JSValue func;
JSOSTimer *th;
int min_delay;
int64_t cur_time, delay;
struct list_head *el;
int r;

if (list_empty(&ts->os_timers))
return -1;
if (list_empty(&ts->os_timers)) {
*min_delay = -1;
return 0;
}

cur_time = js__hrtime_ms();
min_delay = 10000;
*min_delay = INT32_MAX;

list_for_each(el, &ts->os_timers) {
th = list_entry(el, JSOSTimer, link);
delay = th->timeout - cur_time;
if (delay > 0) {
min_delay = min_int(min_delay, delay);
*min_delay = min_int(*min_delay, delay);
} else {
*min_delay = 0;
func = JS_DupValueRT(rt, th->func);
if (th->repeats)
th->timeout = cur_time + th->delay;
else
free_timer(rt, th);
call_handler(ctx, func);
r = call_handler(ctx, func);
JS_FreeValueRT(rt, func);
return 0;
return r;
saghul marked this conversation as resolved.
Show resolved Hide resolved
}
}

return min_delay;
return 0;
}

#if defined(_WIN32)
Expand All @@ -2192,7 +2203,8 @@ static int js_os_poll(JSContext *ctx)

/* XXX: handle signals if useful */

min_delay = js_os_run_timers(rt, ctx, ts);
if (js_os_run_timers(rt, ctx, ts, &min_delay))
return -1;
if (min_delay == 0)
return 0; // expired timer
if (min_delay < 0)
Expand Down Expand Up @@ -2221,9 +2233,8 @@ static int js_os_poll(JSContext *ctx)
list_for_each(el, &ts->os_rw_handlers) {
rh = list_entry(el, JSOSRWHandler, link);
if (rh->fd == console_fd && !JS_IsNull(rh->rw_func[0])) {
call_handler(ctx, rh->rw_func[0]);
return call_handler(ctx, rh->rw_func[0]);
/* must stop because the list may have been modified */
break;
}
}
}
Expand Down Expand Up @@ -2332,13 +2343,13 @@ static int js_os_poll(JSContext *ctx)
mask = (uint64_t)1 << sh->sig_num;
if (os_pending_signals & mask) {
os_pending_signals &= ~mask;
call_handler(ctx, sh->func);
return 0;
return call_handler(ctx, sh->func);
}
}
}

min_delay = js_os_run_timers(rt, ctx, ts);
if (js_os_run_timers(rt, ctx, ts, &min_delay))
return -1;
if (min_delay == 0)
return 0; // expired timer
if (min_delay < 0)
Expand Down Expand Up @@ -2379,15 +2390,13 @@ static int js_os_poll(JSContext *ctx)
rh = list_entry(el, JSOSRWHandler, link);
if (!JS_IsNull(rh->rw_func[0]) &&
FD_ISSET(rh->fd, &rfds)) {
call_handler(ctx, rh->rw_func[0]);
return call_handler(ctx, rh->rw_func[0]);
/* must stop because the list may have been modified */
goto done;
}
if (!JS_IsNull(rh->rw_func[1]) &&
FD_ISSET(rh->fd, &wfds)) {
call_handler(ctx, rh->rw_func[1]);
return call_handler(ctx, rh->rw_func[1]);
/* must stop because the list may have been modified */
goto done;
}
}

Expand Down Expand Up @@ -3879,6 +3888,7 @@ void js_std_init_handlers(JSRuntime *rt)
init_list_head(&ts->port_list);

ts->next_timer_id = 1;
ts->exc = JS_UNDEFINED;

JS_SetRuntimeOpaque(rt, ts);

Expand Down Expand Up @@ -3938,7 +3948,7 @@ static void js_dump_obj(JSContext *ctx, FILE *f, JSValue val)
}
}

static void js_std_dump_error1(JSContext *ctx, JSValue exception_val)
void js_std_dump_error1(JSContext *ctx, JSValue exception_val)
{
JSValue val;
BOOL is_error;
Expand Down Expand Up @@ -3974,8 +3984,10 @@ void js_std_promise_rejection_tracker(JSContext *ctx, JSValue promise,
}

/* main loop which calls the user JS callbacks */
void js_std_loop(JSContext *ctx)
JSValue js_std_loop(JSContext *ctx)
{
JSRuntime *rt = JS_GetRuntime(ctx);
JSThreadState *ts = JS_GetRuntimeOpaque(rt);
JSContext *ctx1;
int err;

Expand All @@ -3985,7 +3997,8 @@ void js_std_loop(JSContext *ctx)
err = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1);
if (err <= 0) {
if (err < 0) {
js_std_dump_error(ctx1);
ts->exc = JS_GetException(ctx1);
goto done;
}
break;
}
Expand All @@ -3994,6 +4007,8 @@ void js_std_loop(JSContext *ctx)
if (!os_poll_func || os_poll_func(ctx))
break;
}
done:
return ts->exc;
}

/* Wait for a promise and execute pending jobs while waiting for
Expand Down
3 changes: 2 additions & 1 deletion quickjs-libc.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,12 @@ JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name);
JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name);
JSModuleDef *js_init_module_bjson(JSContext *ctx, const char *module_name);
void js_std_add_helpers(JSContext *ctx, int argc, char **argv);
void js_std_loop(JSContext *ctx);
JSValue js_std_loop(JSContext *ctx);
JSValue js_std_await(JSContext *ctx, JSValue obj);
void js_std_init_handlers(JSRuntime *rt);
void js_std_free_handlers(JSRuntime *rt);
void js_std_dump_error(JSContext *ctx);
void js_std_dump_error1(JSContext *ctx, JSValue exception_val);
uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename);
int js_module_set_import_meta(JSContext *ctx, JSValue func_val,
JS_BOOL use_realpath, JS_BOOL is_main);
Expand Down
8 changes: 4 additions & 4 deletions tests/test_std.js
Original file line number Diff line number Diff line change
Expand Up @@ -280,10 +280,10 @@ function test_timeout_order()
if (globalThis.__running_with_sanitizer__) return;

var s = "";
os.setTimeout(a, 100);
os.setTimeout(b, 200);
os.setTimeout(d, 500);
function a() { s += "a"; os.setTimeout(c, 200); }
os.setTimeout(a, 0);
os.setTimeout(b, 100);
os.setTimeout(d, 700);
function a() { s += "a"; os.setTimeout(c, 300); }
function b() { s += "b"; }
function c() { s += "c"; }
function d() { assert(s === "abc"); } // not "acb"
Expand Down
Loading