diff --git a/packages/runtime/src/scr_async.c b/packages/runtime/src/scr_async.c index 752854fdf..bf58cdd36 100644 --- a/packages/runtime/src/scr_async.c +++ b/packages/runtime/src/scr_async.c @@ -1664,6 +1664,54 @@ static void scr_switch(ScrCtx *from, ScrCtx *to, ScrFiber *to_fiber) { #endif } +#ifdef SCR_NODE_ISLANDS +/* A nested entry into Node (scr_node_island.c) runs on Node's stack in a + * context of its own while the fiber that made the call stays suspended in + * it. Runtime code that Node calls back there runs as the loop's code does: + * no current fiber, the main exception cell and ALS slot. It may resume + * other fibers, which saves the resumer's context in scr_loop_ctx (and, + * under ASan, its fake stack in the main slot); those belong to whoever + * resumed the calling fiber, so they are saved here and put back when the + * nested entry returns, or the calling fiber's next park would return to a + * context that no longer exists. */ +typedef struct { + ScrFiber *current; + ScrAlsCtx **als; + ScrCtx loop_ctx; +#ifdef SCR_ASAN_FIBERS + void *main_fake; +#endif +} ScrNestedSave; + +void *scr_loop_nested_enter(void) { + ScrNestedSave *s = malloc(sizeof *s); + if (s == NULL) scr_oom(); + s->current = scr_current; + s->als = scr_als_active; + s->loop_ctx = scr_loop_ctx; +#ifdef SCR_ASAN_FIBERS + s->main_fake = scr_main_fake_stack; + scr_main_fake_stack = NULL; +#endif + scr_current = NULL; + scr_exc_swap_cell(NULL); + scr_als_active = &scr_als_main_slot; + return s; +} + +void scr_loop_nested_leave(void *save) { + ScrNestedSave *s = save; + scr_loop_ctx = s->loop_ctx; +#ifdef SCR_ASAN_FIBERS + scr_main_fake_stack = s->main_fake; +#endif + scr_current = s->current; + scr_exc_swap_cell(s->current ? &s->current->exc : NULL); + scr_als_active = s->als; + free(s); +} +#endif + static void scr_promise_settle_wake(ScrPromise *p); /* Copies src's settlement into a still-pending dst (payload RETAINED — diff --git a/packages/runtime/src/scr_node_island.c b/packages/runtime/src/scr_node_island.c index 5c4a9b3a3..1effa3e0a 100644 --- a/packages/runtime/src/scr_node_island.c +++ b/packages/runtime/src/scr_node_island.c @@ -332,6 +332,7 @@ static void ni_on_node_stack_below_mark(void (*job)(void *), void *arg) { ni_nested_return = &caller; uintptr_t guard = scr_stack_guard; SCR_STACK_GUARD_SET(scr_stack_guard_for((uintptr_t)lo, hi - (uintptr_t)lo)); + void *loop_save = scr_loop_nested_enter(); #ifdef NI_ASAN void *caller_fake = NULL; __sanitizer_start_switch_fiber(&caller_fake, lo, hi - (uintptr_t)lo); @@ -342,6 +343,7 @@ static void ni_on_node_stack_below_mark(void (*job)(void *), void *arg) { size_t size; __sanitizer_finish_switch_fiber(caller_fake, &bottom, &size); #endif + scr_loop_nested_leave(loop_save); SCR_STACK_GUARD_SET(guard); } diff --git a/packages/runtime/src/scr_node_island.h b/packages/runtime/src/scr_node_island.h index 2029a5c16..b477ec3b9 100644 --- a/packages/runtime/src/scr_node_island.h +++ b/packages/runtime/src/scr_node_island.h @@ -39,6 +39,11 @@ void scr_loop_host(const struct ScrNodeBridge *bridge, double since_ms); * fiber; 0: none), and the test for Node's stack it uses. */ extern uintptr_t scr_node_stack_low; extern bool (*scr_node_stack_has)(uintptr_t sp); +/* Around a nested entry: the runtime's scheduling state of the suspended + * calling fiber, saved and put back (the SCR_NODE_ISLANDS build of + * scr_async.c). */ +void *scr_loop_nested_enter(void); +void scr_loop_nested_leave(void *save); bool scr_loop_is_hosted(void); void scr_loop_hosted_run(uint32_t kind, uint32_t token); void scr_loop_hosted_io_state(struct ScrNodeIoState *state);