Replies: 2 comments 1 reply
-
|
Good question. I'm not sure we have anything out-of-the box that does this. We do provide a lot of the pieces to build it yourself (i.e. proxying.h and promise.h), but maybe its something we could add more directly. @tlively WDYT? |
Beta Was this translation helpful? Give feedback.
-
|
Hi! Following up on this thread. I tried adopting The motivation is the same as the original question: I don’t want to run heavy C++ code on the main thread, and I also don’t want to use a separate WebWorker + RPC layer because that forces serialization of inputs and outputs. With pthreads + SharedArrayBuffer, the work can stay in-process with a shared Wasm heap. Below is a minimal repro that matches this approach: a single proxying queue + dedicated pthread, using Minimal repro (C++): // repro.cpp
#include <memory>
#include <mutex>
#include <pthread.h>
#include <emscripten/bind.h>
#include <emscripten/proxying.h>
#include <emscripten/threading.h>
namespace {
em_proxying_queue* queue = nullptr;
pthread_t worker{};
std::once_flag initOnce;
void* workerMain(void*) {
emscripten_exit_with_live_runtime();
return nullptr;
}
void initProxying() {
std::call_once(initOnce, [] {
queue = em_proxying_queue_create();
pthread_create(&worker, nullptr, workerMain, nullptr);
});
}
struct Job {
em_proxying_ctx* ctx{};
// Put whatever inputs/outputs you want here for the real code.
};
std::uintptr_t heavyAsync() {
initProxying();
auto job = std::make_unique<Job>();
Job* rawJob = job.release();
em_promise_t promise = emscripten_proxy_promise_with_ctx(
queue,
worker,
[](em_proxying_ctx* ctx, void* arg) {
auto job = std::unique_ptr<Job>{static_cast<Job*>(arg)};
job->ctx = ctx;
// 1) Heavy work would run here on the pthread.
// (omitted for minimal repro)
// 2) Resolve promise on the main runtime thread.
emscripten_proxy_async(
queue,
emscripten_main_runtime_thread_id(),
[](void* arg2) {
auto job2 = std::unique_ptr<Job>{static_cast<Job*>(arg2)};
emscripten_proxy_finish(job2->ctx);
},
job.release()
);
},
rawJob
);
// JS does: await Module.getPromise(promiseId)
return static_cast<std::uintptr_t>(promise);
}
} // namespace
EMSCRIPTEN_BINDINGS(repro) {
emscripten::function("heavyAsync", &heavyAsync);
}Build: emcc repro.cpp -O2 -pthread --bind \
-sMODULARIZE=1 -sEXPORT_NAME=Repro \
'-sEXPORTED_RUNTIME_METHODS=["getPromise"]' \
-o repro.jsJS usage: import Repro from "./repro.js";
const Module = await Repro({ locateFile: (p) => p });
const promiseId = Module.heavyAsync();
await Module.getPromise(promiseId);
console.log("done");Requires of course cross-origin isolation (COOP/COEP) so pthreads + Questions:
Thanks! |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Hi!
I'm looking for best practices to provide non-blocking calls to long running computations. Right now we instantiate the WASM module in a new web-worker and use message passing and promises to call functions. Which works, but we need some boilerplate code for this to work.
Does Emscripten support to always wrap function calls with a promise and execute them in a separate worker? Something like
PROXY_TO_PTHREADbut for functions? No reentrance and only a single worker with synchronous execution inside.Thanks and greetings from Berlin
Sven
Beta Was this translation helpful? Give feedback.
All reactions