From 61d7cf160f750bbef10c770997b839ee3e7c354e Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Thu, 22 Feb 2024 14:36:13 +0100 Subject: 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 --- src/qml/qml/qqmlobjectcreator.cpp | 30 +++++++++++++++++++++--------- src/qml/qml/qqmlobjectcreator_p.h | 21 ++++++++++++++++++--- 2 files changed, 39 insertions(+), 12 deletions(-) (limited to 'src/qml/qml') 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 QQmlRefPointerrootContext = 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(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 { QQmlRefPointer rootContext; @@ -91,7 +105,7 @@ struct QQmlObjectCreatorSharedState final : QQmlRefCounted allCreatedBindings; QFiniteStack allParserStatusCallbacks; QFiniteStack > 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 finalizeHooks; QQmlVmeProfiler profiler; @@ -254,8 +268,9 @@ private: qt_ptr_swap(_scopeObject, scopeObject); QV4::Scope valueScope(v4); - QScopedValueRollback jsObjectGuard(sharedState->allJavaScriptObjects, - valueScope.alloc(compilationUnit->totalObjectCount())); + QScopedValueRollback jsObjectGuard( + sharedState->allJavaScriptObjects, + ObjectInCreationGCAnchorList(valueScope, compilationUnit->totalObjectCount())); Q_ASSERT(topLevelCreator); QV4::QmlContext *qmlContext = static_cast(valueScope.alloc()); -- cgit v1.2.3