Skip to content

Fix Asyncify.handleAsync conflict with PROXY_SYNC_ASYNC#26513

Open
thiblahute wants to merge 1 commit intoemscripten-core:mainfrom
thiblahute:ppoll_proxy
Open

Fix Asyncify.handleAsync conflict with PROXY_SYNC_ASYNC#26513
thiblahute wants to merge 1 commit intoemscripten-core:mainfrom
thiblahute:ppoll_proxy

Conversation

@thiblahute
Copy link
Contributor

@thiblahute thiblahute commented Mar 21, 2026

When in a proxied context, skip the Asyncify unwind and call
startAsync() directly, letting the proxy mechanism handle the
async return.

This already affects __syscall_poll and would also affect the new
__syscall_ppoll.

Copy link
Collaborator

@sbc100 sbc100 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you see I landed support for ppoll and pselect yesterday in #26482. Hopefully this change is not needed after that?

@thiblahute
Copy link
Contributor Author

thiblahute commented Mar 21, 2026

Thanks! Yes, I just saw #26482 landed ppoll support - I've rebased and dropped the ppoll commit. The remaining change here fixes a pre-existing bug in Asyncify.handleAsync when used with PROXY_SYNC_ASYNC: the test confirms it reproduces with the upstream ppoll implementation too (without the fix, you get rtn.then is not a function because handleAsync does an Asyncify unwind instead of returning a Promise in the proxied context).

@thiblahute thiblahute changed the title Implement ppoll() in JS and fix Asyncify/PROXY_SYNC_ASYNC conflict Fix Asyncify.handleAsync conflict with PROXY_SYNC_ASYNC Mar 21, 2026
(end.tv_nsec - begin.tv_nsec) / 1000000;
assert(elapsed_ms >= 195);

printf("done\n");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need a new test here or can we just re-used the existing test test_poll_blocking_asyncify.c but with -sPROXY_TO_PTHREAD?

If there are things we are not covering in the existing test maybe better to add to that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried reusing test_poll_blocking_asyncify.c with -sPROXY_TO_PTHREAD but it hangs because it uses emscripten_set_timeout to schedule a write, which doesn't work when main runs on a worker thread.

The pthread variant needs actual cross thread writes to exercise the proxied poll path, so I think a separate test file is needed here. Or do you have a better idea?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

emscripten_set_timeout doesn't work when run from a pthread?

Is that because the pthread being blocked during poll rather than unwinding to the even loop? I guess the SYNC_ASYNC proxying more is taking precidence of asyncify in this case and poll is not causing and unwind?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is what I understand yes.

@thiblahute thiblahute force-pushed the ppoll_proxy branch 6 times, most recently from 5089fe8 to 4c3d00b Compare March 23, 2026 15:22
return NULL;
}

int main(void) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code looks a lot like the existing test_unblock_poll + write_after_sleep code in test_poll_blocking.c.

Would it be enough to run test_poll_blocking.c in PROXY_TO_PTHREAD+ASYNCIFY mode? (rather than add this new test code)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, I could reproduce the exact error using that test, I removed the file and added a test in the testsuite reusing this one, you first intuition was right, sorry for the noise.

@thiblahute thiblahute force-pushed the ppoll_proxy branch 2 times, most recently from bd9180c to c4ceddb Compare March 23, 2026 16:19
When a JS library function has both __proxy:'sync' and __async:'auto',
the compiler generates an Asyncify.handleAsync wrapper.  When called
from the PROXY_SYNC_ASYNC path on the main thread, handleAsync
triggers an Asyncify unwind instead of returning a Promise, causing
"rtn.then is not a function" in the proxy infrastructure.

Fix by generating a PThread.currentProxiedOperationCallerThread check
in handleAsyncFunction (jsifier.mjs): when in a proxied context, call
the inner function directly and skip the Asyncify unwind, letting the
proxy mechanism handle the async return.
// 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';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can / should this also be true for ASYNCIFY == 2? I don't see any reason to call handleAsync in that case either.

@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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should still test both versions of ASYNCIFY here.

# skipping).
self.set_setting('ASYNCIFY')
self.do_runf('core/test_poll_blocking.c', 'done\n',
cflags=['-sPROXY_TO_PTHREAD', '-sEXIT_RUNTIME'])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can put this all one one line (we allow longer-than-normal lines here in the test code).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants