Skip to content

Instantly share code, notes, and snippets.

@kripken
Last active July 13, 2021 07:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kripken/ab76b8365d0afb8b00fc5eb30879e5e3 to your computer and use it in GitHub Desktop.
Save kripken/ab76b8365d0afb8b00fc5eb30879e5e3 to your computer and use it in GitHub Desktop.
//
// Build with
//
// -pthread -s PROXY_TO_PTHREAD -s EXIT_RUNTIME
//
#include <stdlib.h>
#include <emscripten.h>
#include <emscripten/threading.h>
template <class F>
void emscripten_invoke_on_main_thread_async(F&& f) {
// We need a plain C function pointer for our call back from JS. Use a static
// class method for that, to avoid polluting the outer namespace.
struct Helper {
static void doCall(void* ptr) {
F* f = (F*)ptr;
(*f)();
}
};
// TODO: Lifetime of f! We want to move it to where it remains alive until
// later (despite this function scope exiting), and for us to free it
// manually after we call it. Is there some clever C++ way..?
MAIN_THREAD_ASYNC_EM_ASM({
var doCall = $0;
var ptr = $1;
// Add an extra indirection to force us to run on the main event loop, that
// is, avoid anything else being on the stack when we run.
setTimeout(function() {
wasmTable.get(doCall)(ptr);
}, 0);
}, Helper::doCall, &f);
}
int main() {
struct Foo {
void operator()() {
// Print whether we are on the main thread.
auto mainThread = EM_ASM_INT({
var mainThread = typeof importScripts === 'undefined';
console.log("hello. mainThread=", mainThread);
return mainThread;
});
// If we are on the main thread, it is time to end this test.
if (mainThread) exit(0);
}
};
// Call it on this thread.
Foo()();
// Call it on the main thread.
emscripten_invoke_on_main_thread_async(Foo());
emscripten_exit_with_live_runtime();
}
hello. mainThread= false
hello. mainThread= true
@sean-parent
Copy link

sean-parent commented Jul 13, 2021

At line 35 you are passing a pointer to a function object without ownership. You need to move the object into a heap-allocated block. I believe the code below is correct.

#include <utility>

template <class F>
void emscripten_invoke_on_main_thread_async()(F&& f) const {
    using function_type = typename std::remove_reference<F>::type;

    emscripten_async_run_in_main_runtime_thread(
        EM_FUNC_SIG_VI,
        static_cast<void (*)(void*)>([](void* f_) {
            emscripten_async_call(
                [](void* f_) {
                    auto f = static_cast<function_type*>(f_);
                    (*f)();
                    delete f;
                },
                f_, 0);
        }),
        new function_type(std::forward<F>(f)));
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment