diff --git a/system/include/emscripten/threading.h b/system/include/emscripten/threading.h index aaf02b0b2bb23..9e605da5f3894 100644 --- a/system/include/emscripten/threading.h +++ b/system/include/emscripten/threading.h @@ -152,6 +152,9 @@ typedef struct em_queued_call // after it has been executed. If false, the caller is in control of the // memory. int calleeDelete; + + // The thread on which to perform the call. + void* targetThread; } em_queued_call; void emscripten_sync_run_in_main_thread(em_queued_call *call); diff --git a/system/lib/pthread/library_pthread.c b/system/lib/pthread/library_pthread.c index 6af1da45d6379..e3c1a8de6bd15 100644 --- a/system/lib/pthread/library_pthread.c +++ b/system/lib/pthread/library_pthread.c @@ -174,6 +174,7 @@ static em_queued_call* em_queued_call_malloc() { call->operationDone = 0; call->functionPtr = 0; call->satelliteData = 0; + call->targetThread = 0; } return call; } @@ -411,7 +412,7 @@ static CallQueue* GetOrAllocateQueue( EMSCRIPTEN_RESULT emscripten_wait_for_call_v(em_queued_call* call, double timeoutMSecs) { int r; - + int notifiedThread = 0; int done = emscripten_atomic_load_u32(&call->operationDone); if (!done) { double now = emscripten_get_now(); @@ -420,6 +421,16 @@ EMSCRIPTEN_RESULT emscripten_wait_for_call_v(em_queued_call* call, double timeou while (!done && now < waitEndTime) { r = emscripten_futex_wait(&call->operationDone, 0, waitEndTime - now); done = emscripten_atomic_load_u32(&call->operationDone); + if (!done && !notifiedThread) { + // If we are not done even after the wait, notify the target thread, + // which in a race condition may have finished handling its event queue + // just after we added our event. (We could also notify it once right + // after adding the event to the queue, but that could lead to a lot of + // unnecessary postMessages, so just do it when we are sure it needs to + // be woken up. + _emscripten_notify_thread_queue(call->targetThread, emscripten_main_browser_thread_id()); + notifiedThread = 1; + } now = emscripten_get_now(); } emscripten_set_current_thread_status(EM_THREAD_STATUS_RUNNING); @@ -754,6 +765,7 @@ EMSCRIPTEN_KEEPALIVE double emscripten_run_in_main_runtime_thread_js(int index, c->calleeDelete = 1-sync; c->functionEnum = EM_PROXIED_JS_FUNCTION; c->functionPtr = (void*)index; + c->targetThread = emscripten_main_browser_thread_id(); // We write out the JS doubles into args[], which must be of appropriate size - JS will assume that. assert(sizeof(em_variant_val) == sizeof(double)); assert(num_args+1 <= EM_QUEUED_JS_CALL_MAX_ARGS); @@ -781,6 +793,7 @@ void emscripten_async_run_in_main_runtime_thread_(EM_FUNC_SIGNATURE sig, void* f return; q->functionEnum = sig; q->functionPtr = func_ptr; + q->targetThread = emscripten_main_browser_thread_id(); EM_FUNC_SIGNATURE argumentsType = sig & EM_FUNC_SIG_ARGUMENTS_TYPE_MASK; va_list args; @@ -818,6 +831,7 @@ em_queued_call* emscripten_async_waitable_run_in_main_runtime_thread_( return NULL; q->functionEnum = sig; q->functionPtr = func_ptr; + q->targetThread = emscripten_main_browser_thread_id(); EM_FUNC_SIGNATURE argumentsType = sig & EM_FUNC_SIG_ARGUMENTS_TYPE_MASK; va_list args; @@ -864,6 +878,7 @@ int EMSCRIPTEN_KEEPALIVE _emscripten_call_on_thread( q->functionEnum = sig; q->functionPtr = func_ptr; q->satelliteData = satellite; + q->targetThread = targetThread; EM_FUNC_SIGNATURE argumentsType = sig & EM_FUNC_SIG_ARGUMENTS_TYPE_MASK; va_list args;