wip make select() async when using ASYNCIFY/JSPI#25981
wip make select() async when using ASYNCIFY/JSPI#25981inolen wants to merge 2 commits intoemscripten-core:mainfrom
Conversation
|
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! |
|
Does the preprocessor support some way to conditionally add the async keyword in front of a function? For example, could I do like - and also then 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. |
|
Yes, we already have something like that. See |
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 - 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? |
|
See the existing uses of |
Right, that's what I was trying to noodle to see if it was possible to improve. 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 - and that emit out |
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! |
|
BTW, we don't have to actually mark a function as Marking a functions as |
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. |
f91cf06 to
9e39f40
Compare
CLOCK_MONOTONIC instead of gettimeofday whose implementation isn't guaranteed to be monotonic
|
@sbc100 these changes were circling around in my head last night while checking out the updated code after reading some of the current issues / pr's regarding poll. I copied what I did on my WebTransport code over to the WebSocket backend here. The main changes -
|
src/lib/libsockfs.js
Outdated
| #endif | ||
| queued.data = new Uint8Array(queuedBuffer, queuedOffset + bytesRead, bytesRemaining); | ||
| sock.recv_queue.unshift(queued); | ||
| sock.recvQueue.unshift(queued); |
There was a problem hiding this comment.
Would you mind splitting out the renaming here, so this change becomes more focused?
There was a problem hiding this comment.
Or just skip it since snake seems to be used a fair amount in the FS layer in general?
There was a problem hiding this comment.
No problem skipping that, I just couldn't decide which variable to match up with since it's a mixed bag. I'll go with wake_queue.
| dbg('poll: timeout', timeout); | ||
| #endif | ||
| resolve(0); | ||
| }, timeout); |
There was a problem hiding this comment.
I'm not sure I love the timeout = 0x7fffffff; thing above.. and one downside is that it means we end up setting this timeout here even for the "infinite timeout" case.
i.e. i think it would be nice to skip this timeout completely in that case.
Does timeout = 0x7fffffff; really make things easier in other places?
There was a problem hiding this comment.
Looking at the code, it doesn't matter now. I had added that while shifting the code around trying to get the async code into the single #ifdef / Promise constructor, but didn't pay attention to its value after the dust settled.
src/lib/libsyscall.js
Outdated
| return true; | ||
| } | ||
|
|
||
| return false; |
There was a problem hiding this comment.
Is this return value used somwhere?
What does it mean to return false here? Does it mean we got some kind of spurious wakeup? I.e. the wakeup was triggered with the wrong set of events? Could we not ban this? i.e. should the caller contract be that we only call the wake callback once you have one of the events we care about?
There was a problem hiding this comment.
Preface - this is ultimately just a dumbed down design stolen from the linux kernel's poll_wait design and I'm not married to the details. So yes returning false would mean it's a spurious wake up - the stream maybe woke it up due to a POLLIN but poll only cared about POLLOUT.
I think it's fine to make the stream op take in poll(stream, events, wake) and do as you said. Then there's no return value and the stream is always removing any wake callback who's event mask matches whatever event that's occurred.
There was a problem hiding this comment.
Also regarding the concern for streams not implementing the wake support - instead of passing events and wake, you could make that a callback that returns { events, wake }. If it's not called, no timeouts are ever set.
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 ASYNCIFYand__asyncdidn't feel great, but it did work.