summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJarek Kobus <jaroslaw.kobus@qt.io>2021-09-17 16:32:50 +0200
committerJarek Kobus <jaroslaw.kobus@qt.io>2021-09-28 10:25:29 +0000
commit7958de05f5837b578490c667b255b2bad2166af5 (patch)
tree8101aa3b465f0132d2618f2fae1e20ebe6afb4ca
parent03f6de1eeb26a9b88df250f136cb6f90216ac68d (diff)
downloadqt-creator-7958de05f5837b578490c667b255b2bad2166af5.tar.gz
Introduce Utils::Singleton
Introduce Utils::Singleton class and Utils::SingletonWithOptionalDependencies class template that helps implementing singletons that depend on other singletons. It's guaranteed that whenever singleton B depends on singleton A, than A is always created before B is being created, and that the order of destruction is always opposite to the order of creation. Dependencies of singleton are listed as template arguments for SingletonWithOptionalDependencies class template. The first argument of SingletonWithOptionalDependencies class template is always a singleton class itself. Prepare a common interface for all singleton subclasses: SingletonSubClass *SingletonWithOptionalDependencies::instance(); Make instantiating singletons and its dependencies thread-safe. Create singletons on demand (only if some code needs them). It's not needed anymore to explicitly instantiate all required singletons in tests. Make it possible (and thread-safe) to instantiate ProcessReaper and LauncherInterface singletons in non-main threads. Make the following dependencies between existing singletons: SshConnectionManager depends on: -> LauncherInterface depends on: -> ProcessReaper Change-Id: Iefaacab561c2b3dcf07e7fafbb87339ea6a15278 Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
-rw-r--r--src/app/CMakeLists.txt2
-rw-r--r--src/app/app.pro2
-rw-r--r--src/app/app.qbs1
-rw-r--r--src/app/main.cpp10
-rw-r--r--src/libs/ssh/sshconnectionmanager.cpp19
-rw-r--r--src/libs/ssh/sshconnectionmanager.h12
-rw-r--r--src/libs/utils/CMakeLists.txt1
-rw-r--r--src/libs/utils/launcherinterface.cpp65
-rw-r--r--src/libs/utils/launcherinterface.h16
-rw-r--r--src/libs/utils/processreaper.cpp17
-rw-r--r--src/libs/utils/processreaper.h9
-rw-r--r--src/libs/utils/singleton.cpp82
-rw-r--r--src/libs/utils/singleton.h106
-rw-r--r--src/libs/utils/utils-lib.pri4
-rw-r--r--src/libs/utils/utils.qbs2
-rw-r--r--src/plugins/mesonprojectmanager/tests/testmesoninfoparser.cpp10
-rw-r--r--src/plugins/mesonprojectmanager/tests/testmesonwrapper.cpp10
-rw-r--r--src/tools/processlauncher/CMakeLists.txt2
-rw-r--r--src/tools/processlauncher/processlauncher-main.cpp6
-rw-r--r--src/tools/processlauncher/processlauncher.pro8
-rw-r--r--src/tools/processlauncher/processlauncher.qbs2
-rw-r--r--tests/auto/ssh/tst_ssh.cpp16
-rw-r--r--tests/auto/utils/qtcprocess/tst_qtcprocess.cpp9
-rw-r--r--tests/unit/unittest/unittests-main.cpp9
24 files changed, 282 insertions, 138 deletions
diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt
index 236421558c..ed41f3dce0 100644
--- a/src/app/CMakeLists.txt
+++ b/src/app/CMakeLists.txt
@@ -12,7 +12,7 @@ install(
add_qtc_executable(qtcreator
DEFINES IDE_LIBRARY_BASENAME=\"${IDE_LIBRARY_BASE_PATH}\"
- DEPENDS Aggregation ExtensionSystem Qt5::Core Qt5::Widgets Utils QtcSsh shared_qtsingleapplication app_version
+ DEPENDS Aggregation ExtensionSystem Qt5::Core Qt5::Widgets Utils shared_qtsingleapplication app_version
SOURCES
main.cpp
../tools/qtcreatorcrashhandler/crashhandlersetup.cpp ../tools/qtcreatorcrashhandler/crashhandlersetup.h
diff --git a/src/app/app.pro b/src/app/app.pro
index 1d15d390c3..730427ba89 100644
--- a/src/app/app.pro
+++ b/src/app/app.pro
@@ -17,7 +17,7 @@ include(../rpath.pri)
include(../libs/qt-breakpad/qtbreakpad.pri)
-LIBS *= -l$$qtLibraryName(ExtensionSystem) -l$$qtLibraryName(Aggregation) -l$$qtLibraryName(Utils) -l$$qtLibraryName(QtcSsh)
+LIBS *= -l$$qtLibraryName(ExtensionSystem) -l$$qtLibraryName(Aggregation) -l$$qtLibraryName(Utils)
win32 {
# We need the version in two separate formats for the .rc file
diff --git a/src/app/app.qbs b/src/app/app.qbs
index c97a645727..42172aa09f 100644
--- a/src/app/app.qbs
+++ b/src/app/app.qbs
@@ -46,7 +46,6 @@ QtcProduct {
Depends { name: "app_version_header" }
Depends { name: "Qt"; submodules: ["widgets", "network"] }
Depends { name: "Utils" }
- Depends { name: "QtcSsh" }
Depends { name: "ExtensionSystem" }
files: [
diff --git a/src/app/main.cpp b/src/app/main.cpp
index bc0d72aeac..8ab587a713 100644
--- a/src/app/main.cpp
+++ b/src/app/main.cpp
@@ -32,16 +32,13 @@
#include <extensionsystem/pluginspec.h>
#include <qtsingleapplication.h>
-#include <ssh/sshconnectionmanager.h>
-
#include <utils/algorithm.h>
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
-#include <utils/launcherinterface.h>
#include <utils/optional.h>
-#include <utils/processreaper.h>
#include <utils/qtcsettings.h>
+#include <utils/singleton.h>
#include <utils/temporarydirectory.h>
#include <QDebug>
@@ -537,10 +534,7 @@ int main(int argc, char **argv)
QCoreApplication::setOrganizationName(QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR));
QGuiApplication::setApplicationDisplayName(Core::Constants::IDE_DISPLAY_NAME);
- Utils::ProcessReaper processReaper;
- Utils::LauncherInterface::startLauncher();
- auto cleanup = qScopeGuard([] { Utils::LauncherInterface::stopLauncher(); });
- QSsh::SshConnectionManager sshConnectionManager;
+ auto cleanup = qScopeGuard([] { Utils::Singleton::deleteAll(); });
const QStringList pluginArguments = app.arguments();
diff --git a/src/libs/ssh/sshconnectionmanager.cpp b/src/libs/ssh/sshconnectionmanager.cpp
index 59ddb78f2a..3ac82a7dab 100644
--- a/src/libs/ssh/sshconnectionmanager.cpp
+++ b/src/libs/ssh/sshconnectionmanager.cpp
@@ -53,16 +53,11 @@ bool operator!=(const UnacquiredConnection &c1, const UnacquiredConnection &c2)
return !(c1 == c2);
}
-static class SshConnectionManagerPrivate *s_instance = nullptr;
-
class SshConnectionManagerPrivate : public QObject
{
public:
SshConnectionManagerPrivate()
{
- QTC_ASSERT(s_instance == nullptr, return);
- s_instance = this;
-
connect(&m_removalTimer, &QTimer::timeout,
this, &SshConnectionManagerPrivate::removeInactiveConnections);
m_removalTimer.start(SshSettings::connectionSharingTimeout() * 1000 * 60 / 2);
@@ -70,8 +65,6 @@ public:
~SshConnectionManagerPrivate() override
{
- QTC_ASSERT(s_instance == this, return);
-
for (const UnacquiredConnection &connection : qAsConst(m_unacquiredConnections)) {
disconnect(connection.connection, nullptr, this, nullptr);
delete connection.connection;
@@ -79,8 +72,6 @@ public:
QTC_CHECK(m_acquiredConnections.isEmpty());
QTC_CHECK(m_deprecatedConnections.isEmpty());
-
- s_instance = nullptr;
}
SshConnection *acquireConnection(const SshConnectionParameters &sshParams)
@@ -213,6 +204,7 @@ private:
SshConnectionManager::SshConnectionManager()
: d(new Internal::SshConnectionManagerPrivate())
{
+ QTC_CHECK(QThread::currentThread() == qApp->thread());
}
SshConnectionManager::~SshConnectionManager()
@@ -222,20 +214,17 @@ SshConnectionManager::~SshConnectionManager()
SshConnection *SshConnectionManager::acquireConnection(const SshConnectionParameters &sshParams)
{
- QTC_ASSERT(Internal::s_instance, return nullptr);
- return Internal::s_instance->acquireConnection(sshParams);
+ return instance()->d->acquireConnection(sshParams);
}
void SshConnectionManager::releaseConnection(SshConnection *connection)
{
- QTC_ASSERT(Internal::s_instance, return);
- Internal::s_instance->releaseConnection(connection);
+ instance()->d->releaseConnection(connection);
}
void SshConnectionManager::forceNewConnection(const SshConnectionParameters &sshParams)
{
- QTC_ASSERT(Internal::s_instance, return);
- Internal::s_instance->forceNewConnection(sshParams);
+ instance()->d->forceNewConnection(sshParams);
}
} // namespace QSsh
diff --git a/src/libs/ssh/sshconnectionmanager.h b/src/libs/ssh/sshconnectionmanager.h
index 8d8a03f43a..d46a8d8b89 100644
--- a/src/libs/ssh/sshconnectionmanager.h
+++ b/src/libs/ssh/sshconnectionmanager.h
@@ -27,6 +27,9 @@
#include "ssh_global.h"
+#include <utils/launcherinterface.h>
+#include <utils/singleton.h>
+
namespace QSsh {
class SshConnection;
@@ -35,18 +38,21 @@ class SshConnectionParameters;
namespace Internal { class SshConnectionManagerPrivate; }
class QSSH_EXPORT SshConnectionManager final
+ : public Utils::SingletonWithOptionalDependencies<SshConnectionManager,
+ Utils::LauncherInterface>
{
public:
- SshConnectionManager();
- ~SshConnectionManager();
-
static SshConnection *acquireConnection(const SshConnectionParameters &sshParams);
static void releaseConnection(SshConnection *connection);
// Make sure the next acquireConnection with the given parameters will return a new connection.
static void forceNewConnection(const SshConnectionParameters &sshParams);
private:
+ SshConnectionManager();
+ ~SshConnectionManager();
+
Internal::SshConnectionManagerPrivate *d;
+ friend class Utils::SingletonWithOptionalDependencies<SshConnectionManager, Utils::LauncherInterface>;
};
} // namespace QSsh
diff --git a/src/libs/utils/CMakeLists.txt b/src/libs/utils/CMakeLists.txt
index 3ef203bc21..9db6ea5f1e 100644
--- a/src/libs/utils/CMakeLists.txt
+++ b/src/libs/utils/CMakeLists.txt
@@ -144,6 +144,7 @@ add_qtc_library(Utils
settingsutils.h
shellcommand.cpp shellcommand.h
shellcommandpage.cpp shellcommandpage.h
+ singleton.cpp singleton.h
sizedarray.h
smallstring.h
smallstringfwd.h
diff --git a/src/libs/utils/launcherinterface.cpp b/src/libs/utils/launcherinterface.cpp
index 269e4393d7..a5609fd6e7 100644
--- a/src/libs/utils/launcherinterface.cpp
+++ b/src/libs/utils/launcherinterface.cpp
@@ -28,7 +28,6 @@
#include "filepath.h"
#include "launcherpackets.h"
#include "launchersocket.h"
-#include "processreaper.h"
#include "qtcassert.h"
#include <QCoreApplication>
@@ -42,7 +41,6 @@
#endif
namespace Utils {
-
namespace Internal {
class LauncherProcess : public QProcess
@@ -185,92 +183,73 @@ void LauncherInterfacePrivate::handleProcessStderr()
using namespace Utils::Internal;
static QMutex s_instanceMutex;
-static LauncherInterface *s_instance = nullptr;
+static QString s_pathToLauncher;
+static std::atomic_bool s_started = false;
LauncherInterface::LauncherInterface()
: m_private(new LauncherInterfacePrivate())
{
m_private->moveToThread(&m_thread);
- connect(m_private, &LauncherInterfacePrivate::errorOccurred,
- this, &LauncherInterface::errorOccurred);
- connect(&m_thread, &QThread::finished, m_private, &QObject::deleteLater);
+ QObject::connect(&m_thread, &QThread::finished, m_private, &QObject::deleteLater);
m_thread.start();
-}
+ m_thread.moveToThread(qApp->thread());
-LauncherInterface::~LauncherInterface()
-{
- m_thread.quit();
- m_thread.wait();
-}
-
-// Called from main thread
-void LauncherInterface::startLauncher(const QString &pathToLauncher)
-{
- QMutexLocker locker(&s_instanceMutex);
- QTC_ASSERT(s_instance == nullptr, return);
- s_instance = new LauncherInterface();
- LauncherInterfacePrivate *p = s_instance->m_private;
- p->setPathToLauncher(pathToLauncher);
- const FilePath launcherFilePath = FilePath::fromString(p->launcherFilePath())
+ m_private->setPathToLauncher(s_pathToLauncher);
+ const FilePath launcherFilePath = FilePath::fromString(m_private->launcherFilePath())
.cleanPath().withExecutableSuffix();
auto launcherIsNotExecutable = [&launcherFilePath]() {
qWarning() << "The Creator's process launcher"
<< launcherFilePath << "is not executable.";
};
QTC_ASSERT(launcherFilePath.isExecutableFile(), launcherIsNotExecutable(); return);
+ s_started = true;
// Call in launcher's thread.
- QMetaObject::invokeMethod(p, &LauncherInterfacePrivate::doStart);
+ QMetaObject::invokeMethod(m_private, &LauncherInterfacePrivate::doStart);
}
-// Called from main thread
-void LauncherInterface::stopLauncher()
+LauncherInterface::~LauncherInterface()
{
QMutexLocker locker(&s_instanceMutex);
- QTC_ASSERT(s_instance != nullptr, return);
- LauncherInterfacePrivate *p = s_instance->m_private;
+ LauncherInterfacePrivate *p = instance()->m_private;
// Call in launcher's thread.
QMetaObject::invokeMethod(p, &LauncherInterfacePrivate::doStop, Qt::BlockingQueuedConnection);
- delete s_instance;
- s_instance = nullptr;
+ m_thread.quit();
+ m_thread.wait();
+}
+
+void LauncherInterface::setPathToLauncher(const QString &pathToLauncher)
+{
+ s_pathToLauncher = pathToLauncher;
}
bool LauncherInterface::isStarted()
{
- QMutexLocker locker(&s_instanceMutex);
- return s_instance != nullptr;
+ return s_started;
}
bool LauncherInterface::isReady()
{
QMutexLocker locker(&s_instanceMutex);
- QTC_ASSERT(s_instance != nullptr, return false);
-
- return s_instance->m_private->socket()->isReady();
+ return instance()->m_private->socket()->isReady();
}
void LauncherInterface::sendData(const QByteArray &data)
{
QMutexLocker locker(&s_instanceMutex);
- QTC_ASSERT(s_instance != nullptr, return);
-
- s_instance->m_private->socket()->sendData(data);
+ instance()->m_private->socket()->sendData(data);
}
Utils::Internal::CallerHandle *LauncherInterface::registerHandle(QObject *parent, quintptr token,
ProcessMode mode)
{
QMutexLocker locker(&s_instanceMutex);
- QTC_ASSERT(s_instance != nullptr, return nullptr);
-
- return s_instance->m_private->socket()->registerHandle(parent, token, mode);
+ return instance()->m_private->socket()->registerHandle(parent, token, mode);
}
void LauncherInterface::unregisterHandle(quintptr token)
{
QMutexLocker locker(&s_instanceMutex);
- QTC_ASSERT(s_instance != nullptr, return);
-
- s_instance->m_private->socket()->unregisterHandle(token);
+ instance()->m_private->socket()->unregisterHandle(token);
}
} // namespace Utils
diff --git a/src/libs/utils/launcherinterface.h b/src/libs/utils/launcherinterface.h
index 0df595af85..541501efee 100644
--- a/src/libs/utils/launcherinterface.h
+++ b/src/libs/utils/launcherinterface.h
@@ -27,9 +27,10 @@
#include "utils_global.h"
+#include "processreaper.h"
#include "processutils.h"
+#include "singleton.h"
-#include <QObject>
#include <QThread>
namespace Utils {
@@ -40,15 +41,11 @@ class LauncherInterfacePrivate;
class ProcessLauncherImpl;
}
-class QTCREATOR_UTILS_EXPORT LauncherInterface : public QObject
+class QTCREATOR_UTILS_EXPORT LauncherInterface final
+ : public SingletonWithOptionalDependencies<LauncherInterface, ProcessReaper>
{
- Q_OBJECT
public:
- static void startLauncher(const QString &pathToLauncher = {});
- static void stopLauncher();
-
-signals:
- void errorOccurred(const QString &error);
+ static void setPathToLauncher(const QString &pathToLauncher);
private:
friend class Utils::Internal::CallerHandle;
@@ -63,10 +60,11 @@ private:
static void unregisterHandle(quintptr token);
LauncherInterface();
- ~LauncherInterface() override;
+ ~LauncherInterface();
QThread m_thread;
Internal::LauncherInterfacePrivate *m_private;
+ friend class SingletonWithOptionalDependencies<LauncherInterface, ProcessReaper>;
};
} // namespace Utils
diff --git a/src/libs/utils/processreaper.cpp b/src/libs/utils/processreaper.cpp
index b3679c7d95..c6d2a071f5 100644
--- a/src/libs/utils/processreaper.cpp
+++ b/src/libs/utils/processreaper.cpp
@@ -36,8 +36,6 @@ using namespace Utils;
namespace Utils {
namespace Internal {
-static ProcessReaper *d = nullptr;
-
class Reaper final : public QObject
{
public:
@@ -57,7 +55,7 @@ private:
Reaper::Reaper(QProcess *p, int timeoutMs) : m_process(p)
{
- d->m_reapers.append(this);
+ ProcessReaper::instance()->m_reapers.append(this);
m_iterationTimer.setInterval(timeoutMs);
m_iterationTimer.setSingleShot(true);
@@ -68,7 +66,7 @@ Reaper::Reaper(QProcess *p, int timeoutMs) : m_process(p)
Reaper::~Reaper()
{
- d->m_reapers.removeOne(this);
+ ProcessReaper::instance()->m_reapers.removeOne(this);
}
int Reaper::timeoutMs() const
@@ -112,15 +110,8 @@ void Reaper::nextIteration()
} // namespace Internal
-ProcessReaper::ProcessReaper()
-{
- QTC_ASSERT(Internal::d == nullptr, return);
- Internal::d = this;
-}
-
ProcessReaper::~ProcessReaper()
{
- QTC_ASSERT(Internal::d == this, return);
while (!m_reapers.isEmpty()) {
int alreadyWaited = 0;
QList<Internal::Reaper *> toDelete;
@@ -145,8 +136,6 @@ ProcessReaper::~ProcessReaper()
qDeleteAll(toDelete);
toDelete.clear();
}
-
- Internal::d = nullptr;
}
void ProcessReaper::reap(QProcess *process, int timeoutMs)
@@ -175,8 +164,6 @@ void ProcessReaper::reap(QProcess *process, int timeoutMs)
return;
}
- QTC_ASSERT(Internal::d, return);
-
new Internal::Reaper(process, timeoutMs);
}
diff --git a/src/libs/utils/processreaper.h b/src/libs/utils/processreaper.h
index d26325b6f0..f8485c12e3 100644
--- a/src/libs/utils/processreaper.h
+++ b/src/libs/utils/processreaper.h
@@ -27,6 +27,8 @@
#include "utils_global.h"
+#include "singleton.h"
+
#include <QList>
QT_BEGIN_NAMESPACE
@@ -38,15 +40,16 @@ namespace Utils {
namespace Internal { class Reaper; }
class QTCREATOR_UTILS_EXPORT ProcessReaper final
+ : public SingletonWithOptionalDependencies<ProcessReaper>
{
public:
- ProcessReaper();
- ~ProcessReaper();
-
static void reap(QProcess *process, int timeoutMs = 500);
private:
+ ProcessReaper() = default;
+ ~ProcessReaper();
QList<Internal::Reaper *> m_reapers;
friend class Internal::Reaper;
+ friend class SingletonWithOptionalDependencies<ProcessReaper>;
};
} // namespace Utils
diff --git a/src/libs/utils/singleton.cpp b/src/libs/utils/singleton.cpp
new file mode 100644
index 0000000000..6bd79ca36f
--- /dev/null
+++ b/src/libs/utils/singleton.cpp
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "qtcassert.h"
+#include "singleton.h"
+
+#include <QCoreApplication>
+#include <QList>
+#include <QThread>
+
+#include <unordered_map>
+
+using namespace Utils;
+
+namespace Utils {
+
+// The order of elements reflects dependencies, i.e.
+// if B requires A then B will follow A on this list
+static QList<Singleton *> s_singletonList;
+static QMutex s_mutex;
+static std::unordered_map<std::type_index, SingletonStaticData> s_staticDataList;
+
+Singleton::~Singleton()
+{
+ QMutexLocker locker(&s_mutex);
+ s_singletonList.removeAll(this);
+}
+
+void Singleton::addSingleton(Singleton *singleton)
+{
+ QMutexLocker locker(&s_mutex);
+ s_singletonList.append(singleton);
+}
+
+SingletonStaticData &Singleton::staticData(std::type_index index)
+{
+ QMutexLocker locker(&s_mutex);
+ return s_staticDataList[index];
+}
+
+// Note: it's caller responsibility to ensure that this function is being called when all other
+// threads don't use any singleton. As a good practice: finish all other threads that were using
+// singletons before this function is called.
+// Some singletons (currently e.g. SshConnectionManager) can work only in main thread,
+// so this method should be called from main thread only.
+void Singleton::deleteAll()
+{
+ QTC_ASSERT(QThread::currentThread() == qApp->thread(), return);
+ QList<Singleton *> oldList;
+ {
+ QMutexLocker locker(&s_mutex);
+ oldList = s_singletonList;
+ s_singletonList = {};
+ }
+ // Keep the reverse order when deleting
+ while (!oldList.isEmpty())
+ delete oldList.takeLast();
+}
+
+} // namespace Utils
diff --git a/src/libs/utils/singleton.h b/src/libs/utils/singleton.h
new file mode 100644
index 0000000000..4ba4dfecd0
--- /dev/null
+++ b/src/libs/utils/singleton.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "utils_global.h"
+
+#include <QMutex>
+#include <QMutexLocker>
+
+#include <type_traits>
+#include <typeindex>
+
+namespace Utils {
+
+class Singleton;
+
+struct SingletonStaticData
+{
+ Singleton *m_instance = nullptr;
+ QMutex m_mutex;
+};
+
+class QTCREATOR_UTILS_EXPORT Singleton
+{
+ Q_DISABLE_COPY_MOVE(Singleton)
+public:
+ static void deleteAll();
+
+private:
+ template <typename SingletonSubClass, typename ...Dependencies>
+ friend class SingletonWithOptionalDependencies;
+
+ Singleton() = default;
+ virtual ~Singleton();
+ static void addSingleton(Singleton *singleton);
+ static SingletonStaticData &staticData(std::type_index index);
+};
+
+template <typename SingletonSubClass, typename ...Dependencies>
+class SingletonWithOptionalDependencies : public Singleton
+{
+public:
+ Q_DISABLE_COPY_MOVE(SingletonWithOptionalDependencies)
+ static SingletonSubClass *instance()
+ {
+ SingletonStaticData &data = staticData();
+ QMutexLocker locker(&data.m_mutex);
+ if (data.m_instance == nullptr) {
+ // instantiate all dependencies first
+ if constexpr (sizeof...(Dependencies))
+ instantiateDependencies<Dependencies...>();
+ data.m_instance = new SingletonSubClass;
+ // put instance into static list of registered instances
+ addSingleton(data.m_instance);
+ }
+ return static_cast<SingletonSubClass *>(data.m_instance);
+ }
+
+protected:
+ SingletonWithOptionalDependencies() = default;
+ ~SingletonWithOptionalDependencies() override
+ {
+ SingletonStaticData &data = staticData();
+ QMutexLocker locker(&data.m_mutex);
+ if (data.m_instance == this)
+ data.m_instance = nullptr;
+ }
+
+private:
+ template <typename ...Dependency> static void instantiateDependencies()
+ {
+ static_assert ((... && std::is_base_of_v<Singleton, Dependency>),
+ "All Dependencies must derive from SingletonWithOptionalDependencies class.");
+ (..., Dependency::instance());
+ }
+ static SingletonStaticData &staticData()
+ {
+ static SingletonStaticData &data = Singleton::staticData(std::type_index(typeid(SingletonSubClass)));
+ return data;
+ }
+};
+
+} // namespace Utils
diff --git a/src/libs/utils/utils-lib.pri b/src/libs/utils/utils-lib.pri
index 7ceac359e8..ddaae4bb69 100644
--- a/src/libs/utils/utils-lib.pri
+++ b/src/libs/utils/utils-lib.pri
@@ -143,6 +143,7 @@ SOURCES += \
$$PWD/link.cpp \
$$PWD/linecolumn.cpp \
$$PWD/multitextcursor.cpp \
+ $$PWD/singleton.cpp
HEADERS += \
$$PWD/environmentfwd.h \
@@ -307,8 +308,9 @@ HEADERS += \
$$PWD/launcherinterface.h \
$$PWD/launcherpackets.h \
$$PWD/launchersocket.h \
- $$PWD/qtcsettings.h
+ $$PWD/qtcsettings.h \
$$PWD/multitextcursor.h \
+ $$PWD/singleton.h
FORMS += $$PWD/filewizardpage.ui \
$$PWD/projectintropage.ui \
diff --git a/src/libs/utils/utils.qbs b/src/libs/utils/utils.qbs
index e6b1d0621d..77f80dd81b 100644
--- a/src/libs/utils/utils.qbs
+++ b/src/libs/utils/utils.qbs
@@ -264,6 +264,8 @@ Project {
"shellcommand.h",
"shellcommandpage.cpp",
"shellcommandpage.h",
+ "singleton.cpp",
+ "singleton.h",
"sizedarray.h",
"smallstring.h",
"smallstringiterator.h",
diff --git a/src/plugins/mesonprojectmanager/tests/testmesoninfoparser.cpp b/src/plugins/mesonprojectmanager/tests/testmesoninfoparser.cpp
index cbbf9042da..e25fec5456 100644
--- a/src/plugins/mesonprojectmanager/tests/testmesoninfoparser.cpp
+++ b/src/plugins/mesonprojectmanager/tests/testmesoninfoparser.cpp
@@ -27,7 +27,7 @@
#include "mesoninfoparser/mesoninfoparser.h"
#include <utils/launcherinterface.h>
-#include <utils/processreaper.h>
+#include <utils/singleton.h>
#include <QCoreApplication>
#include <QDir>
@@ -79,9 +79,8 @@ class AMesonInfoParser : public QObject
private slots:
void initTestCase()
{
- Utils::LauncherInterface::startLauncher(
- QCoreApplication::instance()->applicationDirPath() + '/'
- + QLatin1String(TEST_RELATIVE_LIBEXEC_PATH));
+ Utils::LauncherInterface::setPathToLauncher(qApp->applicationDirPath() + '/'
+ + QLatin1String(TEST_RELATIVE_LIBEXEC_PATH));
}
void shouldListTargets_data()
@@ -121,11 +120,10 @@ private slots:
void cleanupTestCase()
{
- Utils::LauncherInterface::stopLauncher();
+ Utils::Singleton::deleteAll();
}
private:
- Utils::ProcessReaper processReaper;
};
QTEST_MAIN(AMesonInfoParser)
diff --git a/src/plugins/mesonprojectmanager/tests/testmesonwrapper.cpp b/src/plugins/mesonprojectmanager/tests/testmesonwrapper.cpp
index c257434151..7db2c49480 100644
--- a/src/plugins/mesonprojectmanager/tests/testmesonwrapper.cpp
+++ b/src/plugins/mesonprojectmanager/tests/testmesonwrapper.cpp
@@ -26,7 +26,7 @@
#include "exewrappers/mesonwrapper.h"
#include <utils/launcherinterface.h>
-#include <utils/processreaper.h>
+#include <utils/singleton.h>
#include <QCoreApplication>
#include <QDir>
@@ -50,9 +50,8 @@ class AMesonWrapper : public QObject
private slots:
void initTestCase()
{
- Utils::LauncherInterface::startLauncher(
- QCoreApplication::instance()->applicationDirPath() + '/'
- + QLatin1String(TEST_RELATIVE_LIBEXEC_PATH));
+ Utils::LauncherInterface::setPathToLauncher(qApp->applicationDirPath() + '/'
+ + QLatin1String(TEST_RELATIVE_LIBEXEC_PATH));
}
void shouldFindMesonFromPATH()
@@ -115,11 +114,10 @@ private slots:
void cleanupTestCase()
{
- Utils::LauncherInterface::stopLauncher();
+ Utils::Singleton::deleteAll();
}
private:
- Utils::ProcessReaper processReaper;
};
QTEST_MAIN(AMesonWrapper)
diff --git a/src/tools/processlauncher/CMakeLists.txt b/src/tools/processlauncher/CMakeLists.txt
index 1cd0e85cd4..e0064c62b3 100644
--- a/src/tools/processlauncher/CMakeLists.txt
+++ b/src/tools/processlauncher/CMakeLists.txt
@@ -18,4 +18,6 @@ add_qtc_executable(qtcreator_processlauncher
${UTILSDIR}/processutils.h
${UTILSDIR}/qtcassert.cpp
${UTILSDIR}/qtcassert.h
+ ${UTILSDIR}/singleton.cpp
+ ${UTILSDIR}/singleton.h
)
diff --git a/src/tools/processlauncher/processlauncher-main.cpp b/src/tools/processlauncher/processlauncher-main.cpp
index c651a276cc..7686925521 100644
--- a/src/tools/processlauncher/processlauncher-main.cpp
+++ b/src/tools/processlauncher/processlauncher-main.cpp
@@ -25,9 +25,10 @@
#include "launcherlogging.h"
#include "launchersockethandler.h"
-#include "processreaper.h"
+#include "singleton.h"
#include <QtCore/qcoreapplication.h>
+#include <QtCore/qscopeguard.h>
#include <QtCore/qtimer.h>
#ifdef Q_OS_WIN
@@ -52,7 +53,8 @@ int main(int argc, char *argv[])
return 1;
}
- Utils::ProcessReaper processReaper;
+ auto cleanup = qScopeGuard([] { Utils::Singleton::deleteAll(); });
+
Utils::Internal::LauncherSocketHandler launcher(app.arguments().constLast());
QTimer::singleShot(0, &launcher, &Utils::Internal::LauncherSocketHandler::start);
return app.exec();
diff --git a/src/tools/processlauncher/processlauncher.pro b/src/tools/processlauncher/processlauncher.pro
index 260e701e3c..737cb366cf 100644
--- a/src/tools/processlauncher/processlauncher.pro
+++ b/src/tools/processlauncher/processlauncher.pro
@@ -7,6 +7,8 @@ QT = core network
UTILS_DIR = $$PWD/../../libs/utils
+DEFINES *= QTCREATOR_UTILS_STATIC_LIB
+
INCLUDEPATH += $$UTILS_DIR
HEADERS += \
@@ -15,7 +17,8 @@ HEADERS += \
$$UTILS_DIR/launcherpackets.h \
$$UTILS_DIR/processreaper.h \
$$UTILS_DIR/processutils.h \
- $$UTILS_DIR/qtcassert.h
+ $$UTILS_DIR/qtcassert.h \
+ $$UTILS_DIR/singleton.h
SOURCES += \
launcherlogging.cpp \
@@ -24,4 +27,5 @@ SOURCES += \
$$UTILS_DIR/launcherpackets.cpp \
$$UTILS_DIR/processreaper.cpp \
$$UTILS_DIR/processutils.cpp \
- $$UTILS_DIR/qtcassert.cpp
+ $$UTILS_DIR/qtcassert.cpp \
+ $$UTILS_DIR/singleton.cpp
diff --git a/src/tools/processlauncher/processlauncher.qbs b/src/tools/processlauncher/processlauncher.qbs
index 1050d0d592..b2fd708169 100644
--- a/src/tools/processlauncher/processlauncher.qbs
+++ b/src/tools/processlauncher/processlauncher.qbs
@@ -30,6 +30,8 @@ QtcTool {
"processutils.h",
"qtcassert.cpp",
"qtcassert.h",
+ "singleton.cpp",
+ "singleton.h",
]
}
}
diff --git a/tests/auto/ssh/tst_ssh.cpp b/tests/auto/ssh/tst_ssh.cpp
index 597d035e50..0990f5727f 100644
--- a/tests/auto/ssh/tst_ssh.cpp
+++ b/tests/auto/ssh/tst_ssh.cpp
@@ -26,15 +26,14 @@
#include <ssh/sftpsession.h>
#include <ssh/sftptransfer.h>
#include <ssh/sshconnection.h>
-#include <ssh/sshconnectionmanager.h>
#include <ssh/sshremoteprocessrunner.h>
#include <ssh/sshsettings.h>
#include <utils/algorithm.h>
#include <utils/environment.h>
#include <utils/launcherinterface.h>
-#include <utils/processreaper.h>
#include <utils/qtcprocess.h>
+#include <utils/singleton.h>
#include <utils/temporarydirectory.h>
#include <QDateTime>
@@ -134,17 +133,12 @@ private slots:
void cleanupTestCase();
private:
bool waitForConnection(SshConnection &connection);
-
- Utils::ProcessReaper *processReaper = nullptr;
- SshConnectionManager *sshConnectionManager = nullptr;
};
void tst_Ssh::initTestCase()
{
- processReaper = new Utils::ProcessReaper();
- Utils::LauncherInterface::startLauncher(qApp->applicationDirPath() + '/'
- + QLatin1String(TEST_RELATIVE_LIBEXEC_PATH));
- sshConnectionManager = new SshConnectionManager();
+ Utils::LauncherInterface::setPathToLauncher(qApp->applicationDirPath() + '/'
+ + QLatin1String(TEST_RELATIVE_LIBEXEC_PATH));
Utils::TemporaryDirectory::setMasterTemporaryDirectory(QDir::tempPath()
+ "/qtc-ssh-autotest-XXXXXX");
}
@@ -647,9 +641,7 @@ void tst_Ssh::sftp()
void tst_Ssh::cleanupTestCase()
{
- delete sshConnectionManager;
- Utils::LauncherInterface::stopLauncher();
- delete processReaper;
+ Utils::Singleton::deleteAll();
}
bool tst_Ssh::waitForConnection(SshConnection &connection)
diff --git a/tests/auto/utils/qtcprocess/tst_qtcprocess.cpp b/tests/auto/utils/qtcprocess/tst_qtcprocess.cpp
index a247e4f963..2ad9eba89d 100644
--- a/tests/auto/utils/qtcprocess/tst_qtcprocess.cpp
+++ b/tests/auto/utils/qtcprocess/tst_qtcprocess.cpp
@@ -27,8 +27,8 @@
#include <utils/hostosinfo.h>
#include <utils/launcherinterface.h>
#include <utils/porting.h>
-#include <utils/processreaper.h>
#include <utils/qtcprocess.h>
+#include <utils/singleton.h>
#include <utils/stringutils.h>
#include <QElapsedTimer>
@@ -213,7 +213,6 @@ private slots:
private:
void iteratorEditsHelper(OsType osType);
- Utils::ProcessReaper processReaper;
Environment envWindows;
Environment envLinux;
@@ -225,8 +224,8 @@ private:
void tst_QtcProcess::initTestCase()
{
- Utils::LauncherInterface::startLauncher(qApp->applicationDirPath() + '/'
- + QLatin1String(TEST_RELATIVE_LIBEXEC_PATH));
+ Utils::LauncherInterface::setPathToLauncher(qApp->applicationDirPath() + '/'
+ + QLatin1String(TEST_RELATIVE_LIBEXEC_PATH));
if (qEnvironmentVariableIsSet(kExitCodeSubProcessCode))
exitCodeSubProcessMain();
if (qEnvironmentVariableIsSet(kRunBlockingStdOutSubProcessWithEndl))
@@ -285,7 +284,7 @@ void tst_QtcProcess::initTestCase()
void tst_QtcProcess::cleanupTestCase()
{
- Utils::LauncherInterface::stopLauncher();
+ Utils::Singleton::deleteAll();
}
Q_DECLARE_METATYPE(ProcessArgs::SplitError)
diff --git a/tests/unit/unittest/unittests-main.cpp b/tests/unit/unittest/unittests-main.cpp
index 4d48f2c1dc..a308b2a6a7 100644
--- a/tests/unit/unittest/unittests-main.cpp
+++ b/tests/unit/unittest/unittests-main.cpp
@@ -30,7 +30,7 @@
#include <sqliteglobal.h>
#include <utils/launcherinterface.h>
-#include <utils/processreaper.h>
+#include <utils/singleton.h>
#include <utils/temporarydirectory.h>
#include <QGuiApplication>
@@ -61,10 +61,8 @@ int main(int argc, char *argv[])
Sqlite::Database::activateLogging();
QGuiApplication application(argc, argv);
- Utils::ProcessReaper processReaper;
- Utils::LauncherInterface::startLauncher(qApp->applicationDirPath() + '/'
- + QLatin1String(TEST_RELATIVE_LIBEXEC_PATH));
- auto cleanup = qScopeGuard([] { Utils::LauncherInterface::stopLauncher(); });
+ Utils::LauncherInterface::setPathToLauncher(qApp->applicationDirPath() + '/'
+ + QLatin1String(TEST_RELATIVE_LIBEXEC_PATH));
testing::InitGoogleTest(&argc, argv);
#ifdef WITH_BENCHMARKS
benchmark::Initialize(&argc, argv);
@@ -75,6 +73,7 @@ int main(int argc, char *argv[])
int testsHaveErrors = RUN_ALL_TESTS();
+ Utils::Singleton::deleteAll();
#ifdef WITH_BENCHMARKS
if (testsHaveErrors == 0 && application.arguments().contains(QStringLiteral("--with-benchmarks")))
benchmark::RunSpecifiedBenchmarks();