From 86c66e98667250f9f7a6a0116fae65287f33d153 Mon Sep 17 00:00:00 2001 From: Sam Clegg Date: Tue, 3 Jun 2025 15:59:34 -0700 Subject: [PATCH] Add emscripten_queue_microtask This change adds a new `emscripten_queue_microtask` API to match the long standing web API (also available in node). See https://developer.mozilla.org/en-US/docs/Web/API/Window/queueMicrotask For now, I'm adding this without a polyfill so usage of the API will crash on very old targets. We could potentially polyfill using something like setTimeout, but the semantics are a little different so I'm not sure it worth doing. Split out from #24481 --- ChangeLog.md | 2 ++ src/lib/libeventloop.js | 9 +++++++++ src/lib/libsigs.js | 1 + system/include/emscripten/eventloop.h | 2 ++ test/other/test_queue_microtask.c | 26 ++++++++++++++++++++++++++ test/other/test_queue_microtask.out | 3 +++ test/test_other.py | 3 +++ 7 files changed, 46 insertions(+) create mode 100644 test/other/test_queue_microtask.c create mode 100644 test/other/test_queue_microtask.out diff --git a/ChangeLog.md b/ChangeLog.md index ff983710057b5..c56595bfcc453 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -20,6 +20,8 @@ See docs/process.md for more on how version tagging works. 4.0.10 (in development) ---------------------- +- New `emscripten_queue_microtask` API was added to reflect the `queueMicrotask` + Web API. (#24483) - Emscripten ports now install pkg-config `.pc` files so they will show up, for example, when you run `pkg-config --list-all` or `pkg-config --cflags `. Bare in mind that the correct PKG_CONFIG_PATH needs to be set for diff --git a/src/lib/libeventloop.js b/src/lib/libeventloop.js index 79a24520cb17b..f9a4d099973f9 100644 --- a/src/lib/libeventloop.js +++ b/src/lib/libeventloop.js @@ -85,6 +85,15 @@ LibraryJSEventLoop = { $emClearImmediate_deps: ['$emSetImmediate'], $emClearImmediate: undefined, + emscripten_queue_microtask__deps: ['$emSetImmediate', '$callUserCallback'], + emscripten_queue_microtask: (cb, userData) => { + {{{ runtimeKeepalivePush(); }}} + queueMicrotask(() => { + {{{ runtimeKeepalivePop(); }}} + callUserCallback(() => {{{ makeDynCall('vp', 'cb') }}}(userData)); + }); + }, + emscripten_set_immediate__deps: ['$emSetImmediate', '$callUserCallback'], emscripten_set_immediate: (cb, userData) => { {{{ runtimeKeepalivePush(); }}} diff --git a/src/lib/libsigs.js b/src/lib/libsigs.js index 2e1b8546b3e41..5ee43b933413c 100644 --- a/src/lib/libsigs.js +++ b/src/lib/libsigs.js @@ -733,6 +733,7 @@ sigs = { emscripten_promise_race__sig: 'ppp', emscripten_promise_resolve__sig: 'vpip', emscripten_promise_then__sig: 'ppppp', + emscripten_queue_microtask__sig: 'ipp', emscripten_random__sig: 'f', emscripten_request_animation_frame__sig: 'ipp', emscripten_request_animation_frame_loop__sig: 'vpp', diff --git a/system/include/emscripten/eventloop.h b/system/include/emscripten/eventloop.h index 4f706f9acf63b..401f09de8e82f 100644 --- a/system/include/emscripten/eventloop.h +++ b/system/include/emscripten/eventloop.h @@ -26,6 +26,8 @@ void emscripten_set_immediate_loop(bool (*cb)(void *user_data), void *user_data) int emscripten_set_interval(void (*cb)(void *user_data) __attribute__((nonnull)), double interval_ms, void *user_data); void emscripten_clear_interval(int id); +int emscripten_queue_microtask(void (*cb)(void *user_data) __attribute__((nonnull)), void *user_data); + void emscripten_runtime_keepalive_push(void); void emscripten_runtime_keepalive_pop(void); bool emscripten_runtime_keepalive_check(void); diff --git a/test/other/test_queue_microtask.c b/test/other/test_queue_microtask.c new file mode 100644 index 0000000000000..9323fa38798b1 --- /dev/null +++ b/test/other/test_queue_microtask.c @@ -0,0 +1,26 @@ +#include +#include +#include +#include + +bool got_timeout = false; +bool got_microtask = false; + +void callback_timeout(void* user_data) { + assert(got_microtask); + printf("callback_timeout: %ld\n", (intptr_t)user_data); + got_timeout = true; +} + +void callback_microtask(void* user_data) { + assert(!got_timeout); + printf("callback_microtask: %ld\n", (intptr_t)user_data); + got_microtask = true; +} + +int main() { + emscripten_set_timeout(callback_timeout, 0, (void*)42); + emscripten_queue_microtask(callback_microtask, (void*)43); + printf("done main\n"); + return 0; +} diff --git a/test/other/test_queue_microtask.out b/test/other/test_queue_microtask.out new file mode 100644 index 0000000000000..d2d78003a0919 --- /dev/null +++ b/test/other/test_queue_microtask.out @@ -0,0 +1,3 @@ +done main +callback_microtask: 43 +callback_timeout: 42 diff --git a/test/test_other.py b/test/test_other.py index 5e1168c96f224..ec6f5a088fafe 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -16186,3 +16186,6 @@ def test_unsupported_min_version_when_unsupported_env(self, env): self.assertContainedIf(f'var MIN_CHROME_VERSION = {unsupported};', src, env == 'node') self.assertContainedIf(f'var MIN_SAFARI_VERSION = {unsupported};', src, env == 'node') self.assertContainedIf(f'var MIN_FIREFOX_VERSION = {unsupported};', src, env == 'node') + + def test_queue_microtask(self): + self.do_other_test('test_queue_microtask.c')