MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

redesign lazy-start/materialization cases

+362 -212
-1
include/silver/engine.h
··· 343 343 344 344 // TODO: move to nested struct 345 345 bool suspended; 346 - bool async_handoff_pending; 347 346 bool suspended_resume_pending; 348 347 bool suspended_resume_is_error; 349 348 sv_resume_kind_t suspended_resume_kind;
+22 -2
include/sugar.h
··· 37 37 CORO_ASYNC_GENERATOR 38 38 } coroutine_type_t; 39 39 40 + typedef enum { 41 + CORO_HOLD_ACTIVE = 1u << 0, 42 + CORO_HOLD_PENDING = 1u << 1, 43 + CORO_HOLD_GENERATOR = 1u << 2, 44 + CORO_HOLD_AWAIT = 1u << 3, 45 + } coroutine_hold_t; 46 + 40 47 typedef struct coroutine { 41 48 ant_t *js; 42 49 ··· 66 73 int owner_saved_fp; 67 74 int nargs; 68 75 76 + uint32_t refcount; 77 + uint8_t hold_bits; 78 + 69 79 bool is_settled; 70 80 bool is_error; 71 81 bool is_done; ··· 73 83 bool mco_started; 74 84 bool is_ready; 75 85 bool did_suspend; 76 - bool free_pending; 86 + bool await_registered; 87 + bool destroy_requested; 77 88 } coroutine_t; 78 89 79 90 typedef struct { ··· 96 107 97 108 void enqueue_coroutine(coroutine_t *coro); 98 109 void remove_coroutine(coroutine_t *coro); 99 - void free_coroutine(coroutine_t *coro); 110 + 111 + void coroutine_retain(coroutine_t *coro); 112 + void coroutine_release(coroutine_t *coro); 113 + 114 + void coroutine_hold(coroutine_t *coro, uint8_t hold); 115 + void coroutine_unhold(coroutine_t *coro, uint8_t hold); 116 + 100 117 void reap_retired_coroutines(void); 118 + void free_coroutine(coroutine_t *coro); 119 + void coroutine_clear_await_registration(coroutine_t *coro); 101 120 102 121 ant_value_t start_async_in_coroutine(ant_t *js, const char *code, size_t code_len, ant_value_t closure_scope, ant_value_t *args, int nargs); 103 122 ant_value_t resume_coroutine_wrapper(ant_t *js, ant_value_t *args, int nargs); 104 123 ant_value_t reject_coroutine_wrapper(ant_t *js, ant_value_t *args, int nargs); 105 124 106 125 js_await_result_t js_promise_await_coroutine(ant_t *js, ant_value_t promise, coroutine_t *coro); 126 + void js_promise_clear_await_coroutine(ant_t *js, ant_value_t promise, coroutine_t *coro); 107 127 void settle_and_resume_coroutine(ant_t *js, coroutine_t *coro, ant_value_t value, bool is_error); 108 128 109 129 bool has_ready_coroutines(void);
+23
src/ant.c
··· 11321 11321 return value; 11322 11322 } 11323 11323 11324 + void js_promise_clear_await_coroutine(ant_t *js, ant_value_t promise, coroutine_t *coro) { 11325 + (void)js; 11326 + if (vtype(promise) != T_PROMISE || !coro) return; 11327 + 11328 + ant_promise_state_t *pd = get_promise_data(js, promise, false); 11329 + if (!pd || pd->handler_count == 0) return; 11330 + 11331 + if (pd->handler_count == 1) { 11332 + if (pd->inline_handler.await_coro == coro) pd->inline_handler.await_coro = NULL; 11333 + return; 11334 + } 11335 + 11336 + if (!pd->handlers) return; 11337 + promise_handler_t *h = NULL; 11338 + while ((h = (promise_handler_t *)utarray_next(pd->handlers, h))) { 11339 + if (h->await_coro == coro) h->await_coro = NULL; 11340 + } 11341 + } 11342 + 11324 11343 js_await_result_t js_promise_await_coroutine(ant_t *js, ant_value_t promise, coroutine_t *coro) { 11325 11344 js_await_result_t result = { 11326 11345 .state = JS_AWAIT_PENDING, ··· 11342 11361 result.value = js_mkerr(js, "out of memory"); 11343 11362 return result; 11344 11363 } 11364 + 11365 + coro->awaited_promise = promise; 11366 + coro->await_registered = true; 11367 + coroutine_hold(coro, CORO_HOLD_AWAIT); 11345 11368 11346 11369 js_mark_promise_rejection_handled_chain(js, promise); 11347 11370 if (pd->state == 0) gc_root_pending_promise(js_obj_ptr(js_as_obj(promise)));
+13 -20
src/modules/generator.c
··· 182 182 static void generator_clear_coro(ant_value_t gen, coroutine_t *coro) { 183 183 generator_data_t *data = generator_data(gen); 184 184 if (data && data->coro == coro) data->coro = NULL; 185 - if (coro) free_coroutine(coro); 185 + if (coro) coroutine_unhold(coro, CORO_HOLD_GENERATOR); 186 186 } 187 187 188 188 coroutine_t *generator_get_coro_for_gc(ant_value_t gen) { ··· 285 285 static void generator_finalize(ant_t *js, ant_object_t *obj) { 286 286 ant_value_t gen = js_obj_from_ptr(obj); 287 287 generator_data_t *data = (generator_data_t *)js_get_native_ptr(gen); 288 + 288 289 if (!data) return; 289 - 290 - if (data->coro) { 291 - coroutine_t *coro = data->coro; 292 - bool linked_active = false; 293 - bool linked_pending = 294 - coro->prev || coro->next || 295 - pending_coroutines.head == coro || 296 - pending_coroutines.tail == coro; 297 - 298 - for (coroutine_t *it = js->active_async_coro; it; it = it->active_parent) if (it == coro) { 299 - linked_active = true; 300 - break; 301 - } 302 - 303 - bool awaiting = vtype(coro->awaited_promise) != T_UNDEF; 304 - if (!linked_pending && !linked_active && !awaiting) free_coroutine(coro); 305 - data->coro = NULL; 306 - } 290 + if (data->coro) generator_clear_coro(gen, data->coro); 307 291 308 292 js_set_native_ptr(gen, NULL); 309 293 js_set_native_tag(gen, 0); ··· 378 362 379 363 coro->active_parent = saved_active; 380 364 js->active_async_coro = coro; 365 + coroutine_hold(coro, CORO_HOLD_ACTIVE); 381 366 382 367 ant_value_t result; 383 368 if (state == GEN_SUSPENDED_START) { ··· 398 383 GC_ROOT_PIN(js, result); 399 384 js->active_async_coro = saved_active; 400 385 coro->active_parent = NULL; 386 + coroutine_unhold(coro, CORO_HOLD_ACTIVE); 401 387 402 388 if (is_err(result)) { 403 389 generator_set_state(gen, GEN_COMPLETED); ··· 559 545 .async_promise = js_mkundef(), 560 546 .next = NULL, 561 547 .mco = NULL, 548 + .owner_vm = gen_vm, 549 + .sv_vm = gen_vm, 562 550 .mco_started = false, 563 551 .is_ready = false, 564 552 .did_suspend = false, 565 - .sv_vm = gen_vm, 553 + .refcount = 1, 554 + .hold_bits = 0, 555 + .await_registered = false, 556 + .destroy_requested = false, 566 557 }; 567 558 568 559 *data = (generator_data_t){ ··· 570 561 .state = GEN_SUSPENDED_START, 571 562 .is_async = closure->func->is_async, 572 563 }; 564 + coroutine_hold(coro, CORO_HOLD_GENERATOR); 565 + coroutine_release(coro); 573 566 574 567 js_set_native_ptr(gen, data); 575 568 js_set_native_tag(gen, GENERATOR_NATIVE_TAG);
+6 -7
src/reactor.c
··· 49 49 50 50 for (;;) { 51 51 coroutine_t *temp = NULL; 52 - for (coroutine_t *c = pending_coroutines.head; c; c = c->next) { 52 + for (coroutine_t *c = pending_coroutines.head; c; c = c->next) 53 53 if (c->is_ready && c->mco && mco_status(c->mco) == MCO_SUSPENDED) { temp = c; break; } 54 - } 55 54 56 55 if (!temp) break; 57 56 temp->is_ready = false; 58 - 57 + coroutine_retain(temp); 58 + 59 59 mco_result res; 60 60 MCO_RESUME_SAVE(js, temp->mco, res); 61 - 62 - if (res != MCO_SUCCESS || mco_status(temp->mco) == MCO_DEAD) { 61 + 62 + if (res != MCO_SUCCESS || mco_status(temp->mco) == MCO_DEAD) 63 63 remove_coroutine(temp); 64 - free_coroutine(temp); 65 - } 64 + coroutine_release(temp); 66 65 } 67 66 68 67 if (g_poll_hook) g_poll_hook(g_poll_hook_data);
+22 -8
src/silver/engine.c
··· 1702 1702 L_ITER_GET_VALUE: { sv_op_iter_get_value(vm, js); NEXT(1); } 1703 1703 L_ITER_CLOSE: { sv_op_iter_close(vm, js); NEXT(1); } 1704 1704 L_ITER_CALL: { VM_CHECK(sv_op_iter_call(vm, js, ip)); NEXT(2); } 1705 - L_AWAIT_ITER_NEXT: { VM_CHECK(sv_op_await_iter_next(vm, js)); NEXT(1); } 1705 + 1706 + L_AWAIT_ITER_NEXT: { 1707 + sv_await_result_t await_result = sv_op_await_iter_next(vm, js); 1708 + if (await_result.state == SV_AWAIT_ERROR) { 1709 + sv_err = await_result.value; 1710 + goto sv_throw; 1711 + } 1712 + if (await_result.state == SV_AWAIT_SUSPENDED) { 1713 + if (await_result.handoff) vm_result = js_mkundef(); 1714 + goto sv_leave; 1715 + } 1716 + NEXT(1); 1717 + } 1718 + 1706 1719 L_DESTRUCTURE_INIT: { VM_CHECK(sv_op_destructure_init(vm, js)); NEXT(1); } 1707 1720 L_DESTRUCTURE_NEXT: { VM_CHECK(sv_op_destructure_next(vm, js)); NEXT(1); } 1708 1721 L_DESTRUCTURE_REST: { VM_CHECK(sv_op_destructure_rest(vm, js)); NEXT(1); } ··· 1714 1727 vm->suspended_entry_fp = entry_fp; 1715 1728 vm->suspended_saved_fp = entry_fp - 1; 1716 1729 1717 - ant_value_t result = sv_await_value(vm, js, await_val); 1718 - if (vm->async_handoff_pending) { 1719 - vm->async_handoff_pending = false; 1730 + sv_await_result_t await_result = sv_await_value(vm, js, await_val); 1731 + if (await_result.state == SV_AWAIT_SUSPENDED && await_result.handoff) { 1732 + vm->suspended_entry_fp = -1; 1733 + vm->suspended_saved_fp = -1; 1720 1734 vm_result = js_mkundef(); 1721 1735 goto sv_leave; 1722 1736 } 1723 - if (vm->suspended) goto sv_leave; 1724 1737 1738 + if (await_result.state == SV_AWAIT_SUSPENDED) goto sv_leave; 1725 1739 vm->suspended_entry_fp = -1; 1726 1740 vm->suspended_saved_fp = -1; 1727 1741 1728 - if (is_err(result)) { 1729 - sv_err = result; 1742 + if (await_result.state == SV_AWAIT_ERROR) { 1743 + sv_err = await_result.value; 1730 1744 goto sv_throw; 1731 1745 } 1732 1746 1733 - vm->stack[vm->sp++] = result; 1747 + vm->stack[vm->sp++] = await_result.value; 1734 1748 NEXT(1); 1735 1749 } 1736 1750
+166 -104
src/silver/ops/async.h
··· 88 88 sv_vm_t *vm; 89 89 } sv_tla_ctx_t; 90 90 91 + typedef enum { 92 + SV_AWAIT_READY = 0, 93 + SV_AWAIT_ERROR, 94 + SV_AWAIT_SUSPENDED, 95 + } sv_await_state_t; 96 + 97 + typedef struct { 98 + sv_await_state_t state; 99 + ant_value_t value; 100 + bool handoff; 101 + } sv_await_result_t; 102 + 91 103 static inline void sv_async_link_activation(ant_t *js, coroutine_t *coro) { 92 104 if (!js || !coro) return; 93 105 coro->active_parent = js->active_async_coro; 94 106 js->active_async_coro = coro; 107 + coroutine_hold(coro, CORO_HOLD_ACTIVE); 95 108 } 96 109 97 110 static inline void sv_async_unlink_activation(ant_t *js, coroutine_t *coro) { 98 111 if (!js || !coro) return; 99 112 if (js->active_async_coro == coro) js->active_async_coro = coro->active_parent; 100 113 coro->active_parent = NULL; 114 + coroutine_unhold(coro, CORO_HOLD_ACTIVE); 115 + } 116 + 117 + static inline bool sv_async_coro_matches_vm(const coroutine_t *coro, const sv_vm_t *vm) { 118 + if (!coro || !vm) return false; 119 + if (coro->sv_vm == vm) return true; 120 + return coro->owner_vm == vm; 101 121 } 102 122 103 123 static inline coroutine_t *sv_async_get_active_coro_for_vm(ant_t *js, sv_vm_t *vm) { 104 124 if (!js || !js->active_async_coro) return NULL; 105 125 106 - coroutine_t *fallback = js->active_async_coro; 107 - if (!vm) return fallback; 126 + if (!vm) return js->active_async_coro; 108 127 109 - for (coroutine_t *it = fallback; it; it = it->active_parent) { 110 - if (it->owner_vm == vm) return it; 128 + for (coroutine_t *it = js->active_async_coro; it; it = it->active_parent) { 129 + if (sv_async_coro_matches_vm(it, vm)) return it; 111 130 } 112 131 113 - return fallback; 132 + return NULL; 114 133 } 115 134 116 135 static inline void sv_async_init_activation( ··· 141 160 .owner_entry_fp = owner_vm ? owner_vm->fp : -1, 142 161 .owner_saved_fp = owner_vm ? owner_vm->fp - 1 : -1, 143 162 .nargs = nargs, 163 + .refcount = 1, 164 + .hold_bits = 0, 144 165 .is_settled = false, 145 166 .is_error = false, 146 167 .is_done = false, ··· 148 169 .mco_started = false, 149 170 .is_ready = false, 150 171 .did_suspend = false, 151 - .free_pending = false, 172 + .await_registered = false, 173 + .destroy_requested = false, 152 174 }; 153 175 } 154 176 ··· 183 205 if (!source_vm || !js || !coro || coro->sv_vm) return coro ? coro->sv_vm : NULL; 184 206 if (source_vm->fp < 0) return NULL; 185 207 186 - sv_frame_t *source_frame = &source_vm->frames[source_vm->fp]; 187 - int stack_base = source_frame->prev_sp; 208 + int entry_fp = source_vm->suspended_entry_fp; 209 + if (entry_fp < 0 || entry_fp > source_vm->fp) entry_fp = source_vm->fp; 210 + 211 + sv_frame_t *entry_frame = &source_vm->frames[entry_fp]; 212 + int frame_count = source_vm->fp - entry_fp + 1; 213 + int stack_base = entry_frame->prev_sp; 188 214 int stack_count = source_vm->sp - stack_base; 189 - int handler_count = source_vm->handler_depth - source_frame->handler_base; 215 + int handler_base = entry_frame->handler_base; 216 + int handler_count = source_vm->handler_depth - handler_base; 190 217 191 218 sv_vm_t *async_vm = sv_vm_create(js, SV_VM_ASYNC); 192 219 if (!async_vm) return NULL; 193 220 if (stack_count < 0 || stack_count > async_vm->stack_size) { 221 + sv_vm_destroy(async_vm); 222 + return NULL; 223 + } 224 + if (frame_count < 1 || frame_count > async_vm->max_frames) { 194 225 sv_vm_destroy(async_vm); 195 226 return NULL; 196 227 } ··· 207 238 ); 208 239 } 209 240 async_vm->sp = stack_count; 241 + async_vm->fp = frame_count - 1; 210 242 211 - async_vm->fp = 0; 212 - async_vm->frames[0] = *source_frame; 213 - async_vm->frames[0].prev_sp = 0; 214 - async_vm->frames[0].handler_base = 0; 215 - async_vm->frames[0].handler_top = handler_count; 243 + for (int i = 0; i < frame_count; i++) { 244 + sv_frame_t *src = &source_vm->frames[entry_fp + i]; 245 + sv_frame_t *dst = &async_vm->frames[i]; 246 + *dst = *src; 247 + dst->prev_sp = src->prev_sp - stack_base; 248 + dst->handler_base = src->handler_base - handler_base; 249 + dst->handler_top = src->handler_top - handler_base; 216 250 217 - if (source_frame->bp) 218 - async_vm->frames[0].bp = async_vm->stack + (source_frame->bp - &source_vm->stack[stack_base]); 219 - if (source_frame->lp) 220 - async_vm->frames[0].lp = async_vm->stack + (source_frame->lp - &source_vm->stack[stack_base]); 251 + if (src->bp) 252 + dst->bp = async_vm->stack + (src->bp - &source_vm->stack[stack_base]); 253 + if (src->lp) 254 + dst->lp = async_vm->stack + (src->lp - &source_vm->stack[stack_base]); 255 + } 221 256 222 257 if (handler_count > 0) { 223 258 memcpy( 224 259 async_vm->handler_stack, 225 - &source_vm->handler_stack[source_frame->handler_base], 260 + &source_vm->handler_stack[handler_base], 226 261 sizeof(sv_handler_t) * (size_t)handler_count 227 262 ); 228 263 } ··· 239 274 return async_vm; 240 275 } 241 276 242 - static inline coroutine_t *sv_async_create_materialized_coro( 243 - sv_vm_t *async_vm, coroutine_t *coro 277 + static inline bool sv_async_materialize_activation( 278 + sv_vm_t *source_vm, sv_vm_t *async_vm, coroutine_t *coro 244 279 ) { 245 - if (!async_vm || !coro) return NULL; 246 - 247 - coroutine_t *heap_coro = (coroutine_t *)CORO_MALLOC(sizeof(coroutine_t)); 248 - if (!heap_coro) { 249 - sv_vm_destroy(async_vm); 250 - return NULL; 251 - } 252 - 253 - *heap_coro = *coro; 254 - heap_coro->prev = NULL; 255 - heap_coro->next = NULL; 256 - heap_coro->sv_vm = async_vm; 257 - heap_coro->materialized = true; 258 - heap_coro->free_pending = false; 259 - return heap_coro; 260 - } 280 + if (!source_vm || !async_vm || !coro || source_vm->fp < 0) return false; 261 281 262 - static inline void sv_async_finalize_materialization( 263 - sv_vm_t *source_vm, sv_vm_t *async_vm, 264 - coroutine_t *pending_coro, coroutine_t *heap_coro 265 - ) { 266 - if (!source_vm || !async_vm || !pending_coro || !heap_coro || source_vm->fp < 0) return; 267 - 268 - sv_frame_t *source_frame = &source_vm->frames[source_vm->fp]; 269 - ant_value_t *source_base = &source_vm->stack[source_frame->prev_sp]; 270 - size_t stack_count = (size_t)(source_vm->sp - source_frame->prev_sp); 271 - 282 + int entry_fp = source_vm->suspended_entry_fp; 283 + if (entry_fp < 0 || entry_fp > source_vm->fp) entry_fp = source_vm->fp; 284 + sv_frame_t *entry_frame = &source_vm->frames[entry_fp]; 285 + ant_value_t *source_base = &source_vm->stack[entry_frame->prev_sp]; 286 + size_t stack_count = (size_t)(source_vm->sp - entry_frame->prev_sp); 272 287 sv_async_move_open_upvalues( 273 288 source_vm, async_vm, source_base, async_vm->stack, stack_count 274 289 ); 275 290 276 - pending_coro->owner_entry_fp = source_vm->fp; 277 - pending_coro->owner_saved_fp = source_vm->fp - 1; 278 - pending_coro->sv_vm = async_vm; 279 - pending_coro->materialized = true; 280 - heap_coro->owner_entry_fp = source_vm->fp; 281 - heap_coro->owner_saved_fp = source_vm->fp - 1; 282 - source_vm->async_handoff_pending = true; 291 + coro->owner_entry_fp = source_vm->suspended_entry_fp; 292 + coro->owner_saved_fp = source_vm->suspended_saved_fp; 293 + coro->sv_vm = async_vm; 294 + coro->materialized = true; 295 + return true; 283 296 } 284 297 285 298 static void sv_mco_tla_entry(mco_coro *mco) { ··· 317 330 GC_ROOT_PIN(js, this_val); 318 331 GC_ROOT_PIN(js, promise); 319 332 320 - sv_vm_t *async_vm = sv_vm_create(js, SV_VM_ASYNC); 321 - if (!async_vm) { 322 - GC_ROOT_RESTORE(js, root_mark); 323 - return js_mkerr(js, "out of memory for TLA VM"); 324 - } 325 - 326 333 coroutine_t *coro = (coroutine_t *)CORO_MALLOC(sizeof(coroutine_t)); 327 334 if (!coro) { 328 - sv_vm_destroy(async_vm); 329 335 GC_ROOT_RESTORE(js, root_mark); 330 336 return js_mkerr(js, "out of memory for TLA coroutine"); 331 337 } 332 338 333 339 sv_async_init_activation( 334 - coro, js, async_vm, promise, this_val, 340 + coro, js, js->vm, promise, this_val, 335 341 js_mkundef(), js_mkundef(), js_mkundef(), 0 336 342 ); 337 - coro->sv_vm = async_vm; 338 - coro->materialized = true; 339 343 sv_async_link_activation(js, coro); 340 344 341 345 ant_value_t result = sv_execute_entry( 342 - async_vm, func, 346 + js->vm, func, 343 347 this_val, NULL, 0 344 348 ); 349 + sv_async_unlink_activation(js, coro); 345 350 346 - if (async_vm->suspended) { 347 - sv_async_unlink_activation(js, coro); 348 - enqueue_coroutine(coro); 351 + if (coro->sv_vm && coro->sv_vm->suspended) { 352 + coroutine_release(coro); 349 353 GC_ROOT_RESTORE(js, root_mark); 350 354 return promise; 351 355 } ··· 358 362 } else { 359 363 js_resolve_promise(js, promise, result); 360 364 } 361 - sv_async_unlink_activation(js, coro); 362 - free_coroutine(coro); 365 + coroutine_release(coro); 363 366 GC_ROOT_RESTORE(js, root_mark); 364 367 365 368 return promise; ··· 421 424 .async_promise = promise, 422 425 .next = NULL, 423 426 .mco = mco, 427 + .owner_vm = async_vm, 428 + .sv_vm = async_vm, 424 429 .mco_started = false, 425 430 .is_ready = true, 426 431 .did_suspend = false, 427 - .sv_vm = async_vm, 432 + .refcount = 1, 433 + .hold_bits = 0, 434 + .await_registered = false, 435 + .destroy_requested = false, 428 436 }; 429 437 430 438 ctx->coro = coro; ··· 433 441 434 442 if (res != MCO_SUCCESS && mco_status(mco) != MCO_DEAD) { 435 443 remove_coroutine(coro); 436 - free_coroutine(coro); 444 + coroutine_release(coro); 437 445 return js_mkerr(js, "failed to start TLA coroutine"); 438 446 } 439 447 440 448 coro->mco_started = true; 441 449 if (mco_status(mco) == MCO_DEAD) { 442 450 remove_coroutine(coro); 443 - free_coroutine(coro); 444 451 } 452 + 453 + coroutine_release(coro); 445 454 446 455 return promise; 447 456 } ··· 497 506 498 507 ant_value_t promise = js_mkpromise(js); 499 508 GC_ROOT_PIN(js, promise); 500 - coroutine_t pending_coro; 509 + coroutine_t *coro = (coroutine_t *)CORO_MALLOC(sizeof(coroutine_t)); 510 + if (!coro) { 511 + GC_ROOT_RESTORE(js, root_mark); 512 + return js_mkerr(js, "out of memory for async coroutine"); 513 + } 514 + 501 515 sv_async_init_activation( 502 - &pending_coro, js, caller_vm, promise, this_val, 516 + coro, js, caller_vm, promise, this_val, 503 517 super_val, js->new_target, callee_func, argc 504 518 ); 505 - sv_async_link_activation(js, &pending_coro); 519 + sv_async_link_activation(js, coro); 506 520 507 521 ant_value_t result = sv_execute_closure_entry( 508 522 caller_vm, closure, callee_func, 509 523 super_val, this_val, args, argc, NULL 510 524 ); 511 525 512 - sv_async_unlink_activation(js, &pending_coro); 526 + sv_async_unlink_activation(js, coro); 513 527 514 - if (pending_coro.materialized && pending_coro.sv_vm) { 528 + if (coro->sv_vm && coro->sv_vm->suspended) { 529 + coroutine_release(coro); 515 530 GC_ROOT_RESTORE(js, root_mark); 516 531 return promise; 517 532 } ··· 524 539 } else { 525 540 js_resolve_promise(js, promise, result); 526 541 } 542 + coroutine_release(coro); 527 543 GC_ROOT_RESTORE(js, root_mark); 528 544 529 545 return promise; ··· 601 617 .async_promise = promise, 602 618 .next = NULL, 603 619 .mco = mco, 620 + .owner_vm = async_vm, 621 + .sv_vm = async_vm, 604 622 .mco_started = false, 605 623 .is_ready = true, 606 624 .did_suspend = false, 607 - .sv_vm = async_vm, 625 + .refcount = 1, 626 + .hold_bits = 0, 627 + .await_registered = false, 628 + .destroy_requested = false, 608 629 }; 609 630 610 631 ctx->coro = coro; ··· 614 635 615 636 if (res != MCO_SUCCESS && start_status != MCO_DEAD) { 616 637 remove_coroutine(coro); 617 - free_coroutine(coro); 638 + coroutine_release(coro); 618 639 return js_mkerr(js, "failed to start async coroutine"); 619 640 } 620 641 621 642 coro->mco_started = true; 622 643 if (start_status == MCO_DEAD) { 623 644 remove_coroutine(coro); 624 - free_coroutine(coro); 625 645 } 646 + 647 + coroutine_release(coro); 626 648 627 649 return promise; 628 650 } 629 651 630 652 631 653 632 - static inline ant_value_t sv_await_value(sv_vm_t *vm, ant_t *js, ant_value_t value) { 654 + static inline sv_await_result_t sv_await_value(sv_vm_t *vm, ant_t *js, ant_value_t value) { 655 + sv_await_result_t out = { 656 + .state = SV_AWAIT_READY, 657 + .value = js_mkundef(), 658 + .handoff = false, 659 + }; 660 + 633 661 value = js_promise_assimilate_awaitable(js, value); 634 - if (is_err(value)) return value; 635 - if (vtype(value) != T_PROMISE) return value; 662 + if (is_err(value)) { 663 + out.state = SV_AWAIT_ERROR; 664 + out.value = value; 665 + return out; 666 + } 667 + if (vtype(value) != T_PROMISE) { 668 + out.value = value; 669 + return out; 670 + } 636 671 637 672 mco_coro *current_mco = mco_running(); 638 673 if (!current_mco) current_mco = NULL; ··· 643 678 if (hdr) coro = hdr->coro; 644 679 } else coro = sv_async_get_active_coro_for_vm(js, vm); 645 680 646 - if (!coro) 647 - return js_mkerr(js, "await can only be used inside async functions"); 681 + if (!coro) { 682 + out.state = SV_AWAIT_ERROR; 683 + out.value = js_mkerr(js, "await can only be used inside async functions"); 684 + return out; 685 + } 648 686 649 687 sv_vm_t *prepared_vm = NULL; 650 - coroutine_t *pending_coro = coro; 688 + bool handoff = false; 651 689 if (!current_mco && vm && coro->owner_vm == vm && !coro->sv_vm) { 652 690 prepared_vm = sv_async_prepare_materialization(vm, js, coro); 653 - if (!prepared_vm) return js_mkerr(js, "out of memory for async VM"); 654 - coroutine_t *materialized_coro = sv_async_create_materialized_coro(prepared_vm, coro); 655 - if (!materialized_coro) return js_mkerr(js, "out of memory for coroutine"); 656 - coro = materialized_coro; 657 - enqueue_coroutine(coro); 691 + if (!prepared_vm) { 692 + out.state = SV_AWAIT_ERROR; 693 + out.value = js_mkerr(js, "out of memory for async VM"); 694 + return out; 695 + } 696 + handoff = true; 658 697 } 659 698 660 - coro->awaited_promise = value; 661 699 coro->is_settled = false; 662 700 coro->is_ready = false; 663 701 js_await_result_t await_result = js_promise_await_coroutine(js, value, coro); 664 702 665 703 if (await_result.state == JS_AWAIT_ERROR) { 666 - if (prepared_vm) free_coroutine(coro); 704 + if (prepared_vm) { 705 + sv_vm_destroy(prepared_vm); 706 + coro->sv_vm = NULL; 707 + coro->materialized = false; 708 + } 667 709 coro->is_settled = false; 668 - coro->awaited_promise = js_mkundef(); 669 - return js_throw(js, await_result.value); 710 + out.state = SV_AWAIT_ERROR; 711 + out.value = js_throw(js, await_result.value); 712 + return out; 670 713 } 671 714 672 715 coro->did_suspend = true; 673 716 if (!current_mco) { 674 - if (prepared_vm) sv_async_finalize_materialization(vm, prepared_vm, pending_coro, coro); 675 - if (coro->sv_vm) coro->sv_vm->suspended = true; 676 - return js_mkundef(); 717 + if (prepared_vm) { 718 + if (!sv_async_materialize_activation(vm, prepared_vm, coro)) { 719 + coroutine_clear_await_registration(coro); 720 + sv_vm_destroy(prepared_vm); 721 + out.state = SV_AWAIT_ERROR; 722 + out.value = js_mkerr(js, "failed to materialize async activation"); 723 + return out; 724 + } 725 + } 726 + out.state = SV_AWAIT_SUSPENDED; 727 + out.handoff = handoff; 728 + if (handoff) coro->sv_vm->suspended = true; 729 + else if (coro->sv_vm) coro->sv_vm->suspended = true; 730 + return out; 677 731 } 678 732 679 733 mco_result mco_res = mco_yield(current_mco); 680 - if (mco_res != MCO_SUCCESS) 681 - return js_mkerr(js, "failed to yield coroutine"); 734 + if (mco_res != MCO_SUCCESS) { 735 + out.state = SV_AWAIT_ERROR; 736 + out.value = js_mkerr(js, "failed to yield coroutine"); 737 + return out; 738 + } 682 739 683 740 MCO_CORO_STACK_ENTER(js, current_mco); 684 - ant_value_t result = coro->result; 741 + out.value = coro->result; 685 742 bool is_error = coro->is_error; 686 743 687 744 coro->is_settled = false; 688 745 coro->awaited_promise = js_mkundef(); 689 - if (is_error) return js_throw(js, result); 746 + if (is_error) { 747 + out.state = SV_AWAIT_ERROR; 748 + out.value = js_throw(js, out.value); 749 + return out; 750 + } 690 751 691 - return result; 752 + out.state = SV_AWAIT_READY; 753 + return out; 692 754 } 693 755 694 756
+32 -12
src/silver/ops/iteration.h
··· 358 358 return tov(0); 359 359 } 360 360 361 - static inline ant_value_t sv_op_await_iter_next(sv_vm_t *vm, ant_t *js) { 361 + static inline sv_await_result_t sv_op_await_iter_next(sv_vm_t *vm, ant_t *js) { 362 + sv_await_result_t out = { 363 + .state = SV_AWAIT_READY, 364 + .value = js_mkundef(), 365 + .handoff = false, 366 + }; 362 367 ant_value_t next_method = vm->stack[vm->sp - 2]; 363 368 ant_value_t iterator = vm->stack[vm->sp - 3]; 364 369 uint8_t ft = vtype(next_method); 365 370 if (ft != T_FUNC && ft != T_CFUNC) 366 - return js_mkerr(js, "iterator.next is not a function"); 371 + return (sv_await_result_t){ .state = SV_AWAIT_ERROR, .value = js_mkerr(js, "iterator.next is not a function"), .handoff = false }; 367 372 ant_value_t result = sv_vm_call(vm, js, next_method, iterator, NULL, 0, NULL, false); 368 - if (is_err(result)) return result; 373 + if (is_err(result)) 374 + return (sv_await_result_t){ .state = SV_AWAIT_ERROR, .value = result, .handoff = false }; 369 375 if (vtype(result) == T_PROMISE) { 370 - ant_value_t awaited = sv_await_value(vm, js, result); 371 - if (is_err(awaited)) return awaited; 372 - result = awaited; 376 + vm->suspended_entry_fp = vm->fp; 377 + vm->suspended_saved_fp = vm->fp - 1; 378 + sv_await_result_t awaited = sv_await_value(vm, js, result); 379 + if (awaited.state != SV_AWAIT_SUSPENDED || awaited.handoff) { 380 + vm->suspended_entry_fp = -1; 381 + vm->suspended_saved_fp = -1; 382 + } 383 + if (awaited.state != SV_AWAIT_READY) return awaited; 384 + result = awaited.value; 373 385 } 374 386 ant_value_t done = js_mkundef(); 375 387 ant_value_t value = js_mkundef(); 376 388 sv_iter_result_unpack(js, result, &done, &value); 377 - if (is_err(done)) return done; 378 - if (is_err(value)) return value; 389 + if (is_err(done)) 390 + return (sv_await_result_t){ .state = SV_AWAIT_ERROR, .value = done, .handoff = false }; 391 + if (is_err(value)) 392 + return (sv_await_result_t){ .state = SV_AWAIT_ERROR, .value = value, .handoff = false }; 379 393 if (vtype(value) == T_PROMISE) { 380 - ant_value_t awaited_val = sv_await_value(vm, js, value); 381 - if (is_err(awaited_val)) return awaited_val; 382 - value = awaited_val; 394 + vm->suspended_entry_fp = vm->fp; 395 + vm->suspended_saved_fp = vm->fp - 1; 396 + sv_await_result_t awaited_val = sv_await_value(vm, js, value); 397 + if (awaited_val.state != SV_AWAIT_SUSPENDED || awaited_val.handoff) { 398 + vm->suspended_entry_fp = -1; 399 + vm->suspended_saved_fp = -1; 400 + } 401 + if (awaited_val.state != SV_AWAIT_READY) return awaited_val; 402 + value = awaited_val.value; 383 403 } 384 404 vm->stack[vm->sp++] = value; 385 405 vm->stack[vm->sp++] = mkval(T_BOOL, js_truthy(js, done)); 386 - return tov(0); 406 + return out; 387 407 } 388 408 389 409 #endif
+78 -58
src/sugar.c
··· 32 32 33 33 void enqueue_coroutine(coroutine_t *coro) { 34 34 if (!coro) return; 35 + if (coro->hold_bits & CORO_HOLD_PENDING) return; 35 36 coro->next = NULL; 36 37 coro->prev = pending_coroutines.tail; 37 38 ··· 39 40 pending_coroutines.tail->next = coro; 40 41 } else pending_coroutines.head = coro; 41 42 pending_coroutines.tail = coro; 43 + coroutine_hold(coro, CORO_HOLD_PENDING); 42 44 } 43 45 44 46 void remove_coroutine(coroutine_t *coro) { 45 - if (!coro) return; 47 + if (!coro || !(coro->hold_bits & CORO_HOLD_PENDING)) return; 46 48 47 49 if (coro->prev) { 48 50 coro->prev->next = coro->next; ··· 54 56 55 57 coro->prev = NULL; 56 58 coro->next = NULL; 57 - } 58 - 59 - static void clear_await_coro_from_promise_state(ant_promise_state_t *pd, coroutine_t *coro) { 60 - if (!pd || !coro || pd->handler_count == 0) return; 61 - 62 - if (pd->handler_count == 1) { 63 - if (pd->inline_handler.await_coro == coro) pd->inline_handler.await_coro = NULL; 64 - return; 65 - } 66 - 67 - if (!pd->handlers) return; 68 - promise_handler_t *h = NULL; 69 - 70 - while ((h = (promise_handler_t *)utarray_next(pd->handlers, h))) 71 - if (h->await_coro == coro) h->await_coro = NULL; 72 - } 73 - 74 - static void clear_await_coro_from_object_list(ant_object_t *head, coroutine_t *coro) { 75 - for (ant_object_t *obj = head; obj; obj = obj->next) 76 - if (obj->promise_state) clear_await_coro_from_promise_state(obj->promise_state, coro); 77 - } 78 - 79 - static inline bool coroutine_is_queued(coroutine_t *coro) { 80 - return coro && ( 81 - coro->prev || coro->next || 82 - pending_coroutines.head == coro || 83 - pending_coroutines.tail == coro 84 - ); 59 + coroutine_unhold(coro, CORO_HOLD_PENDING); 85 60 } 86 61 87 62 static void retire_coroutine_storage(coroutine_t *coro) { ··· 115 90 coro->owner_vm = NULL; 116 91 coro->active_parent = NULL; 117 92 coro->materialized = false; 93 + } 94 + 95 + void coroutine_retain(coroutine_t *coro) { 96 + if (!coro) return; 97 + coro->refcount++; 98 + } 99 + 100 + static void coroutine_release_storage(coroutine_t *coro) { 101 + if (!coro) return; 102 + 103 + ant_t *js = coro->js; 104 + if (js && js->vm_exec_depth > 0) retire_coroutine_storage(coro); 105 + else { 106 + destroy_coroutine_resources(coro); 107 + CORO_FREE(coro); 108 + } 109 + } 110 + 111 + void coroutine_release(coroutine_t *coro) { 112 + if (!coro || coro->refcount == 0) return; 113 + coro->refcount--; 114 + if (coro->refcount != 0) return; 115 + coroutine_release_storage(coro); 116 + } 117 + 118 + void coroutine_hold(coroutine_t *coro, uint8_t hold) { 119 + if (!coro || (coro->hold_bits & hold)) return; 120 + coro->hold_bits |= hold; 121 + coroutine_retain(coro); 122 + } 123 + 124 + void coroutine_unhold(coroutine_t *coro, uint8_t hold) { 125 + if (!coro || !(coro->hold_bits & hold)) return; 126 + coro->hold_bits &= (uint8_t)~hold; 127 + coroutine_release(coro); 118 128 } 119 129 120 130 void reap_retired_coroutines(void) { ··· 129 139 } 130 140 } 131 141 132 - void free_coroutine(coroutine_t *coro) { 133 - if (!coro || coro->free_pending) return; 134 - coro->free_pending = true; 142 + void coroutine_clear_await_registration(coroutine_t *coro) { 143 + if (!coro || !coro->await_registered) return; 135 144 136 145 ant_t *js = coro->js; 137 - if (js) { 138 - clear_await_coro_from_object_list(js->objects, coro); 139 - clear_await_coro_from_object_list(js->objects_old, coro); 140 - clear_await_coro_from_object_list(js->permanent_objects, coro); 141 - 142 - if ( 143 - coro->prev || coro->next || 144 - pending_coroutines.head == coro || 145 - pending_coroutines.tail == coro 146 - ) remove_coroutine(coro); 147 - 148 - if (js->active_async_coro == coro) js->active_async_coro = coro->active_parent; 149 - coro->active_parent = NULL; 150 - } 146 + ant_value_t promise = coro->awaited_promise; 147 + coro->await_registered = false; 148 + coro->awaited_promise = js_mkundef(); 149 + 150 + if (js && vtype(promise) == T_PROMISE) 151 + js_promise_clear_await_coroutine(js, promise, coro); 151 152 152 - if (!js || js->vm_exec_depth == 0) 153 - destroy_coroutine_resources(coro); 153 + coroutine_unhold(coro, CORO_HOLD_AWAIT); 154 + } 154 155 155 - retire_coroutine_storage(coro); 156 + void free_coroutine(coroutine_t *coro) { 157 + if (!coro) return; 158 + coroutine_clear_await_registration(coro); 159 + coroutine_release(coro); 156 160 } 157 161 158 162 static size_t calculate_coro_stack_size(void) { ··· 180 184 181 185 static void resume_coroutine_if_suspended(ant_t *js, coroutine_t *coro) { 182 186 if (!coro) return; 187 + coroutine_retain(coro); 183 188 184 189 if (!coro->mco) { 185 - if (!coro->sv_vm || !coro->sv_vm->suspended) return; 190 + if (!coro->sv_vm || !coro->sv_vm->suspended) { 191 + coroutine_release(coro); 192 + return; 193 + } 186 194 187 195 coro->is_ready = false; 188 196 coro->sv_vm->suspended_resume_value = coro->result; ··· 192 200 193 201 coro->active_parent = js->active_async_coro; 194 202 js->active_async_coro = coro; 203 + coroutine_hold(coro, CORO_HOLD_ACTIVE); 195 204 ant_value_t result = sv_resume_suspended(coro->sv_vm); 196 205 197 206 coro->is_settled = false; 198 207 if (coro->sv_vm->suspended) { 199 208 js->active_async_coro = coro->active_parent; 200 209 coro->active_parent = NULL; 210 + coroutine_unhold(coro, CORO_HOLD_ACTIVE); 201 211 if (generator_resume_pending_request(js, coro, result)) return; 212 + coroutine_release(coro); 202 213 return; 203 214 } 204 215 205 216 js->active_async_coro = coro->active_parent; 206 217 coro->active_parent = NULL; 218 + coroutine_unhold(coro, CORO_HOLD_ACTIVE); 207 219 208 - if (generator_resume_pending_request(js, coro, result)) return; 209 - if (coroutine_is_queued(coro)) remove_coroutine(coro); 220 + if (generator_resume_pending_request(js, coro, result)) { 221 + coroutine_release(coro); 222 + return; 223 + } 210 224 211 225 if (is_err(result)) { 212 226 ant_value_t reject_value = js->thrown_exists ? js->thrown_value : result; ··· 216 230 } else js_resolve_promise(js, coro->async_promise, result); 217 231 218 232 js_maybe_drain_microtasks_after_async_settle(js); 219 - free_coroutine(coro); 233 + coroutine_release(coro); 220 234 221 235 return; 222 236 } 223 237 224 - if (mco_status(coro->mco) != MCO_SUSPENDED) return; 238 + if (mco_status(coro->mco) != MCO_SUSPENDED) { 239 + coroutine_release(coro); 240 + return; 241 + } 225 242 226 243 coro->is_ready = false; 227 244 mco_result res; 228 245 MCO_RESUME_SAVE(js, coro->mco, res); 229 246 mco_state status = mco_status(coro->mco); 230 247 231 - if (res != MCO_SUCCESS || status == MCO_DEAD) { 248 + if (res != MCO_SUCCESS || status == MCO_DEAD) 232 249 remove_coroutine(coro); 233 - free_coroutine(coro); 234 - } 250 + 251 + coroutine_release(coro); 235 252 } 236 253 237 254 ant_value_t resume_coroutine_wrapper(ant_t *js, ant_value_t *args, int nargs) { ··· 265 282 266 283 void settle_and_resume_coroutine(ant_t *js, coroutine_t *coro, ant_value_t value, bool is_error) { 267 284 if (!coro) return; 285 + coroutine_retain(coro); 286 + coroutine_clear_await_registration(coro); 287 + 268 288 ant_value_t args[1] = { value }; 269 289 settle_coroutine(coro, args, 1, is_error); 270 - coro->awaited_promise = js_mkundef(); 271 290 resume_coroutine_if_suspended(js, coro); 291 + coroutine_release(coro); 272 292 }