-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Add basic support for Fibers with JSPI #25111
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -8404,12 +8404,25 @@ def test_async_ccall_promise(self, exit_runtime): | |||||||||||||||||||||||||||||||||||
| self.cflags += ['--pre-js', 'pre.js', '-sINCOMING_MODULE_JS_API=onRuntimeInitialized'] | ||||||||||||||||||||||||||||||||||||
| self.do_runf('main.c', 'stringf: first\nsecond\n6.4') | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| @no_esm_integration('WASM_ESM_INTEGRATION is not compatible with ASYNCIFY=1') | ||||||||||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We probably still want this?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought Lines 212 to 228 in 3bad024
|
||||||||||||||||||||||||||||||||||||
| def test_fibers_asyncify(self): | ||||||||||||||||||||||||||||||||||||
| self.set_setting('ASYNCIFY') | ||||||||||||||||||||||||||||||||||||
| @with_asyncify_and_jspi | ||||||||||||||||||||||||||||||||||||
| def test_fibers(self): | ||||||||||||||||||||||||||||||||||||
| self.maybe_closure() | ||||||||||||||||||||||||||||||||||||
| self.do_runf('test_fibers.cpp', '*leaf-0-100-1-101-1-102-2-103-3-104-5-105-8-106-13-107-21-108-34-109-*') | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| # Test that fibers do not break setjmp/longjmp, ensuring longjmp works after | ||||||||||||||||||||||||||||||||||||
| # switching back from another fiber | ||||||||||||||||||||||||||||||||||||
| @parameterized({ | ||||||||||||||||||||||||||||||||||||
| '': (0,), | ||||||||||||||||||||||||||||||||||||
| 'legacy': (1,), | ||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||
| def test_fibers_setjmp(self, legacy): | ||||||||||||||||||||||||||||||||||||
| self.maybe_closure() | ||||||||||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add a brief comment explaining what the test is doing. |
||||||||||||||||||||||||||||||||||||
| self.set_setting('ASYNCIFY', 2) | ||||||||||||||||||||||||||||||||||||
| self.require_jspi() | ||||||||||||||||||||||||||||||||||||
| self.set_setting('SUPPORT_LONGJMP', 'wasm') | ||||||||||||||||||||||||||||||||||||
| self.set_setting('WASM_LEGACY_EXCEPTIONS', legacy) | ||||||||||||||||||||||||||||||||||||
| self.do_runf('test_fibers_setjmp.cpp', 'main: prejump\nf1: start arg:50\nmain: continuing1\nf1: cont1\nf2: start arg:60\nf1: cont2\nf2: cont1\nf2: exc 11\nmain: continuing2\nf2: cont2\nf1: cont3\nmain: continuing3\nmain: got exc 1\n') | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| @with_asyncify_and_jspi | ||||||||||||||||||||||||||||||||||||
| def test_asyncify_unused(self): | ||||||||||||||||||||||||||||||||||||
| # test a program not using asyncify, but the pref is set | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| // Copyright 2025 The Emscripten Authors. All rights reserved. | ||
| // Emscripten is available under two separate licenses, the MIT license and the | ||
| // University of Illinois/NCSA Open Source License. Both these licenses can be | ||
| // found in the LICENSE file. | ||
|
|
||
| #include <stdio.h> | ||
| #include <stdlib.h> | ||
| #include <setjmp.h> | ||
| #include <emscripten/fiber.h> | ||
|
|
||
| struct Fiber { | ||
| emscripten_fiber_t context; | ||
| char asyncify_stack[1024]; | ||
| alignas(16) char c_stack[4096]; | ||
|
|
||
| void init_current() { | ||
| emscripten_fiber_init_from_current_context(&context, asyncify_stack, sizeof(asyncify_stack)); | ||
| } | ||
|
|
||
| void init(em_arg_callback_func entry, void *arg) { | ||
| emscripten_fiber_init(&context, entry, arg, c_stack, sizeof(c_stack), asyncify_stack, sizeof(asyncify_stack)); | ||
| } | ||
|
|
||
| void swap(Fiber *fiber) { | ||
| emscripten_fiber_swap(&context, &fiber->context); | ||
| } | ||
| }; | ||
|
|
||
| static struct Globals { | ||
| Fiber main; | ||
| Fiber f1; | ||
| Fiber f2; | ||
|
|
||
| jmp_buf buffer; | ||
| jmp_buf buffer2; | ||
| } G; | ||
|
|
||
| [[noreturn]] | ||
| static void f1(void *arg) { | ||
| printf("f1: start arg:%d\n", *(int*)arg); | ||
|
|
||
| G.f1.swap(&G.main); | ||
|
|
||
| printf("f1: cont1\n"); | ||
|
|
||
| G.f1.swap(&G.f2); | ||
|
|
||
| printf("f1: cont2\n"); | ||
|
|
||
| G.f1.swap(&G.f2); | ||
|
|
||
| printf("f1: cont3\n"); | ||
|
|
||
| G.f1.swap(&G.main); | ||
| abort(); | ||
| } | ||
|
|
||
| [[noreturn]] | ||
| static void f2(void *arg) { | ||
| printf("f2: start arg:%d\n", *(int*)arg); | ||
|
|
||
| int exc; | ||
| if ((exc = setjmp(G.buffer2)) == 0) { | ||
| G.f2.swap(&G.f1); | ||
|
|
||
| printf("f2: cont1\n"); | ||
|
|
||
| longjmp(G.buffer2, 11); | ||
|
|
||
| printf("f2: not visible\n"); | ||
| } else { | ||
| printf("f2: exc %d\n", exc); | ||
| } | ||
|
|
||
| G.f2.swap(&G.main); | ||
|
|
||
| printf("f2: cont2\n"); | ||
|
|
||
| G.f2.swap(&G.f1); | ||
| abort(); | ||
| } | ||
|
|
||
| int main(int argc, char **argv) { | ||
| G.main.init_current(); | ||
|
|
||
| int test = 50; | ||
| G.f1.init(f1, &test); | ||
|
|
||
| int test2 = 60; | ||
| G.f2.init(f2, &test2); | ||
|
|
||
| int exc; | ||
| if ((exc = setjmp(G.buffer)) == 0) { | ||
| printf("main: prejump\n"); | ||
|
|
||
| G.main.swap(&G.f1); | ||
|
|
||
| printf("main: continuing1\n"); | ||
|
|
||
| G.main.swap(&G.f1); | ||
|
|
||
| printf("main: continuing2\n"); | ||
|
|
||
| G.main.swap(&G.f2); | ||
|
|
||
| printf("main: continuing3\n"); | ||
|
|
||
| longjmp(G.buffer, 1); | ||
|
|
||
| printf("main: not visible\n"); | ||
|
|
||
| } else { | ||
| printf("main: got exc %d\n", exc); | ||
| } | ||
|
|
||
| return 0; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like we add to this but never remove anything.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you mean nullifying the old continuation references [commited] or was it about the fact that ids only grow but are never reclaimed? Ids work just like
rewindIdand I don't seecallStackIdToFuncbeing cleared either.. (not that it's perfect)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant nullyfing. The overflow is a separate issue, but seeing at it goes 2^53 it's probably fine.