diff options
author | Fabian Kosmale <fabian.kosmale@qt.io> | 2023-11-21 19:36:26 +0100 |
---|---|---|
committer | Fabian Kosmale <fabian.kosmale@qt.io> | 2023-12-20 08:38:26 +0100 |
commit | b9d37a328ba09bcb2a7a95b5778cb8c63d0ace26 (patch) | |
tree | 340739253c3e15f0b6c051434d94061e8e3385c6 /src/qml/memory/qv4mm_p.h | |
parent | d08ede57dd530a67c3420b3858fe39bf1e5eb598 (diff) |
Long live incremental garbage collection in QML!
The design of the garbage collector is described in
src/qml/memory/design.md.
The gc and gcdone test helpers are adjusted to drive the gc to
completion, even when in incremental mode.
Parts of tst_qv4mm and tst_qqmlqt need to run with the incremental gc
disabled, as they call gc inside QML and assumes that the GC finishes
before returning.
Initial-patch-by: Rafal Chomentowski <rafal.chomentowski@ge.com>
Task-number: QTBUG-119274
Change-Id: I1d94f41bc7a434fad67de0fd46454b6db285f2eb
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Diffstat (limited to 'src/qml/memory/qv4mm_p.h')
-rw-r--r-- | src/qml/memory/qv4mm_p.h | 85 |
1 files changed, 83 insertions, 2 deletions
diff --git a/src/qml/memory/qv4mm_p.h b/src/qml/memory/qv4mm_p.h index 3a05bca536..9ed5b946b7 100644 --- a/src/qml/memory/qv4mm_p.h +++ b/src/qml/memory/qv4mm_p.h @@ -28,6 +28,68 @@ QT_BEGIN_NAMESPACE namespace QV4 { +enum GCState { + MarkStart = 0, + MarkGlobalObject, + MarkJSStack, + InitMarkPersistentValues, + MarkPersistentValues, + InitMarkWeakValues, + MarkWeakValues, + MarkDrain, + MarkReady, + Sweep, + Invalid, + Count, +}; + +struct GCData { virtual ~GCData(){};}; + +struct GCIteratorStorage { + PersistentValueStorage::Iterator it{nullptr, 0}; +}; +struct GCStateMachine; + +struct GCStateInfo { + using ExtraData = std::variant<std::monostate, GCIteratorStorage>; + GCState (*execute)(GCStateMachine *, ExtraData &) = nullptr; // Function to execute for this state, returns true if ready to transition + bool breakAfter{false}; +}; + +struct GCStateMachine { + using ExtraData = GCStateInfo::ExtraData; + GCState state{GCState::Invalid}; + std::chrono::microseconds timeLimit{}; + QDeadlineTimer deadline; + std::array<GCStateInfo, GCState::Count> stateInfoMap; + MemoryManager *mm = nullptr; + ExtraData stateData; // extra date for specific states + + GCStateMachine(); + + inline void step() { + if (!inProgress()) { + reset(); + } + transition(); + } + + inline bool inProgress() { + return state != GCState::Invalid; + } + + inline void reset() { + state = GCState::MarkStart; + } + + Q_QML_EXPORT void transition(); + + inline void handleTimeout(GCState state) { + Q_UNUSED(state); + } +}; + + struct ChunkAllocator; struct MemorySegment; @@ -258,12 +320,24 @@ public: typename ManagedType::Data *allocIC() { Heap::Base *b = *allocate(&icAllocator, align(sizeof(typename ManagedType::Data))); + if (m_markStack) { + // If the gc is running right now, it will not have a chance to mark the newly created item + // and may therefore sweep it right away. + // Protect the new object from the current GC run to avoid this. + b->setMarkBit(); + } return static_cast<typename ManagedType::Data *>(b); } void registerWeakMap(Heap::MapObject *map); void registerWeakSet(Heap::SetObject *set); + void onEventLoop(); + + //GC related methods + void setGCTimeLimit(int timeMs); + MarkStack* markStack() { return m_markStack.get(); } + protected: /// expects size to be aligned Heap::Base *allocString(std::size_t unmanagedSize); @@ -275,14 +349,18 @@ private: MinUnmanagedHeapSizeGCLimit = 128 * 1024 }; +public: void collectFromJSStack(MarkStack *markStack) const; - void mark(); void sweep(bool lastSweep = false, ClassDestroyStatsCallback classCountPtr = nullptr); +private: bool shouldRunGC() const; - void collectRoots(MarkStack *markStack); HeapItem *allocate(BlockAllocator *allocator, std::size_t size) { + // We must not call runGC if incremental gc is running + // so temporarily set gcBlocked in that case, too + QBoolBlocker block(gcBlocked, m_markStack != nullptr || gcBlocked); + bool didGCRun = false; if (aggressiveGC) { runGC(); @@ -329,6 +407,9 @@ public: Heap::MapObject *weakMaps = nullptr; Heap::SetObject *weakSets = nullptr; + std::unique_ptr<GCStateMachine> gcStateMachine{nullptr}; + std::unique_ptr<MarkStack> m_markStack{nullptr}; + std::size_t unmanagedHeapSize = 0; // the amount of bytes of heap that is not managed by the memory manager, but which is held onto by managed items. std::size_t unmanagedHeapSizeGCLimit; std::size_t usedSlotsAfterLastFullSweep = 0; |