Hi,
i'm trying to use an external C++ SDK in my javascript app. I wrote some C++ glue code and in general calling functions from my C++ code to JS and vice versa works flawlessly. However, i'm stuck with the following problem:
I'm calling an embinded SDK-function from my js-app. The SDK then does some work involving spawning a pthread. Finally, the thread invokes a callback function within my C++ glue code.
That works, but now i need to notify my js-app that the SDK-function finished, eventually passing it some data. However, my C++-callback still runs in the pthread-context, so i can't access emscripten::val::global or run functions via EM_JS to access an object on my js-app.
I stumbled over this post by @kripken , suggesting to call emscripten_run_script("postMessage('myMessage')"); from the thread and then implementing the onmessage-callback in the --pre-js file. But i could not figure out how to implement this. Simply adding self.onmessage = function(){...} disturbes the normal Module/Worker communication.
I guess i would need some kind of onmessage-proxy, filtering my own messages and passing the others to the Module? Or is there another way to communicate from a worker to the main thread?
Any help would be appreciated!
I think i found a solution. Does not seem to be super elegant, but hey, it works!
I found the following worker-callback in the Module:
worker.onmessage = function(e) {
[...]
else if (d.cmd === 'objectTransfer') {
PThread.receiveObjectTransfer(e.data);
}
Later PThread.receiveObjectTransfer is defined with an empty method body:
receiveObjectTransfer:function (data) {
}
I'm not sure what this method was originally designed for?
Can somebody shed some light on this?
I decided to overwrite the method in my --post-js file, hoping there will be no side effects in future versions of the emscripten-sdk:
//post-js.js
if(!ENVIRONMENT_IS_PTHREAD) {
PThread.receiveObjectTransfer = function (data) {
let event = new CustomEvent('worker_message', {detail : data.msg});
window.dispatchEvent(event);
};
}
Invoking it from the C++ side (running in a pthread) looks like the following:
//bridge.cpp
emscripten_run_script("postMessage({cmd: \"objectTransfer\", msg: \"foobar\"})");
As emscripten_run_script is using eval(), you can put arbitary additional data into the object, as long as cmd : "objectTransfer" is present.
Now i can use a regular EventListener somewhere in my main js-app to react to the event created:
//app.js
addEventListener('worker_message', function(e) {
console.log(e.detail);
});
I would hope there was a simpler way to do this kind of thing. What about the functions in include/emscripten/threading.h such as emscripten_sync_run_in_main_thread?
Or emscripten_async_run_in_main_runtime_thread
Oh that's genius hijacking the receiveObjectTransfer function.
The method is empty because it's body is in an OFFSCREENCANVAS_SUPPORT block:
https://github.com/emscripten-core/emscripten/blob/30eb07a16404e6075c8672f63e1d35e190a6f476/src/library_pthread.js#L224-L236
Of course that means that someone could ifdef out more OFFSCREENCANVAS_SUPPORT stuff, like the entire objectTransfer message handling... But lets hope that doesn't happen any time soon...
Personally I'm trying to run a JS function that triggers a C++ callback at intervals, that triggers DOM changes (changes being specified in a user defined lambda). I can run the callback in a thread, but I'm trying to figure out how to call a JS lambda (specified at run time) on the main thread from a C++ callback, and this might do the trick.
This issue has been automatically marked as stale because there has been no activity in the past year. It will be closed automatically if no further activity occurs in the next 7 days. Feel free to re-open at any time if this issue is still relevant.
@sbc100 indeed you guessed correctly, emscripten_sync_run_in_main_thread and emscripten_async_run_in_main_runtime_thread allow me to run JS code in the main UI thread from a pthread in the C/C++ side, and this allows me to interact with the DOM if needed...
But is it safe/future-proof to use these functions? (as they don't seem to be documented in the Emscripten's API doc)
I think they are as stable as any of our other APIs. We try to avoid changing them if we possibly can. My understanding is that these are not internal-only functions so you are free to use them. @juj might provide more certainty?
We should at least document these (and others in threading.h)
Documentation for similar things like MAIN_THREAD_EM_ASM can be found here, but I don't know if this is the right place for those other functions.
Great I didn't notice MAIN_THREAD_EM_ASM and MAIN_THREAD_ASYNC_EM_ASM but it is exactly what I need :)
Any example on emscripten_sync_run_in_main_thread ?
For e.g. (can also be used without arg, and in ASYNC version) :
MAIN_THREAD_SYNC_EM_ASM(
console.log(some_arg);
, some_arg);
Most helpful comment
Great I didn't notice
MAIN_THREAD_EM_ASMandMAIN_THREAD_ASYNC_EM_ASMbut it is exactly what I need :)