diff --git a/src/jsifier.mjs b/src/jsifier.mjs index dbe20035574e1..84fe9d01b129a 100644 --- a/src/jsifier.mjs +++ b/src/jsifier.mjs @@ -360,16 +360,29 @@ ${body}; }); } -function handleAsyncFunction(snippet, sig) { +function handleAsyncFunction(snippet, sig, proxyingMode) { const return64 = sig && (MEMORY64 && sig.startsWith('p') || sig.startsWith('j')) let handleAsync = 'Asyncify.handleAsync(innerFunc)' if (return64 && ASYNCIFY == 1) { handleAsync = makeReturn64(handleAsync); } + // When a function uses both __proxy:'sync' and __async:'auto', the proxy + // mechanism (PROXY_SYNC_ASYNC) handles the async return itself. In that + // case, skip the Asyncify unwind and call the inner function directly so + // the proxy can use .then() on the returned Promise. + const skipHandleAsync = PTHREADS && ASYNCIFY == 1 && proxyingMode === 'sync'; return modifyJSFunction(snippet, (args, body, async_, oneliner) => { if (!oneliner) { body = `{\n${body}\n}`; } + if (skipHandleAsync) { + return `\ +function(${args}) { + let innerFunc = async () => ${body}; + if (PThread.currentProxiedOperationCallerThread) return innerFunc(); + return ${handleAsync}; +}\n`; + } return `\ function(${args}) { let innerFunc = ${async_} () => ${body}; @@ -474,11 +487,11 @@ function(${args}) { compileTimeContext.i53ConversionDeps.forEach((d) => deps.push(d)); } + const proxyingMode = LibraryManager.library[symbol + '__proxy']; + if (ASYNCIFY && isAsyncFunction == 'auto') { - snippet = handleAsyncFunction(snippet, sig); + snippet = handleAsyncFunction(snippet, sig, proxyingMode); } - - const proxyingMode = LibraryManager.library[symbol + '__proxy']; if (proxyingMode) { if (!['sync', 'async', 'none'].includes(proxyingMode)) { error(`JS library error: invalid proxying mode '${symbol}__proxy: ${proxyingMode}' specified`); diff --git a/test/test_core.py b/test/test_core.py index 7f2766fb1464e..668aaa156380e 100644 --- a/test/test_core.py +++ b/test/test_core.py @@ -9645,6 +9645,18 @@ def test_poll_blocking_asyncify(self): self.skipTest('test requires setTimeout which is not supported under v8') self.do_runf('core/test_poll_blocking_asyncify.c', 'done\n') + @no_esm_integration('WASM_ESM_INTEGRATION is not compatible with ASYNCIFY=1') + @requires_pthreads + def test_poll_blocking_asyncify_pthread(self): + # Only testing ASYNCIFY=1: JSPI's handleAsync is a plain async function + # and doesn't have this bug. Also, with_asyncify_and_jspi can't be + # combined with requires_pthreads since require_jspi may select d8 which + # doesn't support pthreads (require_pthreads then hard-fails instead of + # skipping). + self.set_setting('ASYNCIFY') + self.do_runf('core/test_poll_blocking.c', 'done\n', + cflags=['-sPROXY_TO_PTHREAD', '-sEXIT_RUNTIME']) + @parameterized({ '': ([],), 'pthread': (['-pthread'],),