123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371 |
- #include "config.h"
- #include "SlotVisitor.h"
- #include "SlotVisitorInlines.h"
- #include "ConservativeRoots.h"
- #include "CopiedSpace.h"
- #include "CopiedSpaceInlines.h"
- #include "GCThread.h"
- #include "JSArray.h"
- #include "JSDestructibleObject.h"
- #include "VM.h"
- #include "JSObject.h"
- #include "JSString.h"
- #include "Operations.h"
- #include <wtf/StackStats.h>
- namespace JSC {
- SlotVisitor::SlotVisitor(GCThreadSharedData& shared)
- : m_stack(shared.m_vm->heap.blockAllocator())
- , m_visitCount(0)
- , m_isInParallelMode(false)
- , m_shared(shared)
- , m_shouldHashCons(false)
- #if !ASSERT_DISABLED
- , m_isCheckingForDefaultMarkViolation(false)
- , m_isDraining(false)
- #endif
- {
- }
- SlotVisitor::~SlotVisitor()
- {
- ASSERT(m_stack.isEmpty());
- }
- void SlotVisitor::setup()
- {
- m_shared.m_shouldHashCons = m_shared.m_vm->haveEnoughNewStringsToHashCons();
- m_shouldHashCons = m_shared.m_shouldHashCons;
- #if ENABLE(PARALLEL_GC)
- for (unsigned i = 0; i < m_shared.m_gcThreads.size(); ++i)
- m_shared.m_gcThreads[i]->slotVisitor()->m_shouldHashCons = m_shared.m_shouldHashCons;
- #endif
- }
- void SlotVisitor::reset()
- {
- m_visitCount = 0;
- ASSERT(m_stack.isEmpty());
- #if ENABLE(PARALLEL_GC)
- ASSERT(m_opaqueRoots.isEmpty()); // Should have merged by now.
- #else
- m_opaqueRoots.clear();
- #endif
- if (m_shouldHashCons) {
- m_uniqueStrings.clear();
- m_shouldHashCons = false;
- }
- }
- void SlotVisitor::append(ConservativeRoots& conservativeRoots)
- {
- StackStats::probe();
- JSCell** roots = conservativeRoots.roots();
- size_t size = conservativeRoots.size();
- for (size_t i = 0; i < size; ++i)
- internalAppend(roots[i]);
- }
- ALWAYS_INLINE static void visitChildren(SlotVisitor& visitor, const JSCell* cell)
- {
- StackStats::probe();
- #if ENABLE(SIMPLE_HEAP_PROFILING)
- m_visitedTypeCounts.count(cell);
- #endif
- ASSERT(Heap::isMarked(cell));
-
- if (isJSString(cell)) {
- JSString::visitChildren(const_cast<JSCell*>(cell), visitor);
- return;
- }
- if (isJSFinalObject(cell)) {
- JSFinalObject::visitChildren(const_cast<JSCell*>(cell), visitor);
- return;
- }
- if (isJSArray(cell)) {
- JSArray::visitChildren(const_cast<JSCell*>(cell), visitor);
- return;
- }
- cell->methodTable()->visitChildren(const_cast<JSCell*>(cell), visitor);
- }
- void SlotVisitor::donateKnownParallel()
- {
- StackStats::probe();
- // NOTE: Because we re-try often, we can afford to be conservative, and
- // assume that donating is not profitable.
- // Avoid locking when a thread reaches a dead end in the object graph.
- if (m_stack.size() < 2)
- return;
- // If there's already some shared work queued up, be conservative and assume
- // that donating more is not profitable.
- if (m_shared.m_sharedMarkStack.size())
- return;
- // If we're contending on the lock, be conservative and assume that another
- // thread is already donating.
- MutexTryLocker locker(m_shared.m_markingLock);
- if (!locker.locked())
- return;
- // Otherwise, assume that a thread will go idle soon, and donate.
- m_stack.donateSomeCellsTo(m_shared.m_sharedMarkStack);
- if (m_shared.m_numberOfActiveParallelMarkers < Options::numberOfGCMarkers())
- m_shared.m_markingCondition.broadcast();
- }
- void SlotVisitor::drain()
- {
- StackStats::probe();
- ASSERT(m_isInParallelMode);
-
- #if ENABLE(PARALLEL_GC)
- if (Options::numberOfGCMarkers() > 1) {
- while (!m_stack.isEmpty()) {
- m_stack.refill();
- for (unsigned countdown = Options::minimumNumberOfScansBetweenRebalance(); m_stack.canRemoveLast() && countdown--;)
- visitChildren(*this, m_stack.removeLast());
- donateKnownParallel();
- }
-
- mergeOpaqueRootsIfNecessary();
- return;
- }
- #endif
-
- while (!m_stack.isEmpty()) {
- m_stack.refill();
- while (m_stack.canRemoveLast())
- visitChildren(*this, m_stack.removeLast());
- }
- }
- void SlotVisitor::drainFromShared(SharedDrainMode sharedDrainMode)
- {
- StackStats::probe();
- ASSERT(m_isInParallelMode);
-
- ASSERT(Options::numberOfGCMarkers());
-
- bool shouldBeParallel;
- #if ENABLE(PARALLEL_GC)
- shouldBeParallel = Options::numberOfGCMarkers() > 1;
- #else
- ASSERT(Options::numberOfGCMarkers() == 1);
- shouldBeParallel = false;
- #endif
-
- if (!shouldBeParallel) {
- // This call should be a no-op.
- ASSERT_UNUSED(sharedDrainMode, sharedDrainMode == MasterDrain);
- ASSERT(m_stack.isEmpty());
- ASSERT(m_shared.m_sharedMarkStack.isEmpty());
- return;
- }
-
- #if ENABLE(PARALLEL_GC)
- {
- MutexLocker locker(m_shared.m_markingLock);
- m_shared.m_numberOfActiveParallelMarkers++;
- }
- while (true) {
- {
- MutexLocker locker(m_shared.m_markingLock);
- m_shared.m_numberOfActiveParallelMarkers--;
- // How we wait differs depending on drain mode.
- if (sharedDrainMode == MasterDrain) {
- // Wait until either termination is reached, or until there is some work
- // for us to do.
- while (true) {
- // Did we reach termination?
- if (!m_shared.m_numberOfActiveParallelMarkers && m_shared.m_sharedMarkStack.isEmpty()) {
- // Let any sleeping slaves know it's time for them to return;
- m_shared.m_markingCondition.broadcast();
- return;
- }
-
- // Is there work to be done?
- if (!m_shared.m_sharedMarkStack.isEmpty())
- break;
-
- // Otherwise wait.
- m_shared.m_markingCondition.wait(m_shared.m_markingLock);
- }
- } else {
- ASSERT(sharedDrainMode == SlaveDrain);
-
- // Did we detect termination? If so, let the master know.
- if (!m_shared.m_numberOfActiveParallelMarkers && m_shared.m_sharedMarkStack.isEmpty())
- m_shared.m_markingCondition.broadcast();
-
- while (m_shared.m_sharedMarkStack.isEmpty() && !m_shared.m_parallelMarkersShouldExit)
- m_shared.m_markingCondition.wait(m_shared.m_markingLock);
-
- // Is the current phase done? If so, return from this function.
- if (m_shared.m_parallelMarkersShouldExit)
- return;
- }
-
- size_t idleThreadCount = Options::numberOfGCMarkers() - m_shared.m_numberOfActiveParallelMarkers;
- m_stack.stealSomeCellsFrom(m_shared.m_sharedMarkStack, idleThreadCount);
- m_shared.m_numberOfActiveParallelMarkers++;
- }
-
- drain();
- }
- #endif
- }
- void SlotVisitor::mergeOpaqueRoots()
- {
- StackStats::probe();
- ASSERT(!m_opaqueRoots.isEmpty()); // Should only be called when opaque roots are non-empty.
- {
- MutexLocker locker(m_shared.m_opaqueRootsLock);
- HashSet<void*>::iterator begin = m_opaqueRoots.begin();
- HashSet<void*>::iterator end = m_opaqueRoots.end();
- for (HashSet<void*>::iterator iter = begin; iter != end; ++iter)
- m_shared.m_opaqueRoots.add(*iter);
- }
- m_opaqueRoots.clear();
- }
- ALWAYS_INLINE bool JSString::tryHashConsLock()
- {
- #if ENABLE(PARALLEL_GC)
- unsigned currentFlags = m_flags;
- if (currentFlags & HashConsLock)
- return false;
- unsigned newFlags = currentFlags | HashConsLock;
- if (!WTF::weakCompareAndSwap(&m_flags, currentFlags, newFlags))
- return false;
- WTF::memoryBarrierAfterLock();
- return true;
- #else
- if (isHashConsSingleton())
- return false;
- m_flags |= HashConsLock;
- return true;
- #endif
- }
- ALWAYS_INLINE void JSString::releaseHashConsLock()
- {
- #if ENABLE(PARALLEL_GC)
- WTF::memoryBarrierBeforeUnlock();
- #endif
- m_flags &= ~HashConsLock;
- }
- ALWAYS_INLINE bool JSString::shouldTryHashCons()
- {
- return ((length() > 1) && !isRope() && !isHashConsSingleton());
- }
- ALWAYS_INLINE void SlotVisitor::internalAppend(JSValue* slot)
- {
- // This internalAppend is only intended for visits to object and array backing stores.
- // as it can change the JSValue pointed to be the argument when the original JSValue
- // is a string that contains the same contents as another string.
- StackStats::probe();
- ASSERT(slot);
- JSValue value = *slot;
- ASSERT(value);
- if (!value.isCell())
- return;
- JSCell* cell = value.asCell();
- if (!cell)
- return;
- validate(cell);
- if (m_shouldHashCons && cell->isString()) {
- JSString* string = jsCast<JSString*>(cell);
- if (string->shouldTryHashCons() && string->tryHashConsLock()) {
- UniqueStringMap::AddResult addResult = m_uniqueStrings.add(string->string().impl(), value);
- if (addResult.isNewEntry)
- string->setHashConsSingleton();
- else {
- JSValue existingJSValue = addResult.iterator->value;
- if (value != existingJSValue)
- jsCast<JSString*>(existingJSValue.asCell())->clearHashConsSingleton();
- *slot = existingJSValue;
- string->releaseHashConsLock();
- return;
- }
- string->releaseHashConsLock();
- }
- }
- internalAppend(cell);
- }
- void SlotVisitor::harvestWeakReferences()
- {
- StackStats::probe();
- for (WeakReferenceHarvester* current = m_shared.m_weakReferenceHarvesters.head(); current; current = current->next())
- current->visitWeakReferences(*this);
- }
- void SlotVisitor::finalizeUnconditionalFinalizers()
- {
- StackStats::probe();
- while (m_shared.m_unconditionalFinalizers.hasNext())
- m_shared.m_unconditionalFinalizers.removeNext()->finalizeUnconditionally();
- }
- #if ENABLE(GC_VALIDATION)
- void SlotVisitor::validate(JSCell* cell)
- {
- RELEASE_ASSERT(cell);
- if (!cell->structure()) {
- dataLogF("cell at %p has a null structure\n" , cell);
- CRASH();
- }
- // Both the cell's structure, and the cell's structure's structure should be the Structure Structure.
- // I hate this sentence.
- if (cell->structure()->structure()->JSCell::classInfo() != cell->structure()->JSCell::classInfo()) {
- const char* parentClassName = 0;
- const char* ourClassName = 0;
- if (cell->structure()->structure() && cell->structure()->structure()->JSCell::classInfo())
- parentClassName = cell->structure()->structure()->JSCell::classInfo()->className;
- if (cell->structure()->JSCell::classInfo())
- ourClassName = cell->structure()->JSCell::classInfo()->className;
- dataLogF("parent structure (%p <%s>) of cell at %p doesn't match cell's structure (%p <%s>)\n",
- cell->structure()->structure(), parentClassName, cell, cell->structure(), ourClassName);
- CRASH();
- }
- // Make sure we can walk the ClassInfo chain
- const ClassInfo* info = cell->classInfo();
- do { } while ((info = info->parentClass));
- }
- #else
- void SlotVisitor::validate(JSCell*)
- {
- }
- #endif
- } // namespace JSC
|