Skip to content

Commit c123799

Browse files
committed
core: derive incubation controllers from tracked windows list
Replaces the attempts to track incubation controllers directly with a list of all known windows, then pulls the first usable incubation controller when an assignment is requested. This should finally fix incubation controller related use after free crashes.
1 parent 59f5744 commit c123799

File tree

3 files changed

+26
-101
lines changed

3 files changed

+26
-101
lines changed

src/core/generation.cpp

Lines changed: 20 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <qqmlengine.h>
1818
#include <qqmlerror.h>
1919
#include <qqmlincubator.h>
20+
#include <qquickwindow.h>
2021
#include <qtmetamacros.h>
2122

2223
#include "iconimageprovider.hpp"
@@ -242,90 +243,6 @@ void EngineGeneration::onDirectoryChanged() {
242243
}
243244
}
244245

245-
void EngineGeneration::registerIncubationController(QQmlIncubationController* controller) {
246-
// We only want controllers that we can swap out if destroyed.
247-
// This happens if the window owning the active controller dies.
248-
auto* obj = dynamic_cast<QObject*>(controller);
249-
if (!obj) {
250-
qCWarning(logIncubator) << "Could not register incubation controller as it is not a QObject"
251-
<< controller;
252-
253-
return;
254-
}
255-
256-
QObject::connect(
257-
obj,
258-
&QObject::destroyed,
259-
this,
260-
&EngineGeneration::incubationControllerDestroyed,
261-
Qt::UniqueConnection
262-
);
263-
264-
this->incubationControllers.push_back(obj);
265-
qCDebug(logIncubator) << "Registered incubation controller" << obj << "to generation" << this;
266-
267-
// This function can run during destruction.
268-
if (this->engine == nullptr) return;
269-
270-
if (this->engine->incubationController() == &this->delayedIncubationController) {
271-
this->assignIncubationController();
272-
}
273-
}
274-
275-
// Multiple controllers may be destroyed at once. Dynamic casts must be performed before working
276-
// with any controllers. The QQmlIncubationController destructor will already have run by the
277-
// point QObject::destroyed is called, so we can't cast to that.
278-
void EngineGeneration::deregisterIncubationController(QQmlIncubationController* controller) {
279-
auto* obj = dynamic_cast<QObject*>(controller);
280-
if (!obj) {
281-
qCCritical(logIncubator) << "Deregistering incubation controller which is not a QObject, "
282-
"however only QObject controllers should be registered.";
283-
}
284-
285-
QObject::disconnect(obj, nullptr, this, nullptr);
286-
287-
if (this->incubationControllers.removeOne(obj)) {
288-
qCDebug(logIncubator) << "Deregistered incubation controller" << obj << "from" << this;
289-
} else {
290-
qCCritical(logIncubator) << "Failed to deregister incubation controller" << obj << "from"
291-
<< this << "as it was not registered to begin with";
292-
qCCritical(logIncubator) << "Current registered incuabation controllers"
293-
<< this->incubationControllers;
294-
}
295-
296-
// This function can run during destruction.
297-
if (this->engine == nullptr) return;
298-
299-
if (this->engine->incubationController() == controller) {
300-
qCDebug(logIncubator
301-
) << "Destroyed incubation controller was currently active, reassigning from pool";
302-
this->assignIncubationController();
303-
}
304-
}
305-
306-
void EngineGeneration::incubationControllerDestroyed() {
307-
auto* sender = this->sender();
308-
309-
if (this->incubationControllers.removeAll(sender) != 0) {
310-
qCDebug(logIncubator) << "Destroyed incubation controller" << sender << "deregistered from"
311-
<< this;
312-
} else {
313-
qCCritical(logIncubator) << "Destroyed incubation controller" << sender
314-
<< "was not registered, but its destruction was observed by" << this;
315-
316-
return;
317-
}
318-
319-
// This function can run during destruction.
320-
if (this->engine == nullptr) return;
321-
322-
if (dynamic_cast<QObject*>(this->engine->incubationController()) == sender) {
323-
qCDebug(logIncubator
324-
) << "Destroyed incubation controller was currently active, reassigning from pool";
325-
this->assignIncubationController();
326-
}
327-
}
328-
329246
void EngineGeneration::onEngineWarnings(const QList<QQmlError>& warnings) {
330247
for (const auto& error: warnings) {
331248
const auto& url = error.url();
@@ -367,13 +284,27 @@ void EngineGeneration::exit(int code) {
367284
this->destroy();
368285
}
369286

287+
void EngineGeneration::trackWindowIncubationController(QQuickWindow* window) {
288+
if (this->trackedWindows.contains(window)) return;
289+
290+
QObject::connect(window, &QObject::destroyed, this, &EngineGeneration::onTrackedWindowDestroyed);
291+
this->trackedWindows.append(window);
292+
this->assignIncubationController();
293+
}
294+
295+
void EngineGeneration::onTrackedWindowDestroyed(QObject* object) {
296+
this->trackedWindows.removeAll(static_cast<QQuickWindow*>(object)); // NOLINT
297+
this->assignIncubationController();
298+
}
299+
370300
void EngineGeneration::assignIncubationController() {
371-
QQmlIncubationController* controller = nullptr;
301+
QQmlIncubationController* controller = &this->delayedIncubationController;
372302

373-
if (this->incubationControllersLocked || this->incubationControllers.isEmpty()) {
374-
controller = &this->delayedIncubationController;
375-
} else {
376-
controller = dynamic_cast<QQmlIncubationController*>(this->incubationControllers.first());
303+
for (auto* window: this->trackedWindows) {
304+
if (auto* wctl = window->incubationController()) {
305+
controller = wctl;
306+
break;
307+
}
377308
}
378309

379310
qCDebug(logIncubator) << "Assigning incubation controller" << controller << "to generation"

src/core/generation.hpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <qqmlengine.h>
1010
#include <qqmlerror.h>
1111
#include <qqmlincubator.h>
12+
#include <qquickwindow.h>
1213
#include <qtclasshelpermacros.h>
1314

1415
#include "incubator.hpp"
@@ -40,8 +41,7 @@ class EngineGeneration: public QObject {
4041
void setWatchingFiles(bool watching);
4142
bool setExtraWatchedFiles(const QVector<QString>& files);
4243

43-
void registerIncubationController(QQmlIncubationController* controller);
44-
void deregisterIncubationController(QQmlIncubationController* controller);
44+
void trackWindowIncubationController(QQuickWindow* window);
4545

4646
// takes ownership
4747
void registerExtension(const void* key, EngineGenerationExt* extension);
@@ -84,13 +84,13 @@ public slots:
8484
private slots:
8585
void onFileChanged(const QString& name);
8686
void onDirectoryChanged();
87-
void incubationControllerDestroyed();
87+
void onTrackedWindowDestroyed(QObject* object);
8888
static void onEngineWarnings(const QList<QQmlError>& warnings);
8989

9090
private:
9191
void postReload();
9292
void assignIncubationController();
93-
QVector<QObject*> incubationControllers;
93+
QVector<QQuickWindow*> trackedWindows;
9494
bool incubationControllersLocked = false;
9595
QHash<const void*, EngineGenerationExt*> extensions;
9696

src/window/proxywindow.cpp

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -153,13 +153,7 @@ void ProxyWindowBase::createWindow() {
153153

154154
void ProxyWindowBase::deleteWindow(bool keepItemOwnership) {
155155
if (this->window != nullptr) emit this->windowDestroyed();
156-
if (auto* window = this->disownWindow(keepItemOwnership)) {
157-
if (auto* generation = EngineGeneration::findObjectGeneration(this)) {
158-
generation->deregisterIncubationController(window->incubationController());
159-
}
160-
161-
window->deleteLater();
162-
}
156+
if (auto* window = this->disownWindow(keepItemOwnership)) window->deleteLater();
163157
}
164158

165159
ProxiedWindow* ProxyWindowBase::disownWindow(bool keepItemOwnership) {
@@ -185,7 +179,7 @@ void ProxyWindowBase::connectWindow() {
185179
if (auto* generation = EngineGeneration::findObjectGeneration(this)) {
186180
// All windows have effectively the same incubation controller so it dosen't matter
187181
// which window it belongs to. We do want to replace the delay one though.
188-
generation->registerIncubationController(this->window->incubationController());
182+
generation->trackWindowIncubationController(this->window);
189183
}
190184

191185
this->window->setProxy(this);

0 commit comments

Comments
 (0)