diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index c8ab158d7e46..81c85c021276 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -2829,7 +2829,21 @@ WorkerMain(void* arg) js_delete(input); } -Vector workerThreads; +// Workers can spawn other workers, so we need a lock to access workerThreads. +static PRLock* workerThreadsLock = nullptr; +static Vector workerThreads; + +class MOZ_RAII AutoLockWorkerThreads +{ + public: + AutoLockWorkerThreads() { + MOZ_ASSERT(workerThreadsLock); + PR_Lock(workerThreadsLock); + } + ~AutoLockWorkerThreads() { + PR_Unlock(workerThreadsLock); + } +}; static bool EvalInWorker(JSContext* cx, unsigned argc, Value* vp) @@ -2848,6 +2862,14 @@ EvalInWorker(JSContext* cx, unsigned argc, Value* vp) if (!args[0].toString()->ensureLinear(cx)) return false; + if (!workerThreadsLock) { + workerThreadsLock = PR_NewLock(); + if (!workerThreadsLock) { + ReportOutOfMemory(cx); + return false; + } + } + JSLinearString* str = &args[0].toString()->asLinear(); char16_t* chars = (char16_t*) js_malloc(str->length() * sizeof(char16_t)); @@ -2872,6 +2894,7 @@ EvalInWorker(JSContext* cx, unsigned argc, Value* vp) return false; } + AutoLockWorkerThreads alwt; if (!workerThreads.append(thread)) { ReportOutOfMemory(cx); PR_JoinThread(thread); @@ -3068,6 +3091,34 @@ ScheduleWatchdog(JSRuntime* rt, double t) return true; } +static void +KillWorkerThreads() +{ + MOZ_ASSERT_IF(!CanUseExtraThreads(), workerThreads.empty()); + + if (!workerThreadsLock) { + MOZ_ASSERT(workerThreads.empty()); + return; + } + + while (true) { + // We need to leave the AutoLockWorkerThreads scope before we call + // PR_JoinThread, to avoid deadlocks when AutoLockWorkerThreads is + // used by the worker thread. + PRThread* thread; + { + AutoLockWorkerThreads alwt; + if (workerThreads.empty()) + break; + thread = workerThreads.popCopy(); + } + PR_JoinThread(thread); + } + + PR_DestroyLock(workerThreadsLock); + workerThreadsLock = nullptr; +} + static void CancelExecution(JSRuntime* rt) { @@ -7010,9 +7061,7 @@ main(int argc, char** argv, char** envp) KillWatchdog(rt); - MOZ_ASSERT_IF(!CanUseExtraThreads(), workerThreads.empty()); - for (size_t i = 0; i < workerThreads.length(); i++) - PR_JoinThread(workerThreads[i]); + KillWorkerThreads(); DestructSharedArrayBufferMailbox();