diff options
author | Lars Knoll <lars.knoll@qt.io> | 2017-03-09 10:36:16 +0100 |
---|---|---|
committer | Lars Knoll <lars.knoll@qt.io> | 2017-04-07 06:06:17 +0000 |
commit | 589f8a90fa8c158ec97f32d4a9539b47ba8486a2 (patch) | |
tree | a379d6cc9d85b6565d712943d0fcf763466403e0 /src/qml/memory | |
parent | 1e63f7c4833c19f760f4af0b7650311819d0f2b2 (diff) |
Separate the stack used for GC from the regular JS stack
This is required to be able to implement concurrent or
incremental garbage collection.
Change-Id: Ib3c5eee3779ca2ee08a57cd3961dbcb0537bbb54
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src/qml/memory')
-rw-r--r-- | src/qml/memory/qv4heap_p.h | 6 | ||||
-rw-r--r-- | src/qml/memory/qv4mm.cpp | 90 | ||||
-rw-r--r-- | src/qml/memory/qv4mm_p.h | 9 | ||||
-rw-r--r-- | src/qml/memory/qv4mmdefs_p.h | 22 |
4 files changed, 79 insertions, 48 deletions
diff --git a/src/qml/memory/qv4heap_p.h b/src/qml/memory/qv4heap_p.h index 6c7da91ba0..a38a938588 100644 --- a/src/qml/memory/qv4heap_p.h +++ b/src/qml/memory/qv4heap_p.h @@ -83,7 +83,7 @@ struct VTable uint type : 8; const char *className; void (*destroy)(Heap::Base *); - void (*markObjects)(Heap::Base *, ExecutionEngine *e); + void (*markObjects)(Heap::Base *, MarkStack *markStack); bool (*isEqualTo)(Managed *m, Managed *other); }; @@ -97,7 +97,7 @@ struct Q_QML_EXPORT Base { const VTable *vt; inline ReturnedValue asReturnedValue() const; - inline void mark(QV4::ExecutionEngine *engine); + inline void mark(QV4::MarkStack *markStack); void setVtable(const VTable *v) { vt = v; } const VTable *vtable() const { return vt; } @@ -127,7 +127,7 @@ struct Q_QML_EXPORT Base { return Chunk::testBit(c->objectBitmap, h - c->realBase()); } - inline void markChildren(ExecutionEngine *engine); + inline void markChildren(MarkStack *markStack); void *operator new(size_t, Managed *m) { return m; } void *operator new(size_t, Heap::Base *m) { return m; } diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index 28330a93c9..aebbc07826 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -260,10 +260,10 @@ void ChunkAllocator::free(Chunk *chunk, size_t size) } -void Heap::Base::markChildren(ExecutionEngine *engine) +void Heap::Base::markChildren(MarkStack *markStack) { if (vtable()->markObjects) - vtable()->markObjects(this, engine); + vtable()->markObjects(this, markStack); if (quint64 m = vtable()->markTable) { // qDebug() << "using mark table:" << hex << m << "for" << h; void **mem = reinterpret_cast<void **>(this); @@ -274,13 +274,13 @@ void Heap::Base::markChildren(ExecutionEngine *engine) break; case Mark_Value: // qDebug() << "marking value at " << mem; - reinterpret_cast<Value *>(mem)->mark(engine); + reinterpret_cast<Value *>(mem)->mark(markStack); break; case Mark_Pointer: { // qDebug() << "marking pointer at " << mem; Heap::Base *p = *reinterpret_cast<Heap::Base **>(mem); if (p) - p->mark(engine); + p->mark(markStack); break; } case Mark_ValueArray: { @@ -291,17 +291,20 @@ void Heap::Base::markChildren(ExecutionEngine *engine) const Value *end = v + a->alloc; if (a->alloc > 32*1024) { // drain from time to time to avoid overflows in the js stack - Value *currentBase = engine->jsStackTop; + Heap::Base **currentBase = markStack->top; while (v < end) { - v->mark(engine); + v->mark(markStack); ++v; - if (engine->jsStackTop >= currentBase + 32*1024) - engine->memoryManager->drainMarkStack(currentBase); + if (markStack->top >= currentBase + 32*1024) { + Heap::Base **oldBase = markStack->base; + markStack->base = currentBase; + markStack->drain(); + markStack->base = oldBase; + } } - } else { while (v < end) { - v->mark(engine); + v->mark(markStack); ++v; } } @@ -407,7 +410,7 @@ void Chunk::resetBlackBits() static uint nGrayItems = 0; #endif -void Chunk::collectGrayItems(ExecutionEngine *engine) +void Chunk::collectGrayItems(MarkStack *markStack) { // DEBUG << "sweeping chunk" << this << (*freeList); HeapItem *o = realBase(); @@ -428,7 +431,7 @@ void Chunk::collectGrayItems(ExecutionEngine *engine) HeapItem *itemToFree = o + index; Heap::Base *b = *itemToFree; Q_ASSERT(b->inUse()); - engine->pushForGC(b); + markStack->push(b); #ifdef MM_STATS ++nGrayItems; // qDebug() << "adding gray item" << b << "to mark stack"; @@ -676,10 +679,10 @@ void BlockAllocator::resetBlackBits() c->resetBlackBits(); } -void BlockAllocator::collectGrayItems(ExecutionEngine *engine) +void BlockAllocator::collectGrayItems(MarkStack *markStack) { for (auto c : chunks) - c->collectGrayItems(engine); + c->collectGrayItems(markStack); } @@ -750,7 +753,7 @@ void HugeItemAllocator::resetBlackBits() Chunk::clearBit(c.chunk->blackBitmap, c.chunk->first() - c.chunk->realBase()); } -void HugeItemAllocator::collectGrayItems(ExecutionEngine *engine) +void HugeItemAllocator::collectGrayItems(MarkStack *markStack) { for (auto c : chunks) // Correct for a Steele type barrier @@ -758,7 +761,7 @@ void HugeItemAllocator::collectGrayItems(ExecutionEngine *engine) Chunk::testBit(c.chunk->grayBitmap, c.chunk->first() - c.chunk->realBase())) { HeapItem *i = c.chunk->first(); Heap::Base *b = *i; - b->mark(engine); + b->mark(markStack); } } @@ -908,34 +911,35 @@ Heap::Object *MemoryManager::allocObjectWithMemberData(std::size_t size, uint nM static uint markStackSize = 0; -void MemoryManager::drainMarkStack(Value *markBase) +MarkStack::MarkStack(ExecutionEngine *engine) + : engine(engine) +{ + base = (Heap::Base **)engine->gcStack->base(); + top = base; + limit = base + ExecutionEngine::GCStackLimit/sizeof(Heap::Base)*3/4; +} + +void MarkStack::drain() { - while (engine->jsStackTop > markBase) { - Heap::Base *h = engine->popForGC(); + while (top > base) { + Heap::Base *h = pop(); ++markStackSize; Q_ASSERT(h); // at this point we should only have Heap::Base objects in this area on the stack. If not, weird things might happen. - h->markChildren(engine); + h->markChildren(this); } } -void MemoryManager::mark() +void MemoryManager::collectRoots(MarkStack *markStack) { - Value *markBase = engine->jsStackTop; - - markStackSize = 0; - -// qDebug() << ">>>> Mark phase:"; -// qDebug() << " mark stack after gray items" << (engine->jsStackTop - markBase); - if (!nextGCIsIncremental) - engine->markObjects(); + engine->markObjects(markStack); // qDebug() << " mark stack after engine->mark" << (engine->jsStackTop - markBase); - collectFromJSStack(); + collectFromJSStack(markStack); // qDebug() << " mark stack after js stack collect" << (engine->jsStackTop - markBase); - m_persistentValues->mark(engine); + m_persistentValues->mark(markStack); // qDebug() << " mark stack after persistants" << (engine->jsStackTop - markBase); @@ -964,19 +968,27 @@ void MemoryManager::mark() } if (keepAlive) - qobjectWrapper->mark(engine); + qobjectWrapper->mark(markStack); - if (engine->jsStackTop >= engine->jsStackLimit) - drainMarkStack(markBase); + if (markStack->top >= markStack->limit) + markStack->drain(); } +} + +void MemoryManager::mark() +{ + markStackSize = 0; + + MarkStack markStack(engine); + collectRoots(&markStack); if (nextGCIsIncremental) { // need to collect all gray items and push them onto the mark stack - blockAllocator.collectGrayItems(engine); - hugeItemAllocator.collectGrayItems(engine); + blockAllocator.collectGrayItems(&markStack); + hugeItemAllocator.collectGrayItems(&markStack); } - drainMarkStack(markBase); + markStack.drain(); } void MemoryManager::sweep(bool lastSweep) @@ -1242,7 +1254,7 @@ void MemoryManager::willAllocate(std::size_t size) #endif // DETAILED_MM_STATS -void MemoryManager::collectFromJSStack() const +void MemoryManager::collectFromJSStack(MarkStack *markStack) const { Value *v = engine->jsStackBase; Value *top = engine->jsStackTop; @@ -1250,7 +1262,7 @@ void MemoryManager::collectFromJSStack() const Managed *m = v->managed(); if (m && m->inUse()) // Skip pointers to already freed objects, they are bogus as well - m->mark(engine); + m->mark(markStack); ++v; } } diff --git a/src/qml/memory/qv4mm_p.h b/src/qml/memory/qv4mm_p.h index 7f02a4f929..1335ea59b6 100644 --- a/src/qml/memory/qv4mm_p.h +++ b/src/qml/memory/qv4mm_p.h @@ -156,7 +156,7 @@ struct BlockAllocator { void sweep(); void freeAll(); void resetBlackBits(); - void collectGrayItems(ExecutionEngine *engine); + void collectGrayItems(MarkStack *markStack); // bump allocations HeapItem *nextFree = 0; @@ -179,7 +179,7 @@ struct HugeItemAllocator { void sweep(); void freeAll(); void resetBlackBits(); - void collectGrayItems(ExecutionEngine *engine); + void collectGrayItems(MarkStack *markStack); size_t usedMem() const { size_t used = 0; @@ -432,8 +432,6 @@ public: // called when a JS object grows itself. Specifically: Heap::String::append void changeUnmanagedHeapSizeUsage(qptrdiff delta) { unmanagedHeapSize += delta; } - void drainMarkStack(Value *markBase); - protected: /// expects size to be aligned @@ -446,10 +444,11 @@ protected: #endif // DETAILED_MM_STATS private: - void collectFromJSStack() const; + void collectFromJSStack(MarkStack *markStack) const; void mark(); void sweep(bool lastSweep = false); bool shouldRunGC() const; + void collectRoots(MarkStack *markStack); public: QV4::ExecutionEngine *engine; diff --git a/src/qml/memory/qv4mmdefs_p.h b/src/qml/memory/qv4mmdefs_p.h index 1fc7b6a527..9512722782 100644 --- a/src/qml/memory/qv4mmdefs_p.h +++ b/src/qml/memory/qv4mmdefs_p.h @@ -59,6 +59,8 @@ QT_BEGIN_NAMESPACE namespace QV4 { +struct MarkStack; + /* * Chunks are the basic structure containing GC managed objects. * @@ -185,7 +187,7 @@ struct Chunk { void sweep(); void freeAll(); void resetBlackBits(); - void collectGrayItems(ExecutionEngine *engine); + void collectGrayItems(QV4::MarkStack *markStack); void sortIntoBins(HeapItem **bins, uint nBins); }; @@ -265,6 +267,24 @@ Q_STATIC_ASSERT(sizeof(HeapItem) == Chunk::SlotSize); Q_STATIC_ASSERT(QT_POINTER_SIZE*8 == Chunk::Bits); Q_STATIC_ASSERT((1 << Chunk::BitShift) == Chunk::Bits); +struct MarkStack {\ + MarkStack(ExecutionEngine *engine); + Heap::Base **top = 0; + Heap::Base **base = 0; + Heap::Base **limit = 0; + ExecutionEngine *engine; + void push(Heap::Base *m) { + *top = m; + ++top; + } + Heap::Base *pop() { + --top; + return *top; + } + void drain(); + +}; + // Base class for the execution engine #if defined(Q_CC_MSVC) || defined(Q_CC_GNU) |