diff options
| author | Fabian Kosmale <fabian.kosmale@qt.io> | 2024-09-07 22:51:58 +0200 |
|---|---|---|
| committer | Ulf Hermann <ulf.hermann@qt.io> | 2024-09-10 08:30:44 +0000 |
| commit | 75603bfe1af69ca8a9ce45ee37dee722be8b800e (patch) | |
| tree | e456ab0177f685323808d63fca1e389eff2cc52c | |
| parent | 08f085918ce517199a6e53790a4d3f61fa29b278 (diff) | |
Fix gc interaction when reparenting items
QQuickItem has sspecial integration code with the garbage collector, to
ensure that an item which is in the (item) parent hierarchy will not get
collected. However, that breaks down with incremental gc in one specific
corner case:
1. We have an item A which already got fully marked (its custom
markObjects has already run). The phase in which we mark all object
wrappers has however not ended yet.
2. We have another item B with child item C, and both of their wrappers
are not marked yet.
3. The itme parent of C changes from B to A.
4. The gc resumes, but won't ever reach C, because it is only reachable
from A, which already had been visited.
Note that there is an issue in loadFromModule not correctly setting up
the context, which requires a workaround in the test.
Pick-to: 6.8
Change-Id: Id8d14cd1ea96161d2864f49b43a9369b2f363396
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
| -rw-r--r-- | src/quick/items/qquickitem.cpp | 7 | ||||
| -rw-r--r-- | tests/auto/quick/qquickitem2/tst_qquickitem.cpp | 46 |
2 files changed, 53 insertions, 0 deletions
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index e736a8ddd6..d3d9fd0850 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -2731,6 +2731,13 @@ void QQuickItem::setParentItem(QQuickItem *parentItem) } itemAncestor = itemAncestor->parentItem(); } + auto engine = qmlEngine(this); + if (engine) { + QV4::ExecutionEngine *v4 = engine->handle(); + QV4::WriteBarrier::markCustom(v4, [this](QV4::MarkStack *ms){ + QV4::QObjectWrapper::markWrapper(this, ms); + }); + } } d->removeFromDirtyList(); diff --git a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp index 5ab330224f..eef2bfcedd 100644 --- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp +++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp @@ -17,6 +17,7 @@ #include <QtQuick/private/qquickanchors_p.h> #include <QtGui/qstylehints.h> #include <private/qquickitem_p.h> +#include <private/qv4qobjectwrapper_p.h> #include <QtQuickTest/QtQuickTest> #include <QtQuickTestUtils/private/qmlutils_p.h> #include <QtQuickTestUtils/private/visualtestutils_p.h> @@ -143,6 +144,7 @@ private slots: void embeddedInWidgetsFocus_data(); void embeddedInWidgetsFocus(); #endif + void gcIntegration(); private: QQmlEngine engine; @@ -4513,6 +4515,50 @@ void tst_QQuickItem::embeddedInWidgetsFocus() } #endif +void tst_QQuickItem::gcIntegration() +{ + QQmlEngine e; + QQmlComponent comp(&e); + QQuickItem *parentItem, *child; + comp.loadFromModule("QtQuick", "Item"); + parentItem = qobject_cast<QQuickItem *>(comp.create()); + child = qobject_cast<QQuickItem *>(comp.create()); + QVERIFY(parentItem); + QVERIFY(child); + QV4::ExecutionEngine *v4 = e.handle(); + QV4::QObjectWrapper::ensureWrapper(v4, child); + QV4::QObjectWrapper::ensureWrapper(v4, parentItem); + // line blow is a hack; loadFromModule ought to set that up... + QQmlData::get(child)->context = QQmlContextData::get(e.rootContext()).data(); + QPointer<QObject> observer(child); + QJSEngine::setObjectOwnership(parentItem, QJSEngine::JavaScriptOwnership); + QJSEngine::setObjectOwnership(child, QJSEngine::JavaScriptOwnership); + QVERIFY(!QQmlData::keepAliveDuringGarbageCollection(child)); + QVERIFY(!QQmlData::keepAliveDuringGarbageCollection(parentItem)); + + QV4::MemoryManager *mm = v4->memoryManager; + + QV4::GCStateMachine *sm = mm->gcStateMachine.get(); + sm->reset(); + while (sm->state != QV4::GCState::MarkGlobalObject) { + QV4::GCStateInfo& stateInfo = sm->stateInfoMap[int(sm->state)]; + sm->state = stateInfo.execute(sm, sm->stateData); + } + + // simulate someone owning the parentItem + QV4::QObjectWrapper::markWrapper(parentItem, mm->markStack()); + mm->markStack()->drain(); + + child->setParentItem(parentItem); + child->setParent(nullptr); + gc(*v4); + QVERIFY(observer); + child->setParentItem(nullptr); + QVERIFY(!child->parent()); + gc(*v4); + QVERIFY(!observer); +} + QTEST_MAIN(tst_QQuickItem) #include "tst_qquickitem.moc" |
