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.

async materialization with minicoro fallback only

+244 -104
+5 -2
include/silver/engine.h
··· 342 342 343 343 // TODO: move to nested struct 344 344 bool suspended; 345 + bool async_handoff_pending; 345 346 bool suspended_resume_pending; 346 347 bool suspended_resume_is_error; 347 348 sv_resume_kind_t suspended_resume_kind; ··· 432 433 433 434 static inline sv_vm_t *sv_vm_get_active(ant_t *js) { 434 435 if (!js) return NULL; 435 - if (js->active_async_coro && js->active_async_coro->sv_vm) 436 - return js->active_async_coro->sv_vm; 436 + if (js->active_async_coro) { 437 + if (js->active_async_coro->sv_vm) return js->active_async_coro->sv_vm; 438 + if (js->active_async_coro->owner_vm) return js->active_async_coro->owner_vm; 439 + } 437 440 return js->vm; 438 441 } 439 442
+5
include/sugar.h
··· 56 56 struct coroutine *next; 57 57 58 58 mco_coro *mco; 59 + struct sv_vm *owner_vm; 59 60 struct sv_vm *sv_vm; 60 61 61 62 ant_offset_t resume_point; 62 63 coroutine_type_t type; 63 64 65 + int owner_entry_fp; 66 + int owner_saved_fp; 64 67 int nargs; 68 + 65 69 bool is_settled; 66 70 bool is_error; 67 71 bool is_done; 72 + bool materialized; 68 73 bool mco_started; 69 74 bool is_ready; 70 75 bool did_suspend;
+1 -1
src/gc/objects.c
··· 418 418 419 419 static void gc_mark_coroutine(ant_t *js, coroutine_t *c) { 420 420 if (!c) return; 421 - gc_scan_vm_stack(js, c->sv_vm); 421 + gc_scan_vm_stack(js, c->sv_vm ? c->sv_vm : c->owner_vm); 422 422 gc_mark_value(js, c->this_val); 423 423 gc_mark_value(js, c->async_func); 424 424 gc_mark_value(js, c->async_promise);
+6 -1
src/silver/engine.c
··· 1712 1712 vm->suspended_entry_fp = entry_fp; 1713 1713 vm->suspended_saved_fp = entry_fp - 1; 1714 1714 1715 - ant_value_t result = sv_await_value(js, await_val); 1715 + ant_value_t result = sv_await_value(vm, js, await_val); 1716 + if (vm->async_handoff_pending) { 1717 + vm->async_handoff_pending = false; 1718 + vm_result = js_mkundef(); 1719 + goto sv_leave; 1720 + } 1716 1721 if (vm->suspended) goto sv_leave; 1717 1722 1718 1723 vm->suspended_entry_fp = -1;
+223 -98
src/silver/ops/async.h
··· 88 88 sv_vm_t *vm; 89 89 } sv_tla_ctx_t; 90 90 91 + static inline void sv_async_link_activation(ant_t *js, coroutine_t *coro) { 92 + if (!js || !coro) return; 93 + coro->active_parent = js->active_async_coro; 94 + js->active_async_coro = coro; 95 + } 96 + 97 + static inline void sv_async_unlink_activation(ant_t *js, coroutine_t *coro) { 98 + if (!js || !coro) return; 99 + if (js->active_async_coro == coro) js->active_async_coro = coro->active_parent; 100 + coro->active_parent = NULL; 101 + } 102 + 103 + static inline void sv_async_init_activation( 104 + coroutine_t *coro, ant_t *js, sv_vm_t *owner_vm, ant_value_t promise, 105 + ant_value_t this_val, ant_value_t super_val, ant_value_t new_target, 106 + ant_value_t async_func, int nargs 107 + ) { 108 + if (!coro) return; 109 + *coro = (coroutine_t){ 110 + .js = js, 111 + .this_val = this_val, 112 + .super_val = super_val, 113 + .new_target = new_target, 114 + .result = js_mkundef(), 115 + .async_func = async_func, 116 + .yield_value = js_mkundef(), 117 + .args = NULL, 118 + .awaited_promise = js_mkundef(), 119 + .async_promise = promise, 120 + .active_parent = NULL, 121 + .prev = NULL, 122 + .next = NULL, 123 + .mco = NULL, 124 + .owner_vm = owner_vm, 125 + .sv_vm = NULL, 126 + .resume_point = 0, 127 + .type = CORO_ASYNC_AWAIT, 128 + .owner_entry_fp = owner_vm ? owner_vm->fp : -1, 129 + .owner_saved_fp = owner_vm ? owner_vm->fp - 1 : -1, 130 + .nargs = nargs, 131 + .is_settled = false, 132 + .is_error = false, 133 + .is_done = false, 134 + .materialized = false, 135 + .mco_started = false, 136 + .is_ready = false, 137 + .did_suspend = false, 138 + .free_pending = false, 139 + }; 140 + } 141 + 142 + static inline void sv_async_move_open_upvalues( 143 + sv_vm_t *source_vm, sv_vm_t *async_vm, 144 + ant_value_t *source_base, ant_value_t *dest_base, size_t stack_count 145 + ) { 146 + if (!source_vm || !async_vm || !source_base || !dest_base || stack_count == 0) return; 147 + 148 + sv_upvalue_t **src_pp = &source_vm->open_upvalues; 149 + sv_upvalue_t **dst_pp = &async_vm->open_upvalues; 150 + 151 + while (*src_pp) { 152 + sv_upvalue_t *uv = *src_pp; 153 + if (!sv_slot_in_range(source_base, stack_count, uv->location)) { 154 + src_pp = &uv->next; 155 + continue; 156 + } 157 + 158 + ptrdiff_t slot = uv->location - source_base; 159 + *src_pp = uv->next; 160 + uv->location = &dest_base[slot]; 161 + uv->next = NULL; 162 + *dst_pp = uv; 163 + dst_pp = &uv->next; 164 + } 165 + } 166 + 167 + static inline sv_vm_t *sv_async_prepare_materialization( 168 + sv_vm_t *source_vm, ant_t *js, coroutine_t *coro 169 + ) { 170 + if (!source_vm || !js || !coro || coro->sv_vm) return coro ? coro->sv_vm : NULL; 171 + if (source_vm->fp < 0) return NULL; 172 + 173 + sv_frame_t *source_frame = &source_vm->frames[source_vm->fp]; 174 + int stack_base = source_frame->prev_sp; 175 + int stack_count = source_vm->sp - stack_base; 176 + int handler_count = source_vm->handler_depth - source_frame->handler_base; 177 + 178 + sv_vm_t *async_vm = sv_vm_create(js, SV_VM_ASYNC); 179 + if (!async_vm) return NULL; 180 + if (stack_count < 0 || stack_count > async_vm->stack_size) { 181 + sv_vm_destroy(async_vm); 182 + return NULL; 183 + } 184 + if (handler_count < 0 || handler_count > SV_HANDLER_MAX) { 185 + sv_vm_destroy(async_vm); 186 + return NULL; 187 + } 188 + 189 + if (stack_count > 0) { 190 + memcpy( 191 + async_vm->stack, 192 + &source_vm->stack[stack_base], 193 + sizeof(ant_value_t) * (size_t)stack_count 194 + ); 195 + } 196 + async_vm->sp = stack_count; 197 + 198 + async_vm->fp = 0; 199 + async_vm->frames[0] = *source_frame; 200 + async_vm->frames[0].prev_sp = 0; 201 + async_vm->frames[0].handler_base = 0; 202 + async_vm->frames[0].handler_top = handler_count; 203 + 204 + if (source_frame->bp) 205 + async_vm->frames[0].bp = async_vm->stack + (source_frame->bp - &source_vm->stack[stack_base]); 206 + if (source_frame->lp) 207 + async_vm->frames[0].lp = async_vm->stack + (source_frame->lp - &source_vm->stack[stack_base]); 208 + 209 + if (handler_count > 0) { 210 + memcpy( 211 + async_vm->handler_stack, 212 + &source_vm->handler_stack[source_frame->handler_base], 213 + sizeof(sv_handler_t) * (size_t)handler_count 214 + ); 215 + } 216 + async_vm->handler_depth = handler_count; 217 + 218 + async_vm->suspended = true; 219 + async_vm->suspended_entry_fp = 0; 220 + async_vm->suspended_saved_fp = -1; 221 + async_vm->suspended_resume_pending = false; 222 + async_vm->suspended_resume_is_error = false; 223 + async_vm->suspended_resume_kind = SV_RESUME_NEXT; 224 + async_vm->suspended_resume_value = js_mkundef(); 225 + 226 + return async_vm; 227 + } 228 + 229 + static inline coroutine_t *sv_async_create_materialized_coro( 230 + sv_vm_t *async_vm, coroutine_t *coro 231 + ) { 232 + if (!async_vm || !coro) return NULL; 233 + 234 + coroutine_t *heap_coro = (coroutine_t *)CORO_MALLOC(sizeof(coroutine_t)); 235 + if (!heap_coro) { 236 + sv_vm_destroy(async_vm); 237 + return NULL; 238 + } 239 + 240 + *heap_coro = *coro; 241 + heap_coro->prev = NULL; 242 + heap_coro->next = NULL; 243 + heap_coro->sv_vm = async_vm; 244 + heap_coro->materialized = true; 245 + heap_coro->free_pending = false; 246 + return heap_coro; 247 + } 248 + 249 + static inline void sv_async_finalize_materialization( 250 + sv_vm_t *source_vm, sv_vm_t *async_vm, 251 + coroutine_t *pending_coro, coroutine_t *heap_coro 252 + ) { 253 + if (!source_vm || !async_vm || !pending_coro || !heap_coro || source_vm->fp < 0) return; 254 + 255 + sv_frame_t *source_frame = &source_vm->frames[source_vm->fp]; 256 + ant_value_t *source_base = &source_vm->stack[source_frame->prev_sp]; 257 + size_t stack_count = (size_t)(source_vm->sp - source_frame->prev_sp); 258 + 259 + sv_async_move_open_upvalues( 260 + source_vm, async_vm, source_base, async_vm->stack, stack_count 261 + ); 262 + 263 + pending_coro->owner_entry_fp = source_vm->fp; 264 + pending_coro->owner_saved_fp = source_vm->fp - 1; 265 + pending_coro->sv_vm = async_vm; 266 + pending_coro->materialized = true; 267 + heap_coro->owner_entry_fp = source_vm->fp; 268 + heap_coro->owner_saved_fp = source_vm->fp - 1; 269 + source_vm->async_handoff_pending = true; 270 + } 271 + 91 272 static void sv_mco_tla_entry(mco_coro *mco) { 92 273 sv_tla_ctx_t *ctx = (sv_tla_ctx_t *)mco_get_user_data(mco); 93 274 ant_t *js = ctx->js; ··· 118 299 } 119 300 120 301 ant_value_t promise = js_mkpromise(js); 121 - 122 302 if (func && (!func->has_await || sv_async_func_supports_lazy_start(func))) { 123 303 GC_ROOT_SAVE(root_mark, js); 124 304 GC_ROOT_PIN(js, this_val); 125 - 305 + GC_ROOT_PIN(js, promise); 306 + 126 307 sv_vm_t *async_vm = sv_vm_create(js, SV_VM_ASYNC); 127 308 if (!async_vm) { 128 309 GC_ROOT_RESTORE(js, root_mark); 129 310 return js_mkerr(js, "out of memory for TLA VM"); 130 311 } 131 - 312 + 132 313 coroutine_t *coro = (coroutine_t *)CORO_MALLOC(sizeof(coroutine_t)); 133 314 if (!coro) { 134 315 sv_vm_destroy(async_vm); 135 316 GC_ROOT_RESTORE(js, root_mark); 136 317 return js_mkerr(js, "out of memory for TLA coroutine"); 137 318 } 138 - 139 - GC_ROOT_PIN(js, promise); 140 - *coro = (coroutine_t){ 141 - .js = js, 142 - .type = CORO_ASYNC_AWAIT, 143 - .this_val = this_val, 144 - .super_val = js_mkundef(), 145 - .new_target = js_mkundef(), 146 - .awaited_promise = js_mkundef(), 147 - .result = js_mkundef(), 148 - .async_func = js_mkundef(), 149 - .args = NULL, 150 - .nargs = 0, 151 - .active_parent = NULL, 152 - .is_settled = false, 153 - .is_error = false, 154 - .is_done = false, 155 - .resume_point = 0, 156 - .yield_value = js_mkundef(), 157 - .async_promise = promise, 158 - .next = NULL, 159 - .mco = NULL, 160 - .mco_started = false, 161 - .is_ready = false, 162 - .did_suspend = false, 163 - .sv_vm = async_vm, 164 - }; 165 - 166 - coro->active_parent = js->active_async_coro; 167 - js->active_async_coro = coro; 168 - 319 + 320 + sv_async_init_activation( 321 + coro, js, async_vm, promise, this_val, 322 + js_mkundef(), js_mkundef(), js_mkundef(), 0 323 + ); 324 + coro->sv_vm = async_vm; 325 + coro->materialized = true; 326 + sv_async_link_activation(js, coro); 327 + 169 328 ant_value_t result = sv_execute_entry( 170 - async_vm, func, 329 + async_vm, func, 171 330 this_val, NULL, 0 172 331 ); 173 - 332 + 174 333 if (async_vm->suspended) { 175 - js->active_async_coro = coro->active_parent; 176 - coro->active_parent = NULL; 334 + sv_async_unlink_activation(js, coro); 177 335 enqueue_coroutine(coro); 178 336 GC_ROOT_RESTORE(js, root_mark); 179 337 return promise; 180 338 } 181 - 339 + 182 340 if (is_err(result)) { 183 341 ant_value_t reject_value = js->thrown_exists ? js->thrown_value : result; 184 342 js->thrown_exists = false; 185 343 js->thrown_value = js_mkundef(); 186 - js->active_async_coro = coro->active_parent; 187 - coro->active_parent = NULL; 188 344 js_reject_promise(js, promise, reject_value); 189 345 } else { 190 - js->active_async_coro = coro->active_parent; 191 - coro->active_parent = NULL; 192 346 js_resolve_promise(js, promise, result); 193 347 } 194 - 348 + sv_async_unlink_activation(js, coro); 195 349 free_coroutine(coro); 196 350 GC_ROOT_RESTORE(js, root_mark); 197 351 ··· 329 483 } 330 484 331 485 ant_value_t promise = js_mkpromise(js); 332 - sv_vm_t *async_vm = sv_vm_create(js, SV_VM_ASYNC); 333 - if (!async_vm) { 334 - GC_ROOT_RESTORE(js, root_mark); 335 - return js_mkerr(js, "out of memory for async VM"); 336 - } 337 - 338 - coroutine_t *coro = (coroutine_t *)CORO_MALLOC(sizeof(coroutine_t)); 339 - if (!coro) { 340 - sv_vm_destroy(async_vm); 341 - GC_ROOT_RESTORE(js, root_mark); 342 - return js_mkerr(js, "out of memory for coroutine"); 343 - } 344 - 345 486 GC_ROOT_PIN(js, promise); 346 - *coro = (coroutine_t){ 347 - .js = js, 348 - .type = CORO_ASYNC_AWAIT, 349 - .this_val = this_val, 350 - .super_val = super_val, 351 - .new_target = js->new_target, 352 - .awaited_promise = js_mkundef(), 353 - .result = js_mkundef(), 354 - .async_func = callee_func, 355 - .args = NULL, 356 - .nargs = argc, 357 - .active_parent = NULL, 358 - .is_settled = false, 359 - .is_error = false, 360 - .is_done = false, 361 - .resume_point = 0, 362 - .yield_value = js_mkundef(), 363 - .async_promise = promise, 364 - .next = NULL, 365 - .mco = NULL, 366 - .mco_started = false, 367 - .is_ready = false, 368 - .did_suspend = false, 369 - .sv_vm = async_vm, 370 - }; 371 - 372 - coro->active_parent = js->active_async_coro; 373 - js->active_async_coro = coro; 487 + coroutine_t pending_coro; 488 + sv_async_init_activation( 489 + &pending_coro, js, caller_vm, promise, this_val, 490 + super_val, js->new_target, callee_func, argc 491 + ); 492 + sv_async_link_activation(js, &pending_coro); 374 493 375 494 ant_value_t result = sv_execute_closure_entry( 376 - async_vm, closure, callee_func, 495 + caller_vm, closure, callee_func, 377 496 super_val, this_val, args, argc, NULL 378 497 ); 379 - 380 - if (async_vm->suspended) { 381 - js->active_async_coro = coro->active_parent; 382 - coro->active_parent = NULL; 383 - enqueue_coroutine(coro); 498 + 499 + sv_async_unlink_activation(js, &pending_coro); 500 + 501 + if (pending_coro.materialized && pending_coro.sv_vm) { 384 502 GC_ROOT_RESTORE(js, root_mark); 385 503 return promise; 386 504 } ··· 389 507 ant_value_t reject_value = js->thrown_exists ? js->thrown_value : result; 390 508 js->thrown_exists = false; 391 509 js->thrown_value = js_mkundef(); 392 - js->active_async_coro = coro->active_parent; 393 - coro->active_parent = NULL; 394 510 js_reject_promise(js, promise, reject_value); 395 511 } else { 396 - js->active_async_coro = coro->active_parent; 397 - coro->active_parent = NULL; 398 512 js_resolve_promise(js, promise, result); 399 513 } 400 - 401 - free_coroutine(coro); 402 514 GC_ROOT_RESTORE(js, root_mark); 403 515 404 516 return promise; ··· 502 614 return promise; 503 615 } 504 616 505 - static inline ant_value_t sv_await_value(ant_t *js, ant_value_t value) { 617 + static inline ant_value_t sv_await_value(sv_vm_t *vm, ant_t *js, ant_value_t value) { 506 618 value = js_promise_assimilate_awaitable(js, value); 507 619 if (is_err(value)) return value; 508 620 if (vtype(value) != T_PROMISE) return value; ··· 519 631 if (!coro) 520 632 return js_mkerr(js, "await can only be used inside async functions"); 521 633 634 + sv_vm_t *prepared_vm = NULL; 635 + coroutine_t *pending_coro = coro; 636 + if (!current_mco && vm && coro->owner_vm == vm && !coro->sv_vm) { 637 + prepared_vm = sv_async_prepare_materialization(vm, js, coro); 638 + if (!prepared_vm) return js_mkerr(js, "out of memory for async VM"); 639 + coroutine_t *materialized_coro = sv_async_create_materialized_coro(prepared_vm, coro); 640 + if (!materialized_coro) return js_mkerr(js, "out of memory for coroutine"); 641 + coro = materialized_coro; 642 + enqueue_coroutine(coro); 643 + } 644 + 522 645 coro->awaited_promise = value; 523 646 coro->is_settled = false; 524 647 coro->is_ready = false; 525 648 js_await_result_t await_result = js_promise_await_coroutine(js, value, coro); 526 649 527 650 if (await_result.state == JS_AWAIT_ERROR) { 651 + if (prepared_vm) free_coroutine(coro); 528 652 coro->is_settled = false; 529 653 coro->awaited_promise = js_mkundef(); 530 654 return js_throw(js, await_result.value); ··· 532 656 533 657 coro->did_suspend = true; 534 658 if (!current_mco) { 659 + if (prepared_vm) sv_async_finalize_materialization(vm, prepared_vm, pending_coro, coro); 535 660 if (coro->sv_vm) coro->sv_vm->suspended = true; 536 661 return js_mkundef(); 537 662 }
+2 -2
src/silver/ops/iteration.h
··· 367 367 ant_value_t result = sv_vm_call(vm, js, next_method, iterator, NULL, 0, NULL, false); 368 368 if (is_err(result)) return result; 369 369 if (vtype(result) == T_PROMISE) { 370 - ant_value_t awaited = sv_await_value(js, result); 370 + ant_value_t awaited = sv_await_value(vm, js, result); 371 371 if (is_err(awaited)) return awaited; 372 372 result = awaited; 373 373 } ··· 377 377 if (is_err(done)) return done; 378 378 if (is_err(value)) return value; 379 379 if (vtype(value) == T_PROMISE) { 380 - ant_value_t awaited_val = sv_await_value(js, value); 380 + ant_value_t awaited_val = sv_await_value(vm, js, value); 381 381 if (is_err(awaited_val)) return awaited_val; 382 382 value = awaited_val; 383 383 }
+2
src/sugar.c
··· 112 112 } 113 113 114 114 coro->js = NULL; 115 + coro->owner_vm = NULL; 115 116 coro->active_parent = NULL; 117 + coro->materialized = false; 116 118 } 117 119 118 120 void reap_retired_coroutines(void) {