AsyncAudioDecoder::~AsyncAudioDecoder() { m_queue.kill(); // Stop thread. waitForThreadCompletion(m_threadID); m_threadID = 0; }
void HRTFDatabaseLoader::waitForLoaderThreadCompletion() { MutexLocker locker(m_threadLock); // waitForThreadCompletion() should not be called twice for the same thread. if (m_databaseLoaderThread) waitForThreadCompletion(m_databaseLoaderThread); m_databaseLoaderThread = 0; }
void CurlDownloadManager::stopThread() { m_runThread = false; if (m_threadId) { waitForThreadCompletion(m_threadId); m_threadId = 0; } }
void CurlDownloadManager::startThreadIfNeeded() { if (!m_runThread) { if (m_threadId) waitForThreadCompletion(m_threadId); m_runThread = true; m_threadId = createThread(downloadThread, this, "downloadThread"); } }
AsyncAudioDecoder::~AsyncAudioDecoder() { m_queue.kill(); // Stop thread. void* exitCode; waitForThreadCompletion(m_threadID, &exitCode); m_threadID = 0; }
void CurlDownloadManager::stopThread() { setRunThread(false); if (m_threadId) { waitForThreadCompletion(m_threadId); m_threadId = 0; } }
BlockAllocator::~BlockAllocator() { releaseFreeBlocks(); { MutexLocker locker(m_freeBlockLock); m_blockFreeingThreadShouldQuit = true; m_freeBlockCondition.broadcast(); } waitForThreadCompletion(m_blockFreeingThread); }
void GCController::garbageCollectOnAlternateThreadForDebugging(bool waitUntilDone) { ThreadIdentifier threadID = createThread(collect, 0, "WebCore: GCController"); if (waitUntilDone) { waitForThreadCompletion(threadID); return; } detachThread(threadID); }
Worklist::~Worklist() { { MutexLocker locker(m_lock); for (unsigned i = m_threads.size(); i--;) m_queue.append(nullptr); // Use null plan to indicate that we want the thread to terminate. m_planEnqueued.broadcast(); } for (unsigned i = m_threads.size(); i--;) waitForThreadCompletion(m_threads[i]->m_identifier); ASSERT(!m_numberOfActiveThreads); }
void OfflineAudioDestinationNode::uninitialize() { if (!isInitialized()) return; if (m_renderThread) { waitForThreadCompletion(m_renderThread); m_renderThread = 0; } AudioNode::uninitialize(); }
void StorageThread::terminate() { ASSERT(isMainThread()); ASSERT(!m_queue.killed() && m_threadID); // Even in weird, exceptional cases, don't wait on a nonexistent thread to terminate. if (!m_threadID) return; m_queue.append(StorageTask::createTerminate(this)); waitForThreadCompletion(m_threadID); ASSERT(m_queue.killed()); m_threadID = 0; }
BlockAllocator::~BlockAllocator() { releaseFreeRegions(); { MutexLocker locker(m_emptyRegionConditionLock); m_blockFreeingThreadShouldQuit = true; m_emptyRegionCondition.broadcast(); } if (m_blockFreeingThread) waitForThreadCompletion(m_blockFreeingThread); ASSERT(allRegionSetsAreEmpty()); ASSERT(m_emptyRegions.isEmpty()); }
MarkStackThreadSharedData::~MarkStackThreadSharedData() { #if ENABLE(PARALLEL_GC) // Destroy our marking threads. { MutexLocker locker(m_markingLock); m_parallelMarkersShouldExit = true; m_markingCondition.broadcast(); } for (unsigned i = 0; i < m_markingThreads.size(); ++i) waitForThreadCompletion(m_markingThreads[i]); #endif }
Heap::~Heap() { // destroy our thread { MutexLocker locker(m_freeBlockLock); m_blockFreeingThreadShouldQuit = true; m_freeBlockCondition.broadcast(); } waitForThreadCompletion(m_blockFreeingThread, 0); // The destroy function must already have been called, so assert this. ASSERT(!m_globalData); }
void StorageThread::terminate() { ASSERT(isMainThread()); ASSERT(!m_queue.killed() && m_threadID); activeStorageThreads().remove(this); // Even in weird, exceptional cases, don't wait on a nonexistent thread to terminate. if (!m_threadID) return; m_queue.append(std::make_unique<Function<void ()>>(bind(&StorageThread::performTerminate, this))); waitForThreadCompletion(m_threadID); ASSERT(m_queue.killed()); m_threadID = 0; }
void runLockTest(unsigned numThreadGroups, unsigned numThreadsPerGroup, unsigned workPerCriticalSection, unsigned numIterations) { std::unique_ptr<LockType[]> locks = std::make_unique<LockType[]>(numThreadGroups); std::unique_ptr<double[]> words = std::make_unique<double[]>(numThreadGroups); std::unique_ptr<ThreadIdentifier[]> threads = std::make_unique<ThreadIdentifier[]>(numThreadGroups * numThreadsPerGroup); for (unsigned threadGroupIndex = numThreadGroups; threadGroupIndex--;) { words[threadGroupIndex] = 0; for (unsigned threadIndex = numThreadsPerGroup; threadIndex--;) { threads[threadGroupIndex * numThreadsPerGroup + threadIndex] = createThread( "Lock test thread", [threadGroupIndex, &locks, &words, numIterations, workPerCriticalSection] () { for (unsigned i = numIterations; i--;) { locks[threadGroupIndex].lock(); for (unsigned j = workPerCriticalSection; j--;) words[threadGroupIndex]++; locks[threadGroupIndex].unlock(); } }); } } for (unsigned threadIndex = numThreadGroups * numThreadsPerGroup; threadIndex--;) waitForThreadCompletion(threads[threadIndex]); double expected = 0; for (uint64_t i = static_cast<uint64_t>(numIterations) * workPerCriticalSection * numThreadsPerGroup; i--;) expected++; for (unsigned threadGroupIndex = numThreadGroups; threadGroupIndex--;) EXPECT_EQ(expected, words[threadGroupIndex]); // Now test that the locks correctly reset themselves. We expect that if a single thread locks // each of the locks twice in a row, then the lock should be in a pristine state. for (unsigned threadGroupIndex = numThreadGroups; threadGroupIndex--;) { for (unsigned i = 2; i--;) { locks[threadGroupIndex].lock(); locks[threadGroupIndex].unlock(); } EXPECT_EQ(true, LockInspector::isFullyReset(locks[threadGroupIndex])); } }
GCThreadSharedData::~GCThreadSharedData() { #if ENABLE(PARALLEL_GC) // Destroy our marking threads. { MutexLocker markingLocker(m_markingLock); MutexLocker phaseLocker(m_phaseLock); ASSERT(m_currentPhase == NoPhase); m_parallelMarkersShouldExit = true; m_gcThreadsShouldWait = false; m_currentPhase = Exit; m_phaseCondition.broadcast(); } for (unsigned i = 0; i < m_gcThreads.size(); ++i) { waitForThreadCompletion(m_gcThreads[i]->threadID()); delete m_gcThreads[i]; } #endif }
GCThreadSharedData::~GCThreadSharedData() { #if ENABLE(PARALLEL_GC) // Destroy our marking threads. { std::lock_guard<std::mutex> markingLock(m_markingMutex); std::lock_guard<std::mutex> phaseLock(m_phaseMutex); ASSERT(m_currentPhase == NoPhase); m_parallelMarkersShouldExit = true; m_gcThreadsShouldWait = false; m_currentPhase = Exit; m_phaseConditionVariable.notify_all(); } for (unsigned i = 0; i < m_gcThreads.size(); ++i) { waitForThreadCompletion(m_gcThreads[i]->threadID()); delete m_gcThreads[i]; } #endif }
void SamplingThread::stop() { ASSERT(s_running); s_running = false; waitForThreadCompletion(s_samplingThread, 0); }
int waitForThreadCompletion(ThreadIdentifier threadID, void**) { return waitForThreadCompletion(threadID); }
// We are creating a bunch of threads that touch the main thread's stack. This will make ASAN unhappy. // The reason this is OK is that we guarantee that the main thread doesn't continue until all threads // that could touch its stack are done executing. SUPPRESS_ASAN void Plan::run() { if (!parseAndValidateModule()) return; auto tryReserveCapacity = [this] (auto& vector, size_t size, const char* what) { if (UNLIKELY(!vector.tryReserveCapacity(size))) { StringBuilder builder; builder.appendLiteral("Failed allocating enough space for "); builder.appendNumber(size); builder.append(what); m_errorMessage = builder.toString(); return false; } return true; }; if (!tryReserveCapacity(m_wasmExitStubs, m_moduleInformation->importFunctionSignatureIndices.size(), " WebAssembly to JavaScript stubs") || !tryReserveCapacity(m_unlinkedWasmToWasmCalls, m_functionLocationInBinary.size(), " unlinked WebAssembly to WebAssembly calls") || !tryReserveCapacity(m_wasmInternalFunctions, m_functionLocationInBinary.size(), " WebAssembly functions") || !tryReserveCapacity(m_compilationContexts, m_functionLocationInBinary.size(), " compilation contexts")) return; m_unlinkedWasmToWasmCalls.resize(m_functionLocationInBinary.size()); m_wasmInternalFunctions.resize(m_functionLocationInBinary.size()); m_compilationContexts.resize(m_functionLocationInBinary.size()); for (unsigned importIndex = 0; importIndex < m_moduleInformation->imports.size(); ++importIndex) { Import* import = &m_moduleInformation->imports[importIndex]; if (import->kind != ExternalKind::Function) continue; unsigned importFunctionIndex = m_wasmExitStubs.size(); if (verbose) dataLogLn("Processing import function number ", importFunctionIndex, ": ", import->module, ": ", import->field); SignatureIndex signatureIndex = m_moduleInformation->importFunctionSignatureIndices.at(import->kindIndex); m_wasmExitStubs.uncheckedAppend(exitStubGenerator(m_vm, m_callLinkInfos, signatureIndex, importFunctionIndex)); } m_currentIndex = 0; auto doWork = [this] { while (true) { uint32_t functionIndex; { auto locker = holdLock(m_lock); if (m_currentIndex >= m_functionLocationInBinary.size()) return; functionIndex = m_currentIndex; ++m_currentIndex; } const uint8_t* functionStart = m_source + m_functionLocationInBinary[functionIndex].start; size_t functionLength = m_functionLocationInBinary[functionIndex].end - m_functionLocationInBinary[functionIndex].start; ASSERT(functionLength <= m_sourceLength); SignatureIndex signatureIndex = m_moduleInformation->internalFunctionSignatureIndices[functionIndex]; const Signature* signature = SignatureInformation::get(m_vm, signatureIndex); unsigned functionIndexSpace = m_wasmExitStubs.size() + functionIndex; ASSERT_UNUSED(functionIndexSpace, m_moduleInformation->signatureIndexFromFunctionIndexSpace(functionIndexSpace) == signatureIndex); ASSERT(validateFunction(m_vm, functionStart, functionLength, signature, *m_moduleInformation, m_moduleSignatureIndicesToUniquedSignatureIndices)); m_unlinkedWasmToWasmCalls[functionIndex] = Vector<UnlinkedWasmToWasmCall>(); auto parseAndCompileResult = parseAndCompile(*m_vm, m_compilationContexts[functionIndex], functionStart, functionLength, signature, m_unlinkedWasmToWasmCalls[functionIndex], *m_moduleInformation, m_moduleSignatureIndicesToUniquedSignatureIndices); if (UNLIKELY(!parseAndCompileResult)) { auto locker = holdLock(m_lock); if (!m_errorMessage) { // Multiple compiles could fail simultaneously. We arbitrarily choose the first. m_errorMessage = makeString(parseAndCompileResult.error(), ", in function at index ", String::number(functionIndex)); // FIXME make this an Expected. } m_currentIndex = m_functionLocationInBinary.size(); // We will terminate on the next execution. continue; } m_wasmInternalFunctions[functionIndex] = WTFMove(*parseAndCompileResult); } }; MonotonicTime startTime; if (verbose || Options::reportCompileTimes()) startTime = MonotonicTime::now(); uint32_t threadCount = Options::useConcurrentJIT() ? WTF::numberOfProcessorCores() : 1; uint32_t numWorkerThreads = threadCount - 1; Vector<ThreadIdentifier> threads; threads.reserveCapacity(numWorkerThreads); for (uint32_t i = 0; i < numWorkerThreads; i++) threads.uncheckedAppend(createThread("jsc.wasm-b3-compilation.thread", doWork)); doWork(); // Let the main thread do some work too. for (uint32_t i = 0; i < numWorkerThreads; i++) waitForThreadCompletion(threads[i]); for (uint32_t functionIndex = 0; functionIndex < m_functionLocationInBinary.size(); functionIndex++) { { CompilationContext& context = m_compilationContexts[functionIndex]; SignatureIndex signatureIndex = m_moduleInformation->internalFunctionSignatureIndices[functionIndex]; String signatureDescription = SignatureInformation::get(m_vm, signatureIndex)->toString(); { LinkBuffer linkBuffer(*m_vm, *context.wasmEntrypointJIT, nullptr); m_wasmInternalFunctions[functionIndex]->wasmEntrypoint.compilation = std::make_unique<B3::Compilation>(FINALIZE_CODE(linkBuffer, ("WebAssembly function[%i] %s", functionIndex, signatureDescription.ascii().data())), WTFMove(context.wasmEntrypointByproducts)); } { LinkBuffer linkBuffer(*m_vm, *context.jsEntrypointJIT, nullptr); linkBuffer.link(context.jsEntrypointToWasmEntrypointCall, FunctionPtr(m_wasmInternalFunctions[functionIndex]->wasmEntrypoint.compilation->code().executableAddress())); m_wasmInternalFunctions[functionIndex]->jsToWasmEntrypoint.compilation = std::make_unique<B3::Compilation>(FINALIZE_CODE(linkBuffer, ("JavaScript->WebAssembly entrypoint[%i] %s", functionIndex, signatureDescription.ascii().data())), WTFMove(context.jsEntrypointByproducts)); } } } if (verbose || Options::reportCompileTimes()) { dataLogLn("Took ", (MonotonicTime::now() - startTime).microseconds(), " us to compile and link the module"); } // Patch the call sites for each WebAssembly function. for (auto& unlinked : m_unlinkedWasmToWasmCalls) { for (auto& call : unlinked) { void* executableAddress; if (m_moduleInformation->isImportedFunctionFromFunctionIndexSpace(call.functionIndex)) { // FIXME imports could have been linked in B3, instead of generating a patchpoint. This condition should be replaced by a RELEASE_ASSERT. https://bugs.webkit.org/show_bug.cgi?id=166462 executableAddress = call.target == UnlinkedWasmToWasmCall::Target::ToJs ? m_wasmExitStubs.at(call.functionIndex).wasmToJs.code().executableAddress() : m_wasmExitStubs.at(call.functionIndex).wasmToWasm.code().executableAddress(); } else { ASSERT(call.target != UnlinkedWasmToWasmCall::Target::ToJs); executableAddress = m_wasmInternalFunctions.at(call.functionIndex - m_wasmExitStubs.size())->wasmEntrypoint.compilation->code().executableAddress(); } MacroAssembler::repatchCall(call.callLocation, CodeLocationLabel(executableAddress)); } } m_failed = false; }
void SamplingTool::stop() { ASSERT(m_running); m_running = false; waitForThreadCompletion(m_samplingThread, 0); }
void HTMLParserThread::stop() { m_queue.kill(); waitForThreadCompletion(m_threadID); }