ProjectExplorer: Make subscriptions more robust

Make sure not to continue to connect after the receiver was destructed
and make sure to disconnect from everything when that happens.

Task-number: QTCREATORBUG-19391
Change-Id: I4d09a7dca2a5260c3d4744607dccbde5964a0623
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
Tobias Hunger
2017-12-07 13:10:36 +01:00
parent 1ddee69200
commit 7df3bff07a
2 changed files with 44 additions and 14 deletions

View File

@@ -30,6 +30,7 @@
#include "target.h" #include "target.h"
#include <utils/asconst.h> #include <utils/asconst.h>
#include <utils/qtcassert.h>
namespace ProjectExplorer { namespace ProjectExplorer {
namespace Internal { namespace Internal {
@@ -37,42 +38,42 @@ namespace Internal {
Subscription::Subscription(const Subscription::Connector &s, const QObject *receiver, QObject *parent) : Subscription::Subscription(const Subscription::Connector &s, const QObject *receiver, QObject *parent) :
QObject(parent), m_subscriber(s) QObject(parent), m_subscriber(s)
{ {
if (receiver != parent) if (receiver != parent) {
connect(receiver, &QObject::destroyed, this, &QObject::deleteLater); connect(receiver, &QObject::destroyed, this, [this]() {
unsubscribeAll();
m_subscriber = Connector(); // Reset subscriber
deleteLater();
});
}
} }
Subscription::~Subscription() Subscription::~Subscription()
{ {
for (const auto &c : Utils::asConst(m_connections)) unsubscribeAll();
disconnect(c);
} }
void Subscription::subscribe(ProjectConfiguration *pc) void Subscription::subscribe(ProjectConfiguration *pc)
{ {
if (!m_subscriber) if (!m_subscriber)
return; return;
QMetaObject::Connection conn = m_subscriber(pc);
if (conn) connectTo(pc);
m_connections.insert(pc, conn);
if (auto p = qobject_cast<Project *>(pc)) { if (auto p = qobject_cast<Project *>(pc)) {
for (Target *t : p->targets()) { for (Target *t : p->targets()) {
for (ProjectConfiguration *pc : t->projectConfigurations()) for (ProjectConfiguration *pc : t->projectConfigurations())
m_subscriber(pc); connectTo(pc);
} }
} else if (auto t = qobject_cast<Target *>(pc)) { } else if (auto t = qobject_cast<Target *>(pc)) {
for (ProjectConfiguration *pc : t->projectConfigurations()) for (ProjectConfiguration *pc : t->projectConfigurations())
m_subscriber(pc); connectTo(pc);
} }
} }
void Subscription::unsubscribe(ProjectConfiguration *pc) void Subscription::unsubscribe(ProjectConfiguration *pc)
{ {
auto c = m_connections.value(pc); disconnectFrom(pc);
if (c) {
disconnect(c);
m_connections.remove(pc);
}
if (auto p = qobject_cast<Project *>(pc)) { if (auto p = qobject_cast<Project *>(pc)) {
for (Target *t : p->targets()) { for (Target *t : p->targets()) {
for (ProjectConfiguration *pc : t->projectConfigurations()) for (ProjectConfiguration *pc : t->projectConfigurations())
@@ -82,7 +83,32 @@ void Subscription::unsubscribe(ProjectConfiguration *pc)
for (ProjectConfiguration *pc : t->projectConfigurations()) for (ProjectConfiguration *pc : t->projectConfigurations())
unsubscribe(pc); unsubscribe(pc);
} }
}
void Subscription::unsubscribeAll()
{
for (const auto &c : Utils::asConst(m_connections))
disconnect(c);
m_connections.clear();
}
void Subscription::connectTo(ProjectConfiguration *pc)
{
QTC_ASSERT(!m_connections.contains(pc), return);
QMetaObject::Connection conn = m_subscriber(pc);
if (conn)
m_connections.insert(pc, conn);
}
void Subscription::disconnectFrom(ProjectConfiguration *pc)
{
auto c = m_connections.value(pc);
if (!c)
return;
disconnect(c);
m_connections.remove(pc);
} }
ProjectSubscription::ProjectSubscription(const Subscription::Connector &s, const QObject *r, ProjectSubscription::ProjectSubscription(const Subscription::Connector &s, const QObject *r,

View File

@@ -53,6 +53,10 @@ protected:
void subscribe(ProjectConfiguration *pc); void subscribe(ProjectConfiguration *pc);
void unsubscribe(ProjectConfiguration *pc); void unsubscribe(ProjectConfiguration *pc);
void unsubscribeAll();
void connectTo(ProjectConfiguration *pc);
void disconnectFrom(ProjectConfiguration *pc);
Connector m_subscriber; Connector m_subscriber;
QHash<ProjectConfiguration *, QMetaObject::Connection> m_connections; QHash<ProjectConfiguration *, QMetaObject::Connection> m_connections;
}; };