From a5f02d1b176c6b98aa6a9731bd786876c75da11d Mon Sep 17 00:00:00 2001 From: Sam Clegg Date: Tue, 23 Jun 2026 17:21:17 -0700 Subject: [PATCH] Use Promise internal for handling run dependencies. NFC This is a logical followup to #27121, and comes with a codesize saving in many cases. Add `$resolveRunDependencies` to allow awaiting the resolution of all active run dependencies. This is useful for async runtime operations like `emscripten_async_load_script` which need to wait for loaded scripts to finish their own async setup (e.g. file preloading) before triggering their onload callbacks. Codesize impact: - test_codesize_file_preload: -19 bytes (JS) - test_codesize_hello_dylink_all: +40 bytes (JS) - All other tests (including C++ tests): 0 bytes change (fully DCE'd) --- src/lib/libbrowser.js | 9 ++----- src/lib/libcore.js | 26 +++++++++++-------- src/postamble.js | 4 +-- .../test_codesize_file_preload.expected.js | 23 +++++++++------- test/codesize/test_codesize_file_preload.json | 8 +++--- .../test_codesize_hello_dylink_all.json | 4 +-- 6 files changed, 38 insertions(+), 36 deletions(-) diff --git a/src/lib/libbrowser.js b/src/lib/libbrowser.js index fc5006c7ee2e8..73cd373ddca90 100644 --- a/src/lib/libbrowser.js +++ b/src/lib/libbrowser.js @@ -617,7 +617,7 @@ var LibraryBrowser = { }, // TODO: currently not callable from a pthread, but immediately calls onerror() if not on main thread. - emscripten_async_load_script__deps: ['$UTF8ToString', '$runDependencies', '$dependenciesFulfilled'], + emscripten_async_load_script__deps: ['$UTF8ToString', '$runDependencies', '$resolveRunDependencies'], emscripten_async_load_script: async (url, onload, onerror) => { url = UTF8ToString(url); #if PTHREADS @@ -635,12 +635,7 @@ var LibraryBrowser = { var loadDone = () => { {{{ runtimeKeepalivePop() }}} if (onload) { - var onloadCallback = () => callUserCallback({{{ makeDynCall('v', 'onload') }}}); - if (runDependencies > 0) { - dependenciesFulfilled = onloadCallback; - } else { - onloadCallback(); - } + resolveRunDependencies().then(() => callUserCallback({{{ makeDynCall('v', 'onload') }}})); } } diff --git a/src/lib/libcore.js b/src/lib/libcore.js index 3e14c02af08f9..c27396fb30e37 100644 --- a/src/lib/libcore.js +++ b/src/lib/libcore.js @@ -2268,10 +2268,13 @@ addToLibrary({ // it happens right before run - run will be postponed until // the dependencies are met. $runDependencies__internal: true, + $runDependencies__deps: ['$resolveRunDependencies'], $runDependencies: 0, - // overridden to take different actions when all run dependencies are fulfilled - $dependenciesFulfilled__internal: true, - $dependenciesFulfilled: null, + $dependenciesPromise__internal: true, + $dependenciesPromise: null, + $resolveRunDependencies__internal: true, + $resolveRunDependencies__deps: ['$dependenciesPromise'], + $resolveRunDependencies: async () => dependenciesPromise, #if ASSERTIONS $runDependencyTracking__internal: true, $runDependencyTracking: {}, @@ -2279,13 +2282,18 @@ addToLibrary({ $runDependencyWatcher: null, #endif - $addRunDependency__deps: ['$runDependencies', '$removeRunDependency', + $addRunDependency__deps: ['$runDependencies', '$removeRunDependency', '$dependenciesPromise', #if ASSERTIONS '$runDependencyTracking', '$runDependencyWatcher', #endif ], $addRunDependency: (id) => { + if (!runDependencies) { + var resolve; + dependenciesPromise = new Promise((r) => resolve = r); + dependenciesPromise.resolve = resolve; + } runDependencies++; #if expectToReceiveOnModule('monitorRunDependencies') @@ -2328,7 +2336,7 @@ addToLibrary({ #endif }, - $removeRunDependency__deps: ['$runDependencies', '$dependenciesFulfilled', + $removeRunDependency__deps: ['$runDependencies', '$dependenciesPromise', #if ASSERTIONS '$runDependencyTracking', '$runDependencyWatcher', @@ -2349,18 +2357,14 @@ addToLibrary({ assert(runDependencyTracking[id]); delete runDependencyTracking[id]; #endif - if (runDependencies == 0) { + if (!runDependencies) { #if ASSERTIONS if (runDependencyWatcher !== null) { clearInterval(runDependencyWatcher); runDependencyWatcher = null; } #endif - if (dependenciesFulfilled) { - var callback = dependenciesFulfilled; - dependenciesFulfilled = null; - callback(); // can add another dependenciesFulfilled - } + dependenciesPromise.resolve(); } }, #endif diff --git a/src/postamble.js b/src/postamble.js index 39cb9b4c8452e..1fa46c9b4e83c 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -134,11 +134,11 @@ function stackCheckInit() { preRun(); #if '$runDependencies' in addedLibraryItems - if (runDependencies > 0) { + if (runDependencies) { #if RUNTIME_DEBUG dbg('run: waiting on runDependencies'); #endif - await new Promise((resolve) => dependenciesFulfilled = resolve); + await resolveRunDependencies(); } #endif diff --git a/test/codesize/test_codesize_file_preload.expected.js b/test/codesize/test_codesize_file_preload.expected.js index e480e750f3c58..9edbcaffdedf2 100644 --- a/test/codesize/test_codesize_file_preload.expected.js +++ b/test/codesize/test_codesize_file_preload.expected.js @@ -1305,22 +1305,25 @@ var FS_createDataFile = (...args) => FS.createDataFile(...args); var getUniqueRunDependency = id => id; -var runDependencies = 0; +var dependenciesPromise = null; + +var resolveRunDependencies = async () => dependenciesPromise; -var dependenciesFulfilled = null; +var runDependencies = 0; var removeRunDependency = id => { runDependencies--; - if (runDependencies == 0) { - if (dependenciesFulfilled) { - var callback = dependenciesFulfilled; - dependenciesFulfilled = null; - callback(); - } + if (!runDependencies) { + dependenciesPromise.resolve(); } }; var addRunDependency = id => { + if (!runDependencies) { + var resolve; + dependenciesPromise = new Promise(r => resolve = r); + dependenciesPromise.resolve = resolve; + } runDependencies++; }; @@ -3180,8 +3183,8 @@ function callMain() { async function run() { preRun(); - if (runDependencies > 0) { - await new Promise(resolve => dependenciesFulfilled = resolve); + if (runDependencies) { + await resolveRunDependencies(); } if (ABORT) return; initRuntime(); diff --git a/test/codesize/test_codesize_file_preload.json b/test/codesize/test_codesize_file_preload.json index 26a8db46e27eb..069b0b034a61d 100644 --- a/test/codesize/test_codesize_file_preload.json +++ b/test/codesize/test_codesize_file_preload.json @@ -1,10 +1,10 @@ { - "a.out.js": 22157, - "a.out.js.gz": 9192, + "a.out.js": 22138, + "a.out.js.gz": 9190, "a.out.nodebug.wasm": 1666, "a.out.nodebug.wasm.gz": 945, - "total": 23823, - "total_gz": 10137, + "total": 23804, + "total_gz": 10135, "sent": [ "a (fd_write)" ], diff --git a/test/codesize/test_codesize_hello_dylink_all.json b/test/codesize/test_codesize_hello_dylink_all.json index e9ec5c275e006..ed50856c112d4 100644 --- a/test/codesize/test_codesize_hello_dylink_all.json +++ b/test/codesize/test_codesize_hello_dylink_all.json @@ -1,7 +1,7 @@ { - "a.out.js": 268405, + "a.out.js": 268445, "a.out.nodebug.wasm": 587520, - "total": 855925, + "total": 855965, "sent": [ "IMG_Init", "IMG_Load",