Skip to content

Conversation

@inolen
Copy link
Collaborator

@inolen inolen commented Dec 18, 2025

Hey, this isn't actually ready for merge - I just wanted to get a discussion going to figure out the correct way to implement this.

The quake3 engine uses select() with a timeout to throttle the server frame rate, while still allowing it to process network messages between frames. Using emscripten_set_main_loop doesn't work great here, as the server a.) is limited to handling networking at frame boundaries and b.) the timing isn't very consistent.

This PR doesn't contain an actual async poll implementation for the WebSocket backend - I added that on my wip WebTransport backend, which I'll post a separate PR for soon. However, the problem is still how to correctly handle making the syscall (and then everything else down the callstack that does async work) conditionally async. Sprinkling around #if ASYNCIFY and __async didn't feel great, but it did work.

@sbc100
Copy link
Collaborator

sbc100 commented Dec 18, 2025

Nice idea! Its interesting that we just added potential for select to block in #25523, but I assume you two are working independently?

I have some ideas knocking around about how I think this stuff could be made to be blocking also when called from threads: #25970

I'd also like to refactor such that this version of select can be deleted and we can implement just poll instead (because select can be implemented in native code in terms of poll).

But I like this idea!

@inolen
Copy link
Collaborator Author

inolen commented Dec 19, 2025

Does the preprocessor support some way to conditionally add the async keyword in front of a function? For example, could I do like -

{{{ ASYNC }}} () => {
...
}

and also then

{{{ AWAIT }}} poll();

Where these are empty when ASYNCIFY is disabled?

That would at least make the #if ASYNCIFY stuff a bit nicer, but it wouldn't ensure that the __async decorator is set still.

@sbc100
Copy link
Collaborator

sbc100 commented Dec 19, 2025

Yes, we already have something like that. See {{{ asyncIf(WASM_ASYNC_COMPILATION) }}}

@inolen
Copy link
Collaborator Author

inolen commented Dec 19, 2025

Yes, we already have something like that. See {{{ asyncIf(WASM_ASYNC_COMPILATION) }}}

Is there anything also dealing with the decorator? I was trying to think if there was some way to generate a structure and abuse the spread operator to make that auto-magic.

The idea being to end up pasting something like -

...{
func__async: true,
func: async () => {
...
}
)

but doing that by pasting in something up front would require the rogue extra } at the end. Can the preprocessor take in a JS function body as an argument somehow?

@sbc100
Copy link
Collaborator

sbc100 commented Dec 19, 2025

See the existing uses of asyncIf for example, dlopen_js

@sbc100
Copy link
Collaborator

sbc100 commented Dec 19, 2025

I also happen to be working on select/poll and blocking myself right now. See #25985 which is a followup to #25523

@inolen
Copy link
Collaborator Author

inolen commented Dec 19, 2025

dlopen_js

Right, that's what I was trying to noodle to see if it was possible to improve.

#if ASYNCIFY
  _dlopen_js__async: true,
#endif
  _dlopen_js: {{{ asyncIf(ASYNCIFY == 2) }}} (handle) =>

That's effectively what I'm doing here, but I was trying to think if there was a better way to do that such that both the decorator and async prefix were emitted as a result.

For example, I wonder if it's possible to do like -

{{{
ASYNC_IF(ASYNCIFY == 2, myfunc, () => {
... my function body
})
}}}

and that emit out

...{
myfunc__async: true,
myfunc: async () => {}
}

@inolen
Copy link
Collaborator Author

inolen commented Dec 19, 2025

I also happen to be working on select/poll and blocking myself right now. See #25985 which is a followup to #25523

Sure, not trying to step on toes here, just trying to find a way to have a way for select() to end up being async (when single threaded).

@sbc100
Copy link
Collaborator

sbc100 commented Dec 19, 2025

I also happen to be working on select/poll and blocking myself right now. See #25985 which is a followup to #25523

Sure, not trying to step on toes here, just trying to find a way to have a way for select() to end up being async (when single threaded).

Absolutely, I think we would be great if we could have single approach that would work for both singlethread-JSPI/ASYNCIFY and proxied-from-background thread. Thats what #25970 is all about.

Any help towards that goal is much appreciated!

@sbc100
Copy link
Collaborator

sbc100 commented Dec 19, 2025

BTW, we don't have to actually mark a function as async for it to work to actually return a promise (i.e. work with JSPI). We can have non-async functions that return promises sometimes and non-promises other times. e.g. select and poll might return a promise when called on the main thread but be blocking when called on a background thread and return synchronously.

Marking a functions as async means it can never return synchronously and therefore is not useful when JSPI is disabled so we might have reasons to use the async tag even when we return promises.

@inolen
Copy link
Collaborator Author

inolen commented Dec 19, 2025

BTW, we don't have to actually mark a function as async for it to work to actually return a promise (i.e. work with JSPI). We can have non-async functions that return promises sometimes and non-promises other times. e.g. select and poll might return a promise when called on the main thread but be blocking when called on a background thread and return synchronously.

Marking a functions as async means it can never return synchronously and therefore is not useful when JSPI is disabled so we might have reasons to use the async tag even when we return promises.

Hmm true, we only need the async keyword to then use await (i think), but this is all just syntactic sugar at the end of the day.

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