/* * Copyright (C) 2009, 2011 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "GCThreadSharedData.h" #include "CopyVisitor.h" #include "CopyVisitorInlines.h" #include "GCThread.h" #include "JSGlobalData.h" #include "MarkStack.h" #include "SlotVisitor.h" #include "SlotVisitorInlines.h" namespace JSC { #if ENABLE(PARALLEL_GC) void GCThreadSharedData::resetChildren() { for (size_t i = 0; i < m_gcThreads.size(); ++i) m_gcThreads[i]->slotVisitor()->reset(); } size_t GCThreadSharedData::childVisitCount() { unsigned long result = 0; for (unsigned i = 0; i < m_gcThreads.size(); ++i) result += m_gcThreads[i]->slotVisitor()->visitCount(); return result; } #endif GCThreadSharedData::GCThreadSharedData(JSGlobalData* globalData) : m_globalData(globalData) , m_copiedSpace(&globalData->heap.m_storageSpace) , m_shouldHashConst(false) , m_sharedMarkStack(globalData->heap.blockAllocator()) , m_numberOfActiveParallelMarkers(0) , m_parallelMarkersShouldExit(false) , m_copyIndex(0) , m_numberOfActiveGCThreads(0) , m_gcThreadsShouldWait(false) , m_currentPhase(NoPhase) { m_copyLock.Init(); #if ENABLE(PARALLEL_GC) // Grab the lock so the new GC threads can be properly initialized before they start running. MutexLocker locker(m_phaseLock); for (unsigned i = 1; i < Options::numberOfGCMarkers(); ++i) { m_numberOfActiveGCThreads++; SlotVisitor* slotVisitor = new SlotVisitor(*this); CopyVisitor* copyVisitor = new CopyVisitor(*this); GCThread* newThread = new GCThread(*this, slotVisitor, copyVisitor); ThreadIdentifier threadID = createThread(GCThread::gcThreadStartFunc, newThread, "JavaScriptCore::Marking"); newThread->initializeThreadID(threadID); m_gcThreads.append(newThread); } // Wait for all the GCThreads to get to the right place. while (m_numberOfActiveGCThreads) m_activityCondition.wait(m_phaseLock); #endif } 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 } void GCThreadSharedData::reset() { ASSERT(m_sharedMarkStack.isEmpty()); #if ENABLE(PARALLEL_GC) m_opaqueRoots.clear(); #else ASSERT(m_opaqueRoots.isEmpty()); #endif m_weakReferenceHarvesters.removeAll(); if (m_shouldHashConst) { m_globalData->resetNewStringsSinceLastHashConst(); m_shouldHashConst = false; } } void GCThreadSharedData::startNextPhase(GCPhase phase) { MutexLocker phaseLocker(m_phaseLock); ASSERT(!m_gcThreadsShouldWait); ASSERT(m_currentPhase == NoPhase); m_gcThreadsShouldWait = true; m_currentPhase = phase; m_phaseCondition.broadcast(); } void GCThreadSharedData::endCurrentPhase() { ASSERT(m_gcThreadsShouldWait); MutexLocker locker(m_phaseLock); m_currentPhase = NoPhase; m_gcThreadsShouldWait = false; m_phaseCondition.broadcast(); while (m_numberOfActiveGCThreads) m_activityCondition.wait(m_phaseLock); } void GCThreadSharedData::didStartMarking() { MutexLocker markingLocker(m_markingLock); m_parallelMarkersShouldExit = false; startNextPhase(Mark); } void GCThreadSharedData::didFinishMarking() { { MutexLocker markingLocker(m_markingLock); m_parallelMarkersShouldExit = true; m_markingCondition.broadcast(); } ASSERT(m_currentPhase == Mark); endCurrentPhase(); } void GCThreadSharedData::didStartCopying() { { SpinLockHolder locker(&m_copyLock); WTF::copyToVector(m_copiedSpace->m_blockSet, m_blocksToCopy); m_copyIndex = 0; } // We do this here so that we avoid a race condition where the main thread can // blow through all of the copying work before the GCThreads fully wake up. // The GCThreads then request a block from the CopiedSpace when the copying phase // has completed, which isn't allowed. for (size_t i = 0; i < m_gcThreads.size(); i++) m_gcThreads[i]->copyVisitor()->startCopying(); startNextPhase(Copy); } void GCThreadSharedData::didFinishCopying() { ASSERT(m_currentPhase == Copy); endCurrentPhase(); } } // namespace JSC