| /* |
| * 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. |
| */ |
| |
| #ifndef MarkStack_h |
| #define MarkStack_h |
| |
| #include "CopiedSpace.h" |
| #include "HandleTypes.h" |
| #include "Options.h" |
| #include "JSValue.h" |
| #include "Register.h" |
| #include "UnconditionalFinalizer.h" |
| #include "VTableSpectrum.h" |
| #include "WeakReferenceHarvester.h" |
| #include <wtf/HashMap.h> |
| #include <wtf/HashSet.h> |
| #include <wtf/Vector.h> |
| #include <wtf/Noncopyable.h> |
| #include <wtf/OSAllocator.h> |
| #include <wtf/PageBlock.h> |
| |
| namespace JSC { |
| |
| class ConservativeRoots; |
| class JSGlobalData; |
| class MarkStack; |
| class ParallelModeEnabler; |
| class Register; |
| class SlotVisitor; |
| template<typename T> class WriteBarrierBase; |
| template<typename T> class JITWriteBarrier; |
| |
| struct MarkStackSegment { |
| MarkStackSegment* m_previous; |
| #if !ASSERT_DISABLED |
| size_t m_top; |
| #endif |
| |
| const JSCell** data() |
| { |
| return bitwise_cast<const JSCell**>(this + 1); |
| } |
| |
| static size_t capacityFromSize(size_t size) |
| { |
| return (size - sizeof(MarkStackSegment)) / sizeof(const JSCell*); |
| } |
| |
| static size_t sizeFromCapacity(size_t capacity) |
| { |
| return sizeof(MarkStackSegment) + capacity * sizeof(const JSCell*); |
| } |
| }; |
| |
| class MarkStackSegmentAllocator { |
| public: |
| MarkStackSegmentAllocator(); |
| ~MarkStackSegmentAllocator(); |
| |
| MarkStackSegment* allocate(); |
| void release(MarkStackSegment*); |
| |
| void shrinkReserve(); |
| |
| private: |
| Mutex m_lock; |
| MarkStackSegment* m_nextFreeSegment; |
| }; |
| |
| class MarkStackArray { |
| public: |
| MarkStackArray(MarkStackSegmentAllocator&); |
| ~MarkStackArray(); |
| |
| void append(const JSCell*); |
| |
| bool canRemoveLast(); |
| const JSCell* removeLast(); |
| bool refill(); |
| |
| bool isEmpty(); |
| |
| bool canDonateSomeCells(); // Returns false if you should definitely not call doanteSomeCellsTo(). |
| bool donateSomeCellsTo(MarkStackArray& other); // Returns true if some cells were donated. |
| |
| void stealSomeCellsFrom(MarkStackArray& other); |
| |
| size_t size(); |
| |
| private: |
| MarkStackSegment* m_topSegment; |
| |
| JS_EXPORT_PRIVATE void expand(); |
| |
| MarkStackSegmentAllocator& m_allocator; |
| |
| size_t m_segmentCapacity; |
| size_t m_top; |
| size_t m_numberOfPreviousSegments; |
| |
| size_t postIncTop() |
| { |
| size_t result = m_top++; |
| ASSERT(result == m_topSegment->m_top++); |
| return result; |
| } |
| |
| size_t preDecTop() |
| { |
| size_t result = --m_top; |
| ASSERT(result == --m_topSegment->m_top); |
| return result; |
| } |
| |
| void setTopForFullSegment() |
| { |
| ASSERT(m_topSegment->m_top == m_segmentCapacity); |
| m_top = m_segmentCapacity; |
| } |
| |
| void setTopForEmptySegment() |
| { |
| ASSERT(!m_topSegment->m_top); |
| m_top = 0; |
| } |
| |
| size_t top() |
| { |
| ASSERT(m_top == m_topSegment->m_top); |
| return m_top; |
| } |
| |
| #if ASSERT_DISABLED |
| void validatePrevious() { } |
| #else |
| void validatePrevious() |
| { |
| unsigned count = 0; |
| for (MarkStackSegment* current = m_topSegment->m_previous; current; current = current->m_previous) |
| count++; |
| ASSERT(count == m_numberOfPreviousSegments); |
| } |
| #endif |
| }; |
| |
| class MarkStackThreadSharedData { |
| public: |
| MarkStackThreadSharedData(JSGlobalData*); |
| ~MarkStackThreadSharedData(); |
| |
| void reset(); |
| |
| private: |
| friend class MarkStack; |
| friend class SlotVisitor; |
| |
| #if ENABLE(PARALLEL_GC) |
| void markingThreadMain(); |
| static void markingThreadStartFunc(void* heap); |
| #endif |
| |
| JSGlobalData* m_globalData; |
| CopiedSpace* m_copiedSpace; |
| |
| MarkStackSegmentAllocator m_segmentAllocator; |
| |
| Vector<ThreadIdentifier> m_markingThreads; |
| |
| Mutex m_markingLock; |
| ThreadCondition m_markingCondition; |
| MarkStackArray m_sharedMarkStack; |
| unsigned m_numberOfActiveParallelMarkers; |
| bool m_parallelMarkersShouldExit; |
| |
| Mutex m_opaqueRootsLock; |
| HashSet<void*> m_opaqueRoots; |
| |
| ListableHandler<WeakReferenceHarvester>::List m_weakReferenceHarvesters; |
| ListableHandler<UnconditionalFinalizer>::List m_unconditionalFinalizers; |
| }; |
| |
| class MarkStack { |
| WTF_MAKE_NONCOPYABLE(MarkStack); |
| friend class HeapRootVisitor; // Allowed to mark a JSValue* or JSCell** directly. |
| |
| public: |
| MarkStack(MarkStackThreadSharedData&); |
| ~MarkStack(); |
| |
| void append(ConservativeRoots&); |
| |
| template<typename T> void append(JITWriteBarrier<T>*); |
| template<typename T> void append(WriteBarrierBase<T>*); |
| void appendValues(WriteBarrierBase<Unknown>*, size_t count); |
| |
| template<typename T> |
| void appendUnbarrieredPointer(T**); |
| |
| void addOpaqueRoot(void*); |
| bool containsOpaqueRoot(void*); |
| int opaqueRootCount(); |
| |
| bool isEmpty() { return m_stack.isEmpty(); } |
| |
| void reset(); |
| |
| size_t visitCount() const { return m_visitCount; } |
| |
| #if ENABLE(SIMPLE_HEAP_PROFILING) |
| VTableSpectrum m_visitedTypeCounts; |
| #endif |
| |
| void addWeakReferenceHarvester(WeakReferenceHarvester* weakReferenceHarvester) |
| { |
| m_shared.m_weakReferenceHarvesters.addThreadSafe(weakReferenceHarvester); |
| } |
| |
| void addUnconditionalFinalizer(UnconditionalFinalizer* unconditionalFinalizer) |
| { |
| m_shared.m_unconditionalFinalizers.addThreadSafe(unconditionalFinalizer); |
| } |
| |
| protected: |
| JS_EXPORT_PRIVATE static void validate(JSCell*); |
| |
| void append(JSValue*); |
| void append(JSValue*, size_t count); |
| void append(JSCell**); |
| |
| void internalAppend(JSCell*); |
| void internalAppend(JSValue); |
| |
| JS_EXPORT_PRIVATE void mergeOpaqueRoots(); |
| |
| void mergeOpaqueRootsIfNecessary() |
| { |
| if (m_opaqueRoots.isEmpty()) |
| return; |
| mergeOpaqueRoots(); |
| } |
| |
| void mergeOpaqueRootsIfProfitable() |
| { |
| if (static_cast<unsigned>(m_opaqueRoots.size()) < Options::opaqueRootMergeThreshold) |
| return; |
| mergeOpaqueRoots(); |
| } |
| |
| MarkStackArray m_stack; |
| HashSet<void*> m_opaqueRoots; // Handle-owning data structures not visible to the garbage collector. |
| |
| #if !ASSERT_DISABLED |
| public: |
| bool m_isCheckingForDefaultMarkViolation; |
| bool m_isDraining; |
| #endif |
| protected: |
| friend class ParallelModeEnabler; |
| |
| size_t m_visitCount; |
| bool m_isInParallelMode; |
| |
| MarkStackThreadSharedData& m_shared; |
| }; |
| |
| inline MarkStack::MarkStack(MarkStackThreadSharedData& shared) |
| : m_stack(shared.m_segmentAllocator) |
| #if !ASSERT_DISABLED |
| , m_isCheckingForDefaultMarkViolation(false) |
| , m_isDraining(false) |
| #endif |
| , m_visitCount(0) |
| , m_isInParallelMode(false) |
| , m_shared(shared) |
| { |
| } |
| |
| inline MarkStack::~MarkStack() |
| { |
| ASSERT(m_stack.isEmpty()); |
| } |
| |
| inline void MarkStack::addOpaqueRoot(void* root) |
| { |
| #if ENABLE(PARALLEL_GC) |
| if (Options::numberOfGCMarkers == 1) { |
| // Put directly into the shared HashSet. |
| m_shared.m_opaqueRoots.add(root); |
| return; |
| } |
| // Put into the local set, but merge with the shared one every once in |
| // a while to make sure that the local sets don't grow too large. |
| mergeOpaqueRootsIfProfitable(); |
| m_opaqueRoots.add(root); |
| #else |
| m_opaqueRoots.add(root); |
| #endif |
| } |
| |
| inline bool MarkStack::containsOpaqueRoot(void* root) |
| { |
| ASSERT(!m_isInParallelMode); |
| #if ENABLE(PARALLEL_GC) |
| ASSERT(m_opaqueRoots.isEmpty()); |
| return m_shared.m_opaqueRoots.contains(root); |
| #else |
| return m_opaqueRoots.contains(root); |
| #endif |
| } |
| |
| inline int MarkStack::opaqueRootCount() |
| { |
| ASSERT(!m_isInParallelMode); |
| #if ENABLE(PARALLEL_GC) |
| ASSERT(m_opaqueRoots.isEmpty()); |
| return m_shared.m_opaqueRoots.size(); |
| #else |
| return m_opaqueRoots.size(); |
| #endif |
| } |
| |
| inline void MarkStackArray::append(const JSCell* cell) |
| { |
| if (m_top == m_segmentCapacity) |
| expand(); |
| m_topSegment->data()[postIncTop()] = cell; |
| } |
| |
| inline bool MarkStackArray::canRemoveLast() |
| { |
| return !!m_top; |
| } |
| |
| inline const JSCell* MarkStackArray::removeLast() |
| { |
| return m_topSegment->data()[preDecTop()]; |
| } |
| |
| inline bool MarkStackArray::isEmpty() |
| { |
| if (m_top) |
| return false; |
| if (m_topSegment->m_previous) { |
| ASSERT(m_topSegment->m_previous->m_top == m_segmentCapacity); |
| return false; |
| } |
| return true; |
| } |
| |
| inline bool MarkStackArray::canDonateSomeCells() |
| { |
| size_t numberOfCellsToKeep = Options::minimumNumberOfCellsToKeep; |
| // Another check: see if we have enough cells to warrant donation. |
| if (m_top <= numberOfCellsToKeep) { |
| // This indicates that we might not want to donate anything; check if we have |
| // another full segment. If not, then don't donate. |
| if (!m_topSegment->m_previous) |
| return false; |
| |
| ASSERT(m_topSegment->m_previous->m_top == m_segmentCapacity); |
| } |
| |
| return true; |
| } |
| |
| inline size_t MarkStackArray::size() |
| { |
| return m_top + m_segmentCapacity * m_numberOfPreviousSegments; |
| } |
| |
| ALWAYS_INLINE void MarkStack::append(JSValue* slot, size_t count) |
| { |
| for (size_t i = 0; i < count; ++i) { |
| JSValue& value = slot[i]; |
| if (!value) |
| continue; |
| internalAppend(value); |
| } |
| } |
| |
| template<typename T> |
| inline void MarkStack::appendUnbarrieredPointer(T** slot) |
| { |
| ASSERT(slot); |
| JSCell* cell = *slot; |
| if (cell) |
| internalAppend(cell); |
| } |
| |
| ALWAYS_INLINE void MarkStack::append(JSValue* slot) |
| { |
| ASSERT(slot); |
| internalAppend(*slot); |
| } |
| |
| ALWAYS_INLINE void MarkStack::append(JSCell** slot) |
| { |
| ASSERT(slot); |
| internalAppend(*slot); |
| } |
| |
| ALWAYS_INLINE void MarkStack::internalAppend(JSValue value) |
| { |
| ASSERT(value); |
| if (!value.isCell()) |
| return; |
| internalAppend(value.asCell()); |
| } |
| |
| class ParallelModeEnabler { |
| public: |
| ParallelModeEnabler(MarkStack& stack) |
| : m_stack(stack) |
| { |
| ASSERT(!m_stack.m_isInParallelMode); |
| m_stack.m_isInParallelMode = true; |
| } |
| |
| ~ParallelModeEnabler() |
| { |
| ASSERT(m_stack.m_isInParallelMode); |
| m_stack.m_isInParallelMode = false; |
| } |
| |
| private: |
| MarkStack& m_stack; |
| }; |
| |
| } // namespace JSC |
| |
| #endif |