diff --git a/src/jsifier.mjs b/src/jsifier.mjs index fe71069110e28..084b8a45fe9a3 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 && 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 de3238532da5c..0a2c70ee1dd94 100644 --- a/test/test_core.py +++ b/test/test_core.py @@ -9652,6 +9652,15 @@ 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') + @with_asyncify_and_jspi + def test_poll_blocking_asyncify_pthread(self): + # require_pthreads can fail when require_jspi selects d8 (which doesn't + # support pthreads). Convert to skip since the test needs both. + if not any(engine_is_node(e) or engine_is_bun(e) for e in self.js_engines): + self.skipTest('no JS engine capable of running pthreads') + self.require_pthreads() + self.do_runf('core/test_poll_blocking.c', 'done\n', cflags=['-sPROXY_TO_PTHREAD', '-sEXIT_RUNTIME']) + @parameterized({ '': ([],), 'pthread': (['-pthread'],),