aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml
diff options
context:
space:
mode:
authorFabian Kosmale <fabian.kosmale@qt.io>2024-02-22 14:36:13 +0100
committerFabian Kosmale <fabian.kosmale@qt.io>2024-03-05 14:06:29 +0100
commit61d7cf160f750bbef10c770997b839ee3e7c354e (patch)
tree262bf2902e8c24a893d9d30fe5042fc1f601a2c3 /src/qml/qml
parentb871644b31a5fc1f410e0759f0b51f5418dc8f34 (diff)
Prepare for white allocations during gc(8/9): Object creator
Modify the object creator's tracking of object wrappers. It already pushed them onto the JS stack, so that triggering the gc during object creation would not trigger the wrappers of (temporarily) unparented objects. However, with the incremental gc, this is not enough: We might have already started the gc, and are beyond the phase were we visit all QObjectWrappers. If we then resume the collector only after the object cretaor is done (and the scope has been destroyed), then we don't have any reference to the QObjectWrappers. Fix this by moving the wrapper handling into a helper class, which employs a write barrier to avoid the objects from being hidden from the gc. This unconvered an unrelated issue in the usage of QV4::Scope in QQmlObjectCreator::beginPopulateDeferred, which will be addressed later. Task-number: QTBUG-122956 Change-Id: I23755438e68aa1c82786e619105683d131c31c90 Reviewed-by: Semih Yavuz <semih.yavuz@qt.io>
Diffstat (limited to 'src/qml/qml')
-rw-r--r--src/qml/qml/qqmlobjectcreator.cpp30
-rw-r--r--src/qml/qml/qqmlobjectcreator_p.h21
2 files changed, 39 insertions, 12 deletions
diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp
index 3d4d3596cc..713162a6c7 100644
--- a/src/qml/qml/qqmlobjectcreator.cpp
+++ b/src/qml/qml/qqmlobjectcreator.cpp
@@ -70,7 +70,7 @@ QQmlObjectCreator::QQmlObjectCreator(
sharedState->allCreatedBindings.allocate(compilationUnit->totalBindingsCount());
sharedState->allParserStatusCallbacks.allocate(compilationUnit->totalParserStatusCount());
sharedState->allCreatedObjects.allocate(compilationUnit->totalObjectCount());
- sharedState->allJavaScriptObjects = nullptr;
+ sharedState->allJavaScriptObjects = ObjectInCreationGCAnchorList();
sharedState->creationContext = creationContext;
sharedState->rootContext.reset();
sharedState->hadTopLevelRequiredProperties = false;
@@ -189,9 +189,9 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI
QV4::Scope scope(v4);
- Q_ASSERT(sharedState->allJavaScriptObjects || topLevelCreator);
+ Q_ASSERT(sharedState->allJavaScriptObjects.canTrack() || topLevelCreator);
if (topLevelCreator)
- sharedState->allJavaScriptObjects = scope.alloc(compilationUnit->totalObjectCount());
+ sharedState->allJavaScriptObjects = ObjectInCreationGCAnchorList(scope, compilationUnit->totalObjectCount());
if (!isComponentRoot && sharedState->creationContext) {
// otherwise QQmlEnginePrivate::createInternalContext() handles it
@@ -206,7 +206,7 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI
}
if (topLevelCreator)
- sharedState->allJavaScriptObjects = nullptr;
+ sharedState->allJavaScriptObjects = ObjectInCreationGCAnchorList();
phase = CreatingObjectsPhase2;
@@ -235,10 +235,11 @@ void QQmlObjectCreator::beginPopulateDeferred(const QQmlRefPointer<QQmlContextDa
sharedState->rootContext = newContext;
Q_ASSERT(topLevelCreator);
- Q_ASSERT(!sharedState->allJavaScriptObjects);
+ Q_ASSERT(!sharedState->allJavaScriptObjects.canTrack());
+ // FIXME (QTBUG-122956): allocating from the short lived scope does not make any sense
QV4::Scope valueScope(v4);
- sharedState->allJavaScriptObjects = valueScope.alloc(compilationUnit->totalObjectCount());
+ sharedState->allJavaScriptObjects = ObjectInCreationGCAnchorList(valueScope, compilationUnit->totalObjectCount());
}
void QQmlObjectCreator::populateDeferred(QObject *instance, int deferredIndex,
@@ -1406,9 +1407,8 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
QObject *scopeObject = instance;
qSwap(_scopeObject, scopeObject);
- Q_ASSERT(sharedState->allJavaScriptObjects);
- *sharedState->allJavaScriptObjects = QV4::QObjectWrapper::wrap(v4, instance);
- ++sharedState->allJavaScriptObjects;
+ Q_ASSERT(sharedState->allJavaScriptObjects.canTrack());
+ sharedState->allJavaScriptObjects.trackObject(v4, instance);
QV4::Scope valueScope(v4);
QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc());
@@ -1809,3 +1809,15 @@ QQmlObjectCreatorRecursionWatcher::QQmlObjectCreatorRecursionWatcher(QQmlObjectC
, watcher(creator->sharedState.data())
{
}
+
+void ObjectInCreationGCAnchorList::trackObject(QV4::ExecutionEngine *engine, QObject *instance)
+{
+ *allJavaScriptObjects = QV4::QObjectWrapper::wrap(engine, instance);
+ // we have to handle the case where the gc is already running, but the scope is discarded
+ // before the collector runs again. In that case, rescanning won't help us. Thus, mark the
+ // object.
+ QV4::WriteBarrier::markCustom(engine, [this](QV4::MarkStack *ms) {
+ allJavaScriptObjects->heapObject()->mark(ms);
+ });
+ ++allJavaScriptObjects;
+}
diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h
index 733cfef3f2..eed1dceb3f 100644
--- a/src/qml/qml/qqmlobjectcreator_p.h
+++ b/src/qml/qml/qqmlobjectcreator_p.h
@@ -84,6 +84,20 @@ struct DeferredQPropertyBinding {
QUntypedPropertyBinding binding;
};
+class ObjectInCreationGCAnchorList {
+public:
+ // this is a non owning view, rule of zero applies
+ ObjectInCreationGCAnchorList() = default;
+ ObjectInCreationGCAnchorList(const QV4::Scope &scope, int totalObjectCount)
+ {
+ allJavaScriptObjects = scope.alloc(totalObjectCount);
+ }
+ void trackObject(QV4::ExecutionEngine *engine, QObject *instance);
+ bool canTrack() const { return allJavaScriptObjects; }
+private:
+ QV4::Value *allJavaScriptObjects = nullptr; // pointer to vector on JS stack to reference JS wrappers during creation phase.
+};
+
struct QQmlObjectCreatorSharedState final : QQmlRefCounted<QQmlObjectCreatorSharedState>
{
QQmlRefPointer<QQmlContextData> rootContext;
@@ -91,7 +105,7 @@ struct QQmlObjectCreatorSharedState final : QQmlRefCounted<QQmlObjectCreatorShar
QFiniteStack<QQmlAbstractBinding::Ptr> allCreatedBindings;
QFiniteStack<QQmlParserStatus*> allParserStatusCallbacks;
QFiniteStack<QQmlGuard<QObject> > allCreatedObjects;
- QV4::Value *allJavaScriptObjects; // pointer to vector on JS stack to reference JS wrappers during creation phase.
+ ObjectInCreationGCAnchorList allJavaScriptObjects; // pointer to vector on JS stack to reference JS wrappers during creation phase.
QQmlComponentAttached *componentAttached;
QList<QQmlFinalizerHook *> finalizeHooks;
QQmlVmeProfiler profiler;
@@ -254,8 +268,9 @@ private:
qt_ptr_swap(_scopeObject, scopeObject);
QV4::Scope valueScope(v4);
- QScopedValueRollback<QV4::Value*> jsObjectGuard(sharedState->allJavaScriptObjects,
- valueScope.alloc(compilationUnit->totalObjectCount()));
+ QScopedValueRollback<ObjectInCreationGCAnchorList> jsObjectGuard(
+ sharedState->allJavaScriptObjects,
+ ObjectInCreationGCAnchorList(valueScope, compilationUnit->totalObjectCount()));
Q_ASSERT(topLevelCreator);
QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc());