aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/memory
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2017-03-09 10:36:16 +0100
committerLars Knoll <lars.knoll@qt.io>2017-04-07 06:06:17 +0000
commit589f8a90fa8c158ec97f32d4a9539b47ba8486a2 (patch)
treea379d6cc9d85b6565d712943d0fcf763466403e0 /src/qml/memory
parent1e63f7c4833c19f760f4af0b7650311819d0f2b2 (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.h6
-rw-r--r--src/qml/memory/qv4mm.cpp90
-rw-r--r--src/qml/memory/qv4mm_p.h9
-rw-r--r--src/qml/memory/qv4mmdefs_p.h22
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)