MAIN_THREAD_ASYNC_EM_ASM and pthread locks on main thread

21 views
Skip to first unread message

Phil Endecott

unread,
Aug 21, 2025, 10:50:01 AMAug 21
to emscripte...@googlegroups.com
Dear Experts,

I have code where I (1) use MAIN_THREAD_ASYNC_EM_ASM to
queue code to run on the main thread from a background
thread, and (2) protect some data structures from concurrent
access using pthread mutexes. I am compiling with -pthread.

I know that locking a mutex on the main thread involves
polling. My question is: can work that has been submitted by
MAIN_THREAD_ASYNC_EM_ASM run when this polling occurs?

It looks to me as if it does, which is surprising! But this is
hard to debug and I may have misinterpreted what is going
on.


I.e. (pseudo-code):

// Global:

int i; // Only access this on the main thread!
std::string s; // Only access this with mut locked!
std::mutex mut;

// Main thread, e.g. in WebGL drawing code, or an
// input event handler, etc:

i = 0;
mut.lock();
assert(i == 0); // Can this fail?
foo(s);
mut.unlock();

// Background (worker) thread:

void modify_i() { i++; }

MAIN_THREAD_ASYNC_EM_ASM({
Module.ccall('modify_i', null);
});


Thanks,

Phil.


Phil Endecott

unread,
Aug 28, 2025, 3:21:21 PM (11 days ago) Aug 28
to emscripte...@googlegroups.com
'Phil Endecott' via emscripten-discuss wrote:
> Dear Experts,
>
> I have code where I (1) use MAIN_THREAD_ASYNC_EM_ASM to
> queue code to run on the main thread from a background
> thread, and (2) protect some data structures from concurrent
> access using pthread mutexes. I am compiling with -pthread.
>
> I know that locking a mutex on the main thread involves
> polling. My question is: can work that has been submitted by
> MAIN_THREAD_ASYNC_EM_ASM run when this polling occurs?

I believe the answer is yes.

- MAIN_THREAD_ASYNC_EM_ASM eventually calls
emscripten_run_js_on_main_thread which queues the
work using emscripten_proxy_async().

- pthread_mutex_lock() eventually calls emscripten_futex_wait()
which calls emscripten_yield(), which calls
emscripten_main_thread_process_queued_calls(), which
calls emscripten_proxy_execute_queue().

This seems to be deliberate; there is a comment in
emscripten_futex_wait():

// We are performing a blocking loop here, so we must handle proxied
// events from pthreads, to avoid deadlocks.

Do others see that there is a potential problem here? Rather than
avoiding deadlocks, this can introduce them!

Consider (pseudo-code):

// main thread:

lock A
lock B
...
unlock B
unlock A


// other thread:

MAIN_THREAD_ASYNC_EM_ASM {
lock A
....
unlock A
}


When the main thread locks mutex B, it may invoke the work that
has been sent from the other thread - which tries to lock A, which
the main thread has already locked. This is undefined behaviour.
Various things could happen, none of them good.


So - is there some alternative to MAIN_THREAD_ASYNC_EM_ASM
that I should use, such that the work is only done when the main
thread is actually idle?


Thanks, Phil.


Phil Endecott

unread,
Aug 30, 2025, 11:34:03 AM (9 days ago) Aug 30
to emscripte...@googlegroups.com
It seems that the solution is to create a new emscripten::ProxyingQueue
and add my work to that.

I had previously discounted using that, because I thought that I would
need to arrange to call execute() myself somehow. But that is not the
case:

- All queues are run when their thread is idle; the web workers
postMessage/onMessage mechanism is used to make that happen.

- The "system queue" is additionally run from the yeild() function,
which is called by f/mutex_lock when it blocks.

- MAIN_THREAD_ASYNC_EM_ASM uses the "system queue".

I'm not convinced that having the user-facing
MAIN_THREAD_ASYNC_EM_ASM feature use this special queue
is the right thing to do; wouldn't it be better if it used its own
queue that only ran when the main thread is idle?

Anyway, I now have this working.


Regards, Phil.







Thomas Lively

unread,
Aug 30, 2025, 12:15:54 PM (9 days ago) Aug 30
to emscripte...@googlegroups.com, Sam Clegg
Hi Phil,

Glad you figured this out. Using your own proxying queue sounds exactly like what you need. It's a good question about whether MAIN_THREAD_ASYNC_EM_ASM and other user-facing features should use the system queue. Those features predate the current proxying queue API and when they were introduced the system queue was the only queue available. As you've noticed it can be unsafe to use the system queue for some kinds of work, so it might be reasonable to make MAIN_THREAD_ASYNC_EM_ASM and friends use a separate, dedicated proxying queue, but that would be an observable change.

+Sam Clegg, WDYT about changing to use a dedicated queue for those older APIs?

Thomas

--
You received this message because you are subscribed to the Google Groups "emscripten-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to emscripten-disc...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/emscripten-discuss/1756567907739%40dmwebmail.dmwebmail.chezphil.org.

Sam Clegg

unread,
Sep 2, 2025, 8:28:13 PM (6 days ago) Sep 2
to Thomas Lively, emscripte...@googlegroups.com
On Sat, Aug 30, 2025 at 9:15 AM Thomas Lively <tli...@google.com> wrote:
Hi Phil,

Glad you figured this out. Using your own proxying queue sounds exactly like what you need. It's a good question about whether MAIN_THREAD_ASYNC_EM_ASM and other user-facing features should use the system queue. Those features predate the current proxying queue API and when they were introduced the system queue was the only queue available. As you've noticed it can be unsafe to use the system queue for some kinds of work, so it might be reasonable to make MAIN_THREAD_ASYNC_EM_ASM and friends use a separate, dedicated proxying queue, but that would be an observable change.

+Sam Clegg, WDYT about changing to use a dedicated queue for those older APIs?

Yes that sounds reasonable.  As long as we don't use `MAIN_THREAD_ASYNC_EM_ASM` internally for "system" stuff.    I don't think we do because we try to avoid using EM_ASM internally in favor of JS library functions.

Do you want to open an issue for this and we can discuss further there? 

cheers,
sam
Reply all
Reply to author
Forward
0 new messages