From 1a6b20dd788113bfa93a6e396932453b4e82bf49 Mon Sep 17 00:00:00 2001 From: theMackabu Date: Wed, 28 Jan 2026 23:57:06 -0800 Subject: [PATCH] add coroutine settlement and suspension handling --- src/sugar.c | 41 +++++++++++++------ tests/test_async_await_microtask.cjs | 59 ++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 12 deletions(-) create mode 100644 tests/test_async_await_microtask.cjs diff --git a/src/sugar.c b/src/sugar.c index c3eb895..ccf6eaa 100644 --- a/src/sugar.c +++ b/src/sugar.c @@ -255,6 +255,27 @@ jsval_t start_async_in_coroutine(struct js *js, const char *code, size_t code_le return promise; } +static inline void settle_coroutine(coroutine_t *coro, jsval_t *args, int nargs, bool is_error) { + coro->result = nargs > 0 ? args[0] : js_mkundef(); + coro->is_settled = true; + coro->is_error = is_error; + coro->is_ready = true; +} + +static void resume_coroutine_if_suspended(struct js *js, coroutine_t *coro) { + if (!coro || !coro->mco || mco_status(coro->mco) != MCO_SUSPENDED) return; + + remove_coroutine(coro); + coro_saved_state_t saved = coro_enter(js, coro); + mco_result res = mco_resume(coro->mco); + coro_leave(js, coro, saved); + + if (res == MCO_SUCCESS && mco_status(coro->mco) != MCO_DEAD) { + coro->is_ready = false; + enqueue_coroutine(coro); + } else free_coroutine(coro); +} + jsval_t resume_coroutine_wrapper(struct js *js, jsval_t *args, int nargs) { jsval_t me = js->current_func; jsval_t coro_val = js_get_slot(js, me, SLOT_CORO); @@ -262,12 +283,10 @@ jsval_t resume_coroutine_wrapper(struct js *js, jsval_t *args, int nargs) { coroutine_t *coro = (coroutine_t *)(uintptr_t)tod(coro_val); if (!coro) return js_mkundef(); - - coro->result = nargs > 0 ? args[0] : js_mkundef(); - coro->is_settled = true; - coro->is_error = false; - coro->is_ready = true; - + + settle_coroutine(coro, args, nargs, false); + resume_coroutine_if_suspended(js, coro); + return js_mkundef(); } @@ -279,11 +298,9 @@ jsval_t reject_coroutine_wrapper(struct js *js, jsval_t *args, int nargs) { coroutine_t *coro = (coroutine_t *)(uintptr_t)tod(coro_val); if (!coro) return js_mkundef(); - - coro->result = nargs > 0 ? args[0] : js_mkundef(); - coro->is_settled = true; - coro->is_error = true; - coro->is_ready = true; - + + settle_coroutine(coro, args, nargs, true); + resume_coroutine_if_suspended(js, coro); + return js_mkundef(); } diff --git a/tests/test_async_await_microtask.cjs b/tests/test_async_await_microtask.cjs new file mode 100644 index 0000000..625d2f8 --- /dev/null +++ b/tests/test_async_await_microtask.cjs @@ -0,0 +1,59 @@ +var jobQueue = []; + +function setTimeout(cb, time, cbarg) { + var speedup = 10; + var runTime = Date.now() + time / speedup; + if (!jobQueue[runTime]) { + jobQueue[runTime] = []; + } + jobQueue[runTime].push(function () { + cb(cbarg); + }); +} + +function flushQueue() { + var curTime = Date.now(); + var empty = true; + for (var runTime in jobQueue) { + empty = false; + if (curTime >= runTime) { + var jobs = jobQueue[runTime]; + delete jobQueue[runTime]; + jobs.forEach(function (job) { + job(); + }); + } + } + if (!empty) { + Promise.resolve().then(flushQueue); + } +} + +function asyncTestPassed() { + console.log('test_async_await_microtask.cjs: OK'); +} + +function testCode() { + (async function () { + await Promise.resolve(); + console.log('after'); + var a1 = await new Promise(function (resolve) { + setTimeout(resolve, 800, 'foo'); + }); + var a2 = await new Promise(function (resolve) { + setTimeout(resolve, 800, 'bar'); + }); + if (a1 + a2 === 'foobar') { + asyncTestPassed(); + } else { + console.log('test_async_await_microtask.cjs: FAIL'); + } + })(); +} + +try { + testCode(); + Promise.resolve().then(flushQueue); +} catch (e) { + console.log('test_async_await_microtask.cjs: exception: ' + e); +} -- 2.51.2