wasm: don’t deadlock on event processing

emscripten_async_run_in_main_runtime_thread_ schedules
an async call on the on the main thread. However, the
calls are ordered, also in respect to _synchronous_ calls
to the main thread (for example those made during file
write/flush).

Making a synchronous call from a secondary thread may
then cause Emscripten to service previously scheduled
async calls during the synchronous call. This can cause
a deadlock if:

- a secondary thread makes a sync call while holding a lock, and
- a previously scheduled async call attempt to acquire the same
  lock on the main thread.

(See https://github.com/emscripten-core/emscripten/issues/10155
for sample code)

Avoid this case by adding a second zero-timer async call;
this way Qt should process events when the main thread
becomes idle.

Change-Id: I221fe4e25bbb1a56627e63c3d1809e40ccefb030
Pick-to: 5.15
Reviewed-by: Lorn Potter <lorn.potter@gmail.com>
This commit is contained in:
Morten Johan Sørvig 2020-10-22 11:58:47 +02:00
parent f9e45287d8
commit 4035fdd820

View File

@ -195,9 +195,15 @@ void QWasmEventDispatcher::doMaintainTimers()
void QWasmEventDispatcher::wakeUp()
{
#ifdef EMSCRIPTEN_HAS_ASYNC_RUN_IN_MAIN_RUNTIME_THREAD
if (!emscripten_is_main_runtime_thread())
if (m_hasMainLoop)
emscripten_async_run_in_main_runtime_thread_(EM_FUNC_SIG_VI, (void*)(&QWasmEventDispatcher::mainThreadWakeUp), this);
if (!emscripten_is_main_runtime_thread() && m_hasMainLoop) {
// Make two-step async call to mainThreadWakeUp in order to make sure the
// call is made at a point where the main thread is idle.
void (*intermediate)(void *) = [](void *eventdispatcher){
emscripten_async_call(QWasmEventDispatcher::mainThreadWakeUp, eventdispatcher, 0);
};
emscripten_async_run_in_main_runtime_thread_(EM_FUNC_SIG_VI, (void *)intermediate, this);
}
#endif
QEventDispatcherUNIX::wakeUp();
}