Repository navigation
Add emscripten_proxy_fd_with_ctx and 'fd' async variants for JS library functions - #27922
guybedford wants to merge 1 commit into
Conversation
tlively
left a comment
There was a problem hiding this comment.
It looks like this implementation works by proxying not just the library function, but also a wrapper fdFromPromise call that produces an FD that can be polled for the result of the promise. This makes sense because currently (without WasmFS support for general pollable FDs), FDs can only be created on the main thread, where the FS lives.
I don't think that's general enough, though. JS library functions proxied to the main thread are only one special case of proxied work that a thread might be waiting on. The advantage of using pollable fds is that a thread can synchronously wait on many pending tasks at once, but that only works if all the pending tasks can be represented as fds. If the JS library proxying were built on a lower-level emscripten_proxy_fd_with_ctx, then all tasks proxied via em_proxying_queue would be compatible with epoll, no matter what queue or thread they run on.
Without WasmFS support for pollable FDs, emscripten_proxy_fd_with_ctx targeting any thread other than the main thread will have to trampoline through the main thread to allocate an FD, then do a promise or callback proxy call from the main thread to the target thread to arrange for the FD to become ready when the work is done.
Once we have WasmFS support, though, emscripten_proxy_fd_with_ctx will be able to allocate an FD directly, then proxy directly to the target thread, which will be able to mark the FD ready without having to proxy anything back to the calling thread.
Completely agreed on this overall direction, and I think that sounds like a sensible refactoring later on, that has no public surface area at risk from this current approach for now. |
900ac5a to
32efe20
Compare
ed86481 to
0383e37
Compare
|
How about implementing |
0383e37 to
3a3edc2
Compare
32efe20 to
d84a291
Compare
3a3edc2 to
66b1aa4
Compare
|
I went ahead and added |
66b1aa4 to
3dadb9e
Compare
67698db to
3f4ecef
Compare
3dadb9e to
cf1c6a3
Compare
cf1c6a3 to
1659ecb
Compare
| // Settles the task's fd with the result, on the main thread where the fd | ||
| // lives, before emscripten_proxy_finish settles it as merely finished (which | ||
| // then finds it settled). | ||
| static void js_fd_done(proxied_js_func_t* f, em_proxying_ctx* ctx) { | ||
| assert(ctx->kind == FD); | ||
| _emscripten_proxy_fd_settle(ctx->fd.handle, (intptr_t)f->result, f->fulfilled); | ||
| } |
There was a problem hiding this comment.
Oh, I see. Because there's no equivalent of promise.then() for pollable fds, there's no way to chain on an extraction of the result from the arg the way you can with promises. Given that, I would support adding something like emscripten_proxy_finish_result for use with both promises (if feasible) and fds. It would be nice to do that in a separate PR, either before or after this one.
There was a problem hiding this comment.
Ok I've gone ahead and reinstated the emscripten_proxy_finish_result style approach in the base PR at #27880. Please take another look there further.
There was a problem hiding this comment.
Since it's still not clear if this one will land, I've instead moved the emscripten_proxy_finish_result back into this PR directly, so it doesn't affect the lower stack PR unnecessarily.
3f4ecef to
a249964
Compare
1659ecb to
5435bb5
Compare
a249964 to
a52a242
Compare
86f886b to
708a3fe
Compare
322e972 to
72b406f
Compare
708a3fe to
83e4772
Compare
77a780b to
07bca5f
Compare
83e4772 to
d3107ef
Compare
07bca5f to
9457279
Compare
d3107ef to
cfb0c36
Compare
9457279 to
5eaf9ea
Compare
…ry functions emscripten_proxy_fd_with_ctx(q, target, func, arg) proxies a task and returns a file descriptor readable once it is finished (POLLERR if it fails or is cancelled), so proxied work can be awaited with poll/select/epoll alongside other descriptors; read() yields the emscripten_proxy_finish_result value. Descriptors live on the main thread, which is the only supported target for now. foo_fd, the 'fd' async variant of a JS library function, is built on it: readable once the result is available (POLLERR if rejected); read() of an intptr_t takes the value, after which the fd is at EOF. On the main thread the body's value or Promise settles the fd directly.
cfb0c36 to
6ccf5e7
Compare
Adds the
_fdvariant alongside the_promiseand_callbackvariants of #27880 / #27954, so the result of an asynchronous JS library function can be awaited throughpoll/select/epollfrom any stack, alongside sockets and other descriptors. It is built on a new proxying primitive.emscripten_proxy_fd_with_ctx(q, target, func, arg)proxies a task and returns a file descriptor that becomes readable once the task callsemscripten_proxy_finish/emscripten_proxy_finish_result(readable withPOLLERRif it fails or is cancelled), so proxied work can be waited on with the usual descriptor APIs.read()of anintptr_tyields the result (EIOon failure) and the fd is then at EOF. It is a newFDcontext kind alongside sync and callback: finishing settles the fd directly on the target thread, with nothing proxied back. Descriptors live on the main thread, which is the only supported target for now, so the calling thread may be blocked polling the fd; with WasmFS pollable descriptors the same API will be able to target any thread.Since an fd has no
thenthrough which the task'sargcould hand the result over,proxying.halso gainsemscripten_proxy_finish_result(ctx, void*)(the fd is readable with it; the promise ofemscripten_proxy_promise_with_ctxis fulfilled with it, where it was alwaysNULL) andemscripten_proxy_fail(ctx)(the fd is readable withPOLLERR; the promise is rejected, or thecancelcallback ofemscripten_proxy_callback_with_ctxruns, as if the target thread had exited).foo_fd(), listed as__async: ['fd'], runs the same body (withsyncalways false). From a pthread it isemscripten_proxy_fd_with_ctxof the main-thread body, finished withemscripten_proxy_finish_result. On the main thread the body's value or Promise settles the fd directly, so a synchronous completion is readable on return.read()of anintptr_ttakes the value (0 for avoidfunction,EIOif the Promise rejected); the fd is then at EOF and must be closed. It is a link-time error under WASMFS, which has no readiness layer. As with the other variants, it is emitted only where used.Tests:
test_pthread_proxyingcoversemscripten_proxy_fd_with_ctx,emscripten_proxy_finish_resultandemscripten_proxy_fail;jslib.test_jslib_async_auto_variants_fdin plain, ASYNCIFY, JSPI andPROXY_TO_PTHREADbuilds, wasm32 and wasm64;test_jslib_async_auto_variants_fd_wasmfs.Made with AI assistance under my review