core/reloader: fix more crashes (not all of them)

This commit is contained in:
outfoxxed 2024-04-20 02:59:50 -07:00
parent 31462b9797
commit c6e5a35745
Signed by: outfoxxed
GPG key ID: 4C88A185FB89301E
4 changed files with 52 additions and 21 deletions

View file

@ -26,27 +26,46 @@ static QHash<QQmlEngine*, EngineGeneration*> g_generations; // NOLINT
EngineGeneration::EngineGeneration(QmlScanner scanner) EngineGeneration::EngineGeneration(QmlScanner scanner)
: scanner(std::move(scanner)) : scanner(std::move(scanner))
, interceptNetFactory(this->scanner.qmldirIntercepts) { , interceptNetFactory(this->scanner.qmldirIntercepts)
g_generations.insert(&this->engine, this); , engine(new QQmlEngine()) {
g_generations.insert(this->engine, this);
this->engine.addUrlInterceptor(&this->urlInterceptor); this->engine->addUrlInterceptor(&this->urlInterceptor);
this->engine.setNetworkAccessManagerFactory(&this->interceptNetFactory); this->engine->setNetworkAccessManagerFactory(&this->interceptNetFactory);
this->engine.setIncubationController(&this->delayedIncubationController); this->engine->setIncubationController(&this->delayedIncubationController);
this->engine.addImageProvider("icon", new IconImageProvider()); this->engine->addImageProvider("icon", new IconImageProvider());
QuickshellPlugin::runConstructGeneration(*this); QuickshellPlugin::runConstructGeneration(*this);
} }
EngineGeneration::~EngineGeneration() { EngineGeneration::~EngineGeneration() {
g_generations.remove(&this->engine); g_generations.remove(this->engine);
if (this->root != nullptr) this->root->deleteLater(); delete this->engine;
} }
void EngineGeneration::destroy() { void EngineGeneration::destroy() {
if (this->root != nullptr) { // Multiple generations can detect a reload at the same time.
delete this->watcher;
this->watcher = nullptr;
// Yes all of this is actually necessary.
if (this->engine != nullptr && this->root != nullptr) {
QObject::connect(this->root, &QObject::destroyed, this, [this]() { QObject::connect(this->root, &QObject::destroyed, this, [this]() {
delete this; // The timer seems to fix *one* of the possible qml item destructor crashes.
QTimer::singleShot(0, [this]() {
// Garbage is not collected during engine destruction.
this->engine->collectGarbage();
QObject::connect(this->engine, &QObject::destroyed, this, [this]() { delete this; });
// Even after all of that there's still multiple failing assertions and segfaults.
// Pray you don't hit one.
// Note: it appeats *some* of the crashes are related to values owned by the generation.
// Test by commenting the connect() above.
this->engine->deleteLater();
this->engine = nullptr;
});
}); });
this->root->deleteLater(); this->root->deleteLater();
@ -63,8 +82,8 @@ void EngineGeneration::onReload(EngineGeneration* old) {
} }
auto* app = QCoreApplication::instance(); auto* app = QCoreApplication::instance();
QObject::connect(&this->engine, &QQmlEngine::quit, app, &QCoreApplication::quit); QObject::connect(this->engine, &QQmlEngine::quit, app, &QCoreApplication::quit);
QObject::connect(&this->engine, &QQmlEngine::exit, app, &QCoreApplication::exit); QObject::connect(this->engine, &QQmlEngine::exit, app, &QCoreApplication::exit);
this->root->reload(old == nullptr ? nullptr : old->root); this->root->reload(old == nullptr ? nullptr : old->root);
this->singletonRegistry.onReload(old == nullptr ? nullptr : &old->singletonRegistry); this->singletonRegistry.onReload(old == nullptr ? nullptr : &old->singletonRegistry);
@ -72,14 +91,17 @@ void EngineGeneration::onReload(EngineGeneration* old) {
emit this->reloadFinished(); emit this->reloadFinished();
if (old != nullptr) { if (old != nullptr) {
old->destroy();
QObject::connect(old, &QObject::destroyed, this, [this]() { this->postReload(); }); QObject::connect(old, &QObject::destroyed, this, [this]() { this->postReload(); });
old->destroy();
} else { } else {
this->postReload(); this->postReload();
} }
} }
void EngineGeneration::postReload() { void EngineGeneration::postReload() {
// This can be called on a generation during its destruction.
if (this->engine == nullptr || this->root == nullptr) return;
QuickshellPlugin::runOnReload(); QuickshellPlugin::runOnReload();
PostReloadHook::postReloadTree(this->root); PostReloadHook::postReloadTree(this->root);
this->singletonRegistry.onPostReload(); this->singletonRegistry.onPostReload();
@ -132,7 +154,10 @@ void EngineGeneration::registerIncubationController(QQmlIncubationController* co
qCDebug(logIncubator) << "Registered incubation controller" << controller; qCDebug(logIncubator) << "Registered incubation controller" << controller;
if (this->engine.incubationController() == &this->delayedIncubationController) { // This function can run during destruction.
if (this->engine == nullptr) return;
if (this->engine->incubationController() == &this->delayedIncubationController) {
this->assignIncubationController(); this->assignIncubationController();
} }
} }
@ -156,7 +181,10 @@ void EngineGeneration::deregisterIncubationController(QQmlIncubationController*
qCDebug(logIncubator) << "Deregistered incubation controller" << controller; qCDebug(logIncubator) << "Deregistered incubation controller" << controller;
} }
if (this->engine.incubationController() == controller) { // This function can run during destruction.
if (this->engine == nullptr) return;
if (this->engine->incubationController() == controller) {
qCDebug(logIncubator qCDebug(logIncubator
) << "Destroyed incubation controller was currently active, reassigning from pool"; ) << "Destroyed incubation controller was currently active, reassigning from pool";
this->assignIncubationController(); this->assignIncubationController();
@ -183,7 +211,10 @@ void EngineGeneration::incubationControllerDestroyed() {
qCDebug(logIncubator) << "Destroyed incubation controller" << controller << "deregistered"; qCDebug(logIncubator) << "Destroyed incubation controller" << controller << "deregistered";
} }
if (this->engine.incubationController() == controller) { // This function can run during destruction.
if (this->engine == nullptr) return;
if (this->engine->incubationController() == controller) {
qCDebug(logIncubator qCDebug(logIncubator
) << "Destroyed incubation controller was currently active, reassigning from pool"; ) << "Destroyed incubation controller was currently active, reassigning from pool";
this->assignIncubationController(); this->assignIncubationController();
@ -198,7 +229,7 @@ void EngineGeneration::assignIncubationController() {
qCDebug(logIncubator) << "Assigning incubation controller to engine:" << controller qCDebug(logIncubator) << "Assigning incubation controller to engine:" << controller
<< "fallback:" << (controller == &this->delayedIncubationController); << "fallback:" << (controller == &this->delayedIncubationController);
this->engine.setIncubationController(controller); this->engine->setIncubationController(controller);
} }
EngineGeneration* EngineGeneration::findObjectGeneration(QObject* object) { EngineGeneration* EngineGeneration::findObjectGeneration(QObject* object) {

View file

@ -36,7 +36,7 @@ public:
QmlScanner scanner; QmlScanner scanner;
QsUrlInterceptor urlInterceptor; QsUrlInterceptor urlInterceptor;
QsInterceptNetworkAccessManagerFactory interceptNetFactory; QsInterceptNetworkAccessManagerFactory interceptNetFactory;
QQmlEngine engine; QQmlEngine* engine = nullptr;
ShellRoot* root = nullptr; ShellRoot* root = nullptr;
SingletonRegistry singletonRegistry; SingletonRegistry singletonRegistry;
QFileSystemWatcher* watcher = nullptr; QFileSystemWatcher* watcher = nullptr;

View file

@ -58,9 +58,9 @@ void RootWrapper::reloadGraph(bool hard) {
auto url = QUrl::fromLocalFile(this->rootPath); auto url = QUrl::fromLocalFile(this->rootPath);
// unless the original file comes from the qsintercept scheme // unless the original file comes from the qsintercept scheme
url.setScheme("qsintercept"); url.setScheme("qsintercept");
auto component = QQmlComponent(&generation->engine, url); auto component = QQmlComponent(generation->engine, url);
auto* obj = component.beginCreate(generation->engine.rootContext()); auto* obj = component.beginCreate(generation->engine->rootContext());
if (obj == nullptr) { if (obj == nullptr) {
qWarning() << component.errorString().toStdString().c_str(); qWarning() << component.errorString().toStdString().c_str();

View file

@ -6,7 +6,7 @@ namespace {
class SniPlugin: public QuickshellPlugin { class SniPlugin: public QuickshellPlugin {
void constructGeneration(EngineGeneration& generation) override { void constructGeneration(EngineGeneration& generation) override {
generation.engine.addImageProvider("service.sni", new qs::service::sni::TrayImageProvider()); generation.engine->addImageProvider("service.sni", new qs::service::sni::TrayImageProvider());
} }
}; };