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>
This commit is contained in:
Jarek Kobus
2021-09-17 16:32:50 +02:00
parent 03f6de1eeb
commit 7958de05f5
24 changed files with 282 additions and 138 deletions

View File

@@ -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