diff options
author | Fabian Kosmale <fabian.kosmale@qt.io> | 2024-02-22 14:36:13 +0100 |
---|---|---|
committer | Fabian Kosmale <fabian.kosmale@qt.io> | 2024-03-05 14:06:29 +0100 |
commit | 61d7cf160f750bbef10c770997b839ee3e7c354e (patch) | |
tree | 262bf2902e8c24a893d9d30fe5042fc1f601a2c3 /src/qml/qml | |
parent | b871644b31a5fc1f410e0759f0b51f5418dc8f34 (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.cpp | 30 | ||||
-rw-r--r-- | src/qml/qml/qqmlobjectcreator_p.h | 21 |
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()); |