Move ProcessReaper into lib/utils

Reuse ProcessReaper inside process launcher.
Automatically reap all internal QProcesses of QtcProcess
(either direct child of QtcProcess in QProcessImpl
or indirectly inside process launcher).
Make ProcessReaper work again on QProcess instead of on
QtcProcess, so it may still be reused for non-QtcProcesses.

Change-Id: I950cac5cec28f17ae97fe474d6a4e48c01d6aaa2
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Jarek Kobus
2021-09-06 14:48:08 +02:00
parent 72d52f3ac2
commit ace765c199
26 changed files with 142 additions and 203 deletions

View File

@@ -38,6 +38,7 @@
#include <utils/hostosinfo.h> #include <utils/hostosinfo.h>
#include <utils/launcherinterface.h> #include <utils/launcherinterface.h>
#include <utils/optional.h> #include <utils/optional.h>
#include <utils/processreaper.h>
#include <utils/qtcsettings.h> #include <utils/qtcsettings.h>
#include <utils/temporarydirectory.h> #include <utils/temporarydirectory.h>
@@ -534,6 +535,7 @@ int main(int argc, char **argv)
QCoreApplication::setOrganizationName(QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR)); QCoreApplication::setOrganizationName(QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR));
QGuiApplication::setApplicationDisplayName(Core::Constants::IDE_DISPLAY_NAME); QGuiApplication::setApplicationDisplayName(Core::Constants::IDE_DISPLAY_NAME);
Utils::ProcessReaper processReaper;
Utils::LauncherInterface::startLauncher(); Utils::LauncherInterface::startLauncher();
auto cleanup = qScopeGuard([] { Utils::LauncherInterface::stopLauncher(); }); auto cleanup = qScopeGuard([] { Utils::LauncherInterface::stopLauncher(); });

View File

@@ -121,6 +121,7 @@ add_qtc_library(Utils
portlist.cpp portlist.h portlist.cpp portlist.h
predicates.h predicates.h
processhandle.cpp processhandle.h processhandle.cpp processhandle.h
processreaper.cpp processreaper.h
processutils.cpp processutils.h processutils.cpp processutils.h
progressindicator.cpp progressindicator.h progressindicator.cpp progressindicator.h
projectintropage.cpp projectintropage.h projectintropage.ui projectintropage.cpp projectintropage.h projectintropage.ui

View File

@@ -28,6 +28,7 @@
#include "filepath.h" #include "filepath.h"
#include "launcherpackets.h" #include "launcherpackets.h"
#include "launchersocket.h" #include "launchersocket.h"
#include "processreaper.h"
#include "qtcassert.h" #include "qtcassert.h"
#include <QCoreApplication> #include <QCoreApplication>
@@ -143,7 +144,7 @@ void LauncherInterfacePrivate::doStop()
m_process->disconnect(); m_process->disconnect();
m_socket->shutdown(); m_socket->shutdown();
m_process->waitForFinished(3000); m_process->waitForFinished(3000);
m_process->deleteLater(); ProcessReaper::reap(m_process);
m_process = nullptr; m_process = nullptr;
} }

View File

@@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of Qt Creator. ** This file is part of Qt Creator.
@@ -23,29 +23,26 @@
** **
****************************************************************************/ ****************************************************************************/
#include "reaper.h" #include "processreaper.h"
#include "reaper_p.h" #include "qtcassert.h"
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
#include <QCoreApplication>
#include <QProcess> #include <QProcess>
#include <QThread> #include <QThread>
#include <QTimer> #include <QTimer>
using namespace Utils; using namespace Utils;
namespace Core { namespace Utils {
namespace Internal { namespace Internal {
static ProcessReapers *d = nullptr; static ProcessReaper *d = nullptr;
class ProcessReaper final : public QObject class Reaper final : public QObject
{ {
public: public:
ProcessReaper(QtcProcess *p, int timeoutMs); Reaper(QProcess *p, int timeoutMs);
~ProcessReaper() final; ~Reaper() final;
int timeoutMs() const; int timeoutMs() const;
bool isFinished() const; bool isFinished() const;
@@ -53,28 +50,28 @@ public:
private: private:
mutable QTimer m_iterationTimer; mutable QTimer m_iterationTimer;
QtcProcess *m_process; QProcess *m_process = nullptr;
int m_emergencyCounter = 0; int m_emergencyCounter = 0;
QProcess::ProcessState m_lastState = QProcess::NotRunning; QProcess::ProcessState m_lastState = QProcess::NotRunning;
}; };
ProcessReaper::ProcessReaper(QtcProcess *p, int timeoutMs) : m_process(p) Reaper::Reaper(QProcess *p, int timeoutMs) : m_process(p)
{ {
d->m_reapers.append(this); d->m_reapers.append(this);
m_iterationTimer.setInterval(timeoutMs); m_iterationTimer.setInterval(timeoutMs);
m_iterationTimer.setSingleShot(true); m_iterationTimer.setSingleShot(true);
connect(&m_iterationTimer, &QTimer::timeout, this, &ProcessReaper::nextIteration); connect(&m_iterationTimer, &QTimer::timeout, this, &Reaper::nextIteration);
QMetaObject::invokeMethod(this, &ProcessReaper::nextIteration, Qt::QueuedConnection); QMetaObject::invokeMethod(this, &Reaper::nextIteration, Qt::QueuedConnection);
} }
ProcessReaper::~ProcessReaper() Reaper::~Reaper()
{ {
d->m_reapers.removeOne(this); d->m_reapers.removeOne(this);
} }
int ProcessReaper::timeoutMs() const int Reaper::timeoutMs() const
{ {
const int remaining = m_iterationTimer.remainingTime(); const int remaining = m_iterationTimer.remainingTime();
if (remaining < 0) if (remaining < 0)
@@ -83,12 +80,12 @@ int ProcessReaper::timeoutMs() const
return remaining; return remaining;
} }
bool ProcessReaper::isFinished() const bool Reaper::isFinished() const
{ {
return !m_process; return !m_process;
} }
void ProcessReaper::nextIteration() void Reaper::nextIteration()
{ {
QProcess::ProcessState state = m_process ? m_process->state() : QProcess::NotRunning; QProcess::ProcessState state = m_process ? m_process->state() : QProcess::NotRunning;
if (state == QProcess::NotRunning || m_emergencyCounter > 5) { if (state == QProcess::NotRunning || m_emergencyCounter > 5) {
@@ -113,19 +110,23 @@ void ProcessReaper::nextIteration()
++m_emergencyCounter; ++m_emergencyCounter;
} }
ProcessReapers::ProcessReapers() } // namespace Internal
ProcessReaper::ProcessReaper()
{ {
d = this; QTC_ASSERT(Internal::d == nullptr, return);
Internal::d = this;
} }
ProcessReapers::~ProcessReapers() ProcessReaper::~ProcessReaper()
{ {
QTC_ASSERT(Internal::d == this, return);
while (!m_reapers.isEmpty()) { while (!m_reapers.isEmpty()) {
int alreadyWaited = 0; int alreadyWaited = 0;
QList<ProcessReaper *> toDelete; QList<Internal::Reaper *> toDelete;
// push reapers along: // push reapers along:
foreach (ProcessReaper *pr, m_reapers) { for (Internal::Reaper *pr : qAsConst(m_reapers)) {
const int timeoutMs = pr->timeoutMs(); const int timeoutMs = pr->timeoutMs();
if (alreadyWaited < timeoutMs) { if (alreadyWaited < timeoutMs) {
const unsigned long toSleep = static_cast<unsigned long>(timeoutMs - alreadyWaited); const unsigned long toSleep = static_cast<unsigned long>(timeoutMs - alreadyWaited);
@@ -145,25 +146,30 @@ ProcessReapers::~ProcessReapers()
toDelete.clear(); toDelete.clear();
} }
d = nullptr; Internal::d = nullptr;
} }
} // namespace Internal void ProcessReaper::reap(QProcess *process, int timeoutMs)
namespace Reaper {
void reap(QtcProcess *process, int timeoutMs)
{ {
if (!process) if (!process)
return; return;
QTC_ASSERT(QThread::currentThread() == process->thread(), return); QTC_ASSERT(QThread::currentThread() == process->thread(), return);
process->disconnect();
process->setParent(nullptr);
if (process->state() == QProcess::NotRunning) {
process->deleteLater();
return;
} else {
process->kill();
}
// Neither can move object with a parent into a different thread // Neither can move object with a parent into a different thread
// nor reaping the process with a parent makes any sense. // nor reaping the process with a parent makes any sense.
process->setParent(nullptr); process->setParent(nullptr);
if (process->thread() != qApp->thread()) { if (process->thread() != QCoreApplication::instance()->thread()) {
process->moveToThread(qApp->thread()); process->moveToThread(QCoreApplication::instance()->thread());
QMetaObject::invokeMethod(process, [process, timeoutMs] { QMetaObject::invokeMethod(process, [process, timeoutMs] {
reap(process, timeoutMs); reap(process, timeoutMs);
}); // will be queued }); // will be queued
@@ -172,9 +178,8 @@ void reap(QtcProcess *process, int timeoutMs)
QTC_ASSERT(Internal::d, return); QTC_ASSERT(Internal::d, return);
new Internal::ProcessReaper(process, timeoutMs); new Internal::Reaper(process, timeoutMs);
} }
} // namespace Reaper } // namespace Utils
} // namespace Core

View File

@@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of Qt Creator. ** This file is part of Qt Creator.
@@ -25,25 +25,28 @@
#pragma once #pragma once
#include "utils_global.h"
#include <QList> #include <QList>
namespace Core { QT_BEGIN_NAMESPACE
namespace Internal { class QProcess;
QT_END_NAMESPACE
class CorePlugin; namespace Utils {
class ProcessReaper;
class ProcessReapers final namespace Internal { class Reaper; }
class QTCREATOR_UTILS_EXPORT ProcessReaper final
{ {
public:
ProcessReaper();
~ProcessReaper();
static void reap(QProcess *process, int timeoutMs = 500);
private: private:
~ProcessReapers(); QList<Internal::Reaper *> m_reapers;
ProcessReapers(); friend class Internal::Reaper;
QList<ProcessReaper *> m_reapers;
friend class CorePlugin;
friend class ProcessReaper;
}; };
} // namespace Internal } // namespace Utils
} // namespace Core

View File

@@ -31,6 +31,7 @@
#include "launcherinterface.h" #include "launcherinterface.h"
#include "launcherpackets.h" #include "launcherpackets.h"
#include "launchersocket.h" #include "launchersocket.h"
#include "processreaper.h"
#include "qtcassert.h" #include "qtcassert.h"
#include "stringutils.h" #include "stringutils.h"
@@ -278,86 +279,90 @@ class QProcessImpl : public ProcessInterface
public: public:
QProcessImpl(QObject *parent, ProcessMode processMode) QProcessImpl(QObject *parent, ProcessMode processMode)
: ProcessInterface(parent, processMode) : ProcessInterface(parent, processMode)
, m_process(parent) , m_process(new ProcessHelper(parent))
{ {
connect(&m_process, &QProcess::started, connect(m_process, &QProcess::started,
this, &QProcessImpl::handleStarted); this, &QProcessImpl::handleStarted);
connect(&m_process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), connect(m_process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
this, &ProcessInterface::finished); this, &ProcessInterface::finished);
connect(&m_process, &QProcess::errorOccurred, connect(m_process, &QProcess::errorOccurred,
this, &ProcessInterface::errorOccurred); this, &ProcessInterface::errorOccurred);
connect(&m_process, &QProcess::readyReadStandardOutput, connect(m_process, &QProcess::readyReadStandardOutput,
this, &ProcessInterface::readyReadStandardOutput); this, &ProcessInterface::readyReadStandardOutput);
connect(&m_process, &QProcess::readyReadStandardError, connect(m_process, &QProcess::readyReadStandardError,
this, &ProcessInterface::readyReadStandardError); this, &ProcessInterface::readyReadStandardError);
} }
~QProcessImpl() override
{
ProcessReaper::reap(m_process);
}
QByteArray readAllStandardOutput() override { return m_process.readAllStandardOutput(); } QByteArray readAllStandardOutput() override { return m_process->readAllStandardOutput(); }
QByteArray readAllStandardError() override { return m_process.readAllStandardError(); } QByteArray readAllStandardError() override { return m_process->readAllStandardError(); }
void setProcessEnvironment(const QProcessEnvironment &environment) override void setProcessEnvironment(const QProcessEnvironment &environment) override
{ m_process.setProcessEnvironment(environment); } { m_process->setProcessEnvironment(environment); }
void setWorkingDirectory(const QString &dir) override void setWorkingDirectory(const QString &dir) override
{ m_process.setWorkingDirectory(dir); } { m_process->setWorkingDirectory(dir); }
void start(const QString &program, const QStringList &arguments, const QByteArray &writeData) override void start(const QString &program, const QStringList &arguments, const QByteArray &writeData) override
{ {
m_processStartHandler.setProcessMode(processMode()); m_processStartHandler.setProcessMode(processMode());
m_processStartHandler.setWriteData(writeData); m_processStartHandler.setWriteData(writeData);
if (isBelowNormalPriority()) if (isBelowNormalPriority())
m_processStartHandler.setBelowNormalPriority(&m_process); m_processStartHandler.setBelowNormalPriority(m_process);
m_processStartHandler.setNativeArguments(&m_process, nativeArguments()); m_processStartHandler.setNativeArguments(m_process, nativeArguments());
if (isLowPriority()) if (isLowPriority())
m_process.setLowPriority(); m_process->setLowPriority();
if (isUnixTerminalDisabled()) if (isUnixTerminalDisabled())
m_process.setUnixTerminalDisabled(); m_process->setUnixTerminalDisabled();
m_process.start(program, arguments, m_processStartHandler.openMode()); m_process->start(program, arguments, m_processStartHandler.openMode());
m_processStartHandler.handleProcessStart(&m_process); m_processStartHandler.handleProcessStart(m_process);
} }
void terminate() override void terminate() override
{ m_process.terminate(); } { m_process->terminate(); }
void kill() override void kill() override
{ m_process.kill(); } { m_process->kill(); }
void close() override void close() override
{ m_process.close(); } { m_process->close(); }
qint64 write(const QByteArray &data) override qint64 write(const QByteArray &data) override
{ return m_process.write(data); } { return m_process->write(data); }
void setStandardInputFile(const QString &fileName) override void setStandardInputFile(const QString &fileName) override
{ m_process.setStandardInputFile(fileName); } { m_process->setStandardInputFile(fileName); }
void setProcessChannelMode(QProcess::ProcessChannelMode mode) override void setProcessChannelMode(QProcess::ProcessChannelMode mode) override
{ m_process.setProcessChannelMode(mode); } { m_process->setProcessChannelMode(mode); }
QString program() const override QString program() const override
{ return m_process.program(); } { return m_process->program(); }
QProcess::ProcessError error() const override QProcess::ProcessError error() const override
{ return m_process.error(); } { return m_process->error(); }
QProcess::ProcessState state() const override QProcess::ProcessState state() const override
{ return m_process.state(); } { return m_process->state(); }
qint64 processId() const override qint64 processId() const override
{ return m_process.processId(); } { return m_process->processId(); }
int exitCode() const override int exitCode() const override
{ return m_process.exitCode(); } { return m_process->exitCode(); }
QProcess::ExitStatus exitStatus() const override QProcess::ExitStatus exitStatus() const override
{ return m_process.exitStatus(); } { return m_process->exitStatus(); }
QString errorString() const override QString errorString() const override
{ return m_process.errorString(); } { return m_process->errorString(); }
void setErrorString(const QString &str) override void setErrorString(const QString &str) override
{ m_process.setErrorString(str); } { m_process->setErrorString(str); }
bool waitForStarted(int msecs) override bool waitForStarted(int msecs) override
{ return m_process.waitForStarted(msecs); } { return m_process->waitForStarted(msecs); }
bool waitForReadyRead(int msecs) override bool waitForReadyRead(int msecs) override
{ return m_process.waitForReadyRead(msecs); } { return m_process->waitForReadyRead(msecs); }
bool waitForFinished(int msecs) override bool waitForFinished(int msecs) override
{ return m_process.waitForFinished(msecs); } { return m_process->waitForFinished(msecs); }
private: private:
void handleStarted() void handleStarted()
{ {
m_processStartHandler.handleProcessStarted(&m_process); m_processStartHandler.handleProcessStarted(m_process);
emit started(); emit started();
} }
ProcessHelper m_process; ProcessHelper *m_process;
ProcessStartHandler m_processStartHandler; ProcessStartHandler m_processStartHandler;
}; };

View File

@@ -84,6 +84,7 @@ SOURCES += \
$$PWD/json.cpp \ $$PWD/json.cpp \
$$PWD/portlist.cpp \ $$PWD/portlist.cpp \
$$PWD/processhandle.cpp \ $$PWD/processhandle.cpp \
$$PWD/processreaper.cpp \
$$PWD/processutils.cpp \ $$PWD/processutils.cpp \
$$PWD/appmainwindow.cpp \ $$PWD/appmainwindow.cpp \
$$PWD/basetreeview.cpp \ $$PWD/basetreeview.cpp \
@@ -222,6 +223,7 @@ HEADERS += \
$$PWD/runextensions.h \ $$PWD/runextensions.h \
$$PWD/portlist.h \ $$PWD/portlist.h \
$$PWD/processhandle.h \ $$PWD/processhandle.h \
$$PWD/processreaper.h \
$$PWD/processutils.h \ $$PWD/processutils.h \
$$PWD/appmainwindow.h \ $$PWD/appmainwindow.h \
$$PWD/basetreeview.h \ $$PWD/basetreeview.h \

View File

@@ -218,6 +218,8 @@ Project {
"portlist.h", "portlist.h",
"processhandle.cpp", "processhandle.cpp",
"processhandle.h", "processhandle.h",
"processreaper.cpp",
"processreaper.h",
"processutils.cpp", "processutils.cpp",
"processutils.h", "processutils.h",
"progressindicator.cpp", "progressindicator.cpp",

View File

@@ -39,7 +39,6 @@
#include <android/androidconstants.h> #include <android/androidconstants.h>
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h> #include <coreplugin/progressmanager/progressmanager.h>
#include <coreplugin/reaper.h>
#include <cppeditor/cppeditorconstants.h> #include <cppeditor/cppeditorconstants.h>
#include <cppeditor/cppprojectupdater.h> #include <cppeditor/cppprojectupdater.h>
#include <cppeditor/generatedcodemodelsupport.h> #include <cppeditor/generatedcodemodelsupport.h>

View File

@@ -28,7 +28,6 @@
#include "cmakeparser.h" #include "cmakeparser.h"
#include <coreplugin/progressmanager/progressmanager.h> #include <coreplugin/progressmanager/progressmanager.h>
#include <coreplugin/reaper.h>
#include <projectexplorer/buildsystem.h> #include <projectexplorer/buildsystem.h>
#include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/taskhub.h> #include <projectexplorer/taskhub.h>
@@ -57,11 +56,7 @@ CMakeProcess::CMakeProcess()
CMakeProcess::~CMakeProcess() CMakeProcess::~CMakeProcess()
{ {
if (m_process) { m_process.reset();
m_process->disconnect();
Core::Reaper::reap(m_process.release());
}
m_parser.flush(); m_parser.flush();
if (m_future) { if (m_future) {

View File

@@ -135,7 +135,6 @@ add_qtc_plugin(Core
progressmanager/progressbar.cpp progressmanager/progressbar.h progressmanager/progressbar.cpp progressmanager/progressbar.h
progressmanager/progressmanager.cpp progressmanager/progressmanager.h progressmanager/progressmanager_p.h progressmanager/progressmanager.cpp progressmanager/progressmanager.h progressmanager/progressmanager_p.h
progressmanager/progressview.cpp progressmanager/progressview.h progressmanager/progressview.cpp progressmanager/progressview.h
reaper.cpp reaper.h reaper_p.h
rightpane.cpp rightpane.h rightpane.cpp rightpane.h
settingsdatabase.cpp settingsdatabase.h settingsdatabase.cpp settingsdatabase.h
shellcommand.cpp shellcommand.h shellcommand.cpp shellcommand.h

View File

@@ -32,7 +32,6 @@
#include "iwizardfactory.h" #include "iwizardfactory.h"
#include "mainwindow.h" #include "mainwindow.h"
#include "modemanager.h" #include "modemanager.h"
#include "reaper_p.h"
#include "themechooser.h" #include "themechooser.h"
#include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/actionmanager.h>
@@ -96,13 +95,11 @@ CorePlugin::CorePlugin()
qRegisterMetaType<Utils::CommandLine>(); qRegisterMetaType<Utils::CommandLine>();
qRegisterMetaType<Utils::FilePath>(); qRegisterMetaType<Utils::FilePath>();
m_instance = this; m_instance = this;
m_reaper = new ProcessReapers;
setupSystemEnvironment(); setupSystemEnvironment();
} }
CorePlugin::~CorePlugin() CorePlugin::~CorePlugin()
{ {
delete m_reaper;
IWizardFactory::destroyFeatureProvider(); IWizardFactory::destroyFeatureProvider();
Find::destroy(); Find::destroy();

View File

@@ -44,7 +44,6 @@ namespace Internal {
class EditMode; class EditMode;
class MainWindow; class MainWindow;
class Locator; class Locator;
class ProcessReapers;
class CorePlugin : public ExtensionSystem::IPlugin class CorePlugin : public ExtensionSystem::IPlugin
{ {
@@ -93,7 +92,6 @@ private:
MainWindow *m_mainWindow = nullptr; MainWindow *m_mainWindow = nullptr;
EditMode *m_editMode = nullptr; EditMode *m_editMode = nullptr;
Locator *m_locator = nullptr; Locator *m_locator = nullptr;
ProcessReapers *m_reaper = nullptr;
Utils::Environment m_startupSystemEnvironment; Utils::Environment m_startupSystemEnvironment;
Utils::EnvironmentItems m_environmentChanges; Utils::EnvironmentItems m_environmentChanges;
}; };

View File

@@ -54,7 +54,6 @@ SOURCES += corejsextensions.cpp \
progressmanager/progressview.cpp \ progressmanager/progressview.cpp \
progressmanager/progressbar.cpp \ progressmanager/progressbar.cpp \
progressmanager/futureprogress.cpp \ progressmanager/futureprogress.cpp \
reaper.cpp \
coreplugin.cpp \ coreplugin.cpp \
modemanager.cpp \ modemanager.cpp \
basefilewizard.cpp \ basefilewizard.cpp \
@@ -161,8 +160,6 @@ HEADERS += corejsextensions.h \
progressmanager/progressbar.h \ progressmanager/progressbar.h \
progressmanager/futureprogress.h \ progressmanager/futureprogress.h \
progressmanager/progressmanager.h \ progressmanager/progressmanager.h \
reaper.h \
reaper_p.h \
icontext.h \ icontext.h \
icore.h \ icore.h \
imode.h \ imode.h \

View File

@@ -144,9 +144,6 @@ Project {
"plugindialog.h", "plugindialog.h",
"plugininstallwizard.cpp", "plugininstallwizard.cpp",
"plugininstallwizard.h", "plugininstallwizard.h",
"reaper.cpp",
"reaper.h",
"reaper_p.h",
"rightpane.cpp", "rightpane.cpp",
"rightpane.h", "rightpane.h",
"settingsdatabase.cpp", "settingsdatabase.cpp",

View File

@@ -27,7 +27,6 @@
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h> #include <coreplugin/messagemanager.h>
#include <coreplugin/reaper.h>
#include <utils/macroexpander.h> #include <utils/macroexpander.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
@@ -199,12 +198,7 @@ void ExecuteFilter::removeProcess()
return; return;
m_taskQueue.dequeue(); m_taskQueue.dequeue();
m_process->disconnect(); delete m_process;
if (m_process->state() == QProcess::NotRunning)
m_process->deleteLater();
else
Reaper::reap(m_process);
m_process = nullptr; m_process = nullptr;
} }

View File

@@ -28,7 +28,6 @@
#include "../messagemanager.h" #include "../messagemanager.h"
#include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/reaper.h>
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/commandline.h> #include <utils/commandline.h>
#include <utils/environment.h> #include <utils/environment.h>
@@ -156,10 +155,7 @@ void SpotlightIterator::killProcess()
QMutexLocker lock(&m_mutex); QMutexLocker lock(&m_mutex);
m_finished = true; m_finished = true;
m_waitForItems.wakeAll(); m_waitForItems.wakeAll();
if (m_process->state() == QProcess::NotRunning) m_process.reset();
m_process.reset();
else
Reaper::reap(m_process.release());
} }
void SpotlightIterator::ensureNext() void SpotlightIterator::ensureNext()

View File

@@ -1,38 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 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 "core_global.h"
namespace Utils { class QtcProcess; }
namespace Core {
namespace Reaper {
CORE_EXPORT void reap(Utils::QtcProcess *p, int timeoutMs = 500);
} // namespace Reaper
} // namespace Core

View File

@@ -34,8 +34,6 @@
#include "target.h" #include "target.h"
#include "task.h" #include "task.h"
#include <coreplugin/reaper.h>
#include <utils/fileutils.h> #include <utils/fileutils.h>
#include <utils/outputformatter.h> #include <utils/outputformatter.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
@@ -255,7 +253,7 @@ void AbstractProcessStep::setLowPriority()
void AbstractProcessStep::doCancel() void AbstractProcessStep::doCancel()
{ {
Core::Reaper::reap(d->m_process.release()); d->m_process.reset();
} }
ProcessParameters *AbstractProcessStep::processParameters() ProcessParameters *AbstractProcessStep::processParameters()

View File

@@ -11,6 +11,10 @@ add_qtc_executable(qtcreator_processlauncher
processlauncher-main.cpp processlauncher-main.cpp
${UTILSDIR}/launcherpackets.cpp ${UTILSDIR}/launcherpackets.cpp
${UTILSDIR}/launcherpackets.h ${UTILSDIR}/launcherpackets.h
${UTILSDIR}/processreaper.cpp
${UTILSDIR}/processreaper.h
${UTILSDIR}/processutils.cpp ${UTILSDIR}/processutils.cpp
${UTILSDIR}/processutils.h ${UTILSDIR}/processutils.h
${UTILSDIR}/qtcassert.cpp
${UTILSDIR}/qtcassert.h
) )

View File

@@ -26,12 +26,12 @@
#include "launchersockethandler.h" #include "launchersockethandler.h"
#include "launcherlogging.h" #include "launcherlogging.h"
#include "processreaper.h"
#include "processutils.h" #include "processutils.h"
#include <QCoreApplication> #include <QCoreApplication>
#include <QLocalSocket> #include <QLocalSocket>
#include <QProcess> #include <QProcess>
#include <QTimer>
namespace Utils { namespace Utils {
namespace Internal { namespace Internal {
@@ -41,43 +41,13 @@ class Process : public ProcessHelper
Q_OBJECT Q_OBJECT
public: public:
Process(quintptr token, QObject *parent = nullptr) : Process(quintptr token, QObject *parent = nullptr) :
ProcessHelper(parent), m_token(token), m_stopTimer(new QTimer(this)) ProcessHelper(parent), m_token(token) { }
{
m_stopTimer->setSingleShot(true);
connect(m_stopTimer, &QTimer::timeout, this, &Process::cancel);
}
void cancel()
{
if (state() == QProcess::NotRunning) {
deleteLater();
return;
}
switch (m_stopState) {
case StopState::Inactive:
m_stopState = StopState::Terminating;
m_stopTimer->start(3000);
terminate();
break;
case StopState::Terminating:
m_stopState = StopState::Killing;
m_stopTimer->start(3000);
kill();
break;
case StopState::Killing:
m_stopState = StopState::Inactive;
deleteLater(); // TODO: employ something like Core::Reaper here
break;
}
}
quintptr token() const { return m_token; } quintptr token() const { return m_token; }
ProcessStartHandler *processStartHandler() { return &m_processStartHandler; } ProcessStartHandler *processStartHandler() { return &m_processStartHandler; }
private: private:
const quintptr m_token; const quintptr m_token;
QTimer * const m_stopTimer;
enum class StopState { Inactive, Terminating, Killing } m_stopState = StopState::Inactive;
ProcessStartHandler m_processStartHandler; ProcessStartHandler m_processStartHandler;
}; };
@@ -97,7 +67,7 @@ LauncherSocketHandler::~LauncherSocketHandler()
m_socket->close(); m_socket->close();
} }
for (auto it = m_processes.cbegin(); it != m_processes.cend(); ++it) for (auto it = m_processes.cbegin(); it != m_processes.cend(); ++it)
it.value()->disconnect(); ProcessReaper::reap(it.value());
} }
void LauncherSocketHandler::start() void LauncherSocketHandler::start()
@@ -269,13 +239,15 @@ void LauncherSocketHandler::handleStopPacket()
{ {
Process * const process = m_processes.value(m_packetParser.token()); Process * const process = m_processes.value(m_packetParser.token());
if (!process) { if (!process) {
logWarn("got stop request for unknown process"); // This can happen when the process finishes on its own at about the same time the client
// sends the request. In this case the process was already deleted.
logDebug("got stop request for unknown process");
return; return;
} }
if (process->state() == QProcess::NotRunning) { if (process->state() == QProcess::NotRunning) {
// This can happen if the process finishes on its own at about the same time the client // This shouldn't happen, since as soon as process finishes or error occurrs
// sends the request. // the process is being removed.
logDebug("got stop request when process was not running"); logWarn("got stop request when process was not running");
} else { } else {
// We got the client request to stop the starting / running process. // We got the client request to stop the starting / running process.
// We report process exit to the client. // We report process exit to the client.
@@ -331,11 +303,7 @@ void LauncherSocketHandler::removeProcess(quintptr token)
Process *process = it.value(); Process *process = it.value();
m_processes.erase(it); m_processes.erase(it);
process->disconnect(); ProcessReaper::reap(process);
if (process->state() != QProcess::NotRunning)
process->cancel();
else
process->deleteLater();
} }
Process *LauncherSocketHandler::senderProcess() const Process *LauncherSocketHandler::senderProcess() const

View File

@@ -25,6 +25,7 @@
#include "launcherlogging.h" #include "launcherlogging.h"
#include "launchersockethandler.h" #include "launchersockethandler.h"
#include "processreaper.h"
#include <QtCore/qcoreapplication.h> #include <QtCore/qcoreapplication.h>
#include <QtCore/qtimer.h> #include <QtCore/qtimer.h>
@@ -51,6 +52,7 @@ int main(int argc, char *argv[])
return 1; return 1;
} }
Utils::ProcessReaper processReaper;
Utils::Internal::LauncherSocketHandler launcher(app.arguments().constLast()); Utils::Internal::LauncherSocketHandler launcher(app.arguments().constLast());
QTimer::singleShot(0, &launcher, &Utils::Internal::LauncherSocketHandler::start); QTimer::singleShot(0, &launcher, &Utils::Internal::LauncherSocketHandler::start);
return app.exec(); return app.exec();

View File

@@ -13,11 +13,15 @@ HEADERS += \
launcherlogging.h \ launcherlogging.h \
launchersockethandler.h \ launchersockethandler.h \
$$UTILS_DIR/launcherpackets.h \ $$UTILS_DIR/launcherpackets.h \
$$UTILS_DIR/processutils.h $$UTILS_DIR/processreaper.h \
$$UTILS_DIR/processutils.h \
$$UTILS_DIR/qtcassert.h
SOURCES += \ SOURCES += \
launcherlogging.cpp \ launcherlogging.cpp \
launchersockethandler.cpp \ launchersockethandler.cpp \
processlauncher-main.cpp \ processlauncher-main.cpp \
$$UTILS_DIR/launcherpackets.cpp \ $$UTILS_DIR/launcherpackets.cpp \
$$UTILS_DIR/processutils.cpp $$UTILS_DIR/processreaper.cpp \
$$UTILS_DIR/processutils.cpp \
$$UTILS_DIR/qtcassert.cpp

View File

@@ -23,8 +23,12 @@ QtcTool {
files: [ files: [
"launcherpackets.cpp", "launcherpackets.cpp",
"launcherpackets.h", "launcherpackets.h",
"processreaper.cpp",
"processreaper.h",
"processutils.cpp", "processutils.cpp",
"processutils.h", "processutils.h",
"qtcassert.cpp",
"qtcassert.h",
] ]
} }
} }

View File

@@ -27,6 +27,7 @@
#include <utils/hostosinfo.h> #include <utils/hostosinfo.h>
#include <utils/launcherinterface.h> #include <utils/launcherinterface.h>
#include <utils/porting.h> #include <utils/porting.h>
#include <utils/processreaper.h>
#include <utils/qtcprocess.h> #include <utils/qtcprocess.h>
#include <utils/stringutils.h> #include <utils/stringutils.h>
@@ -203,6 +204,7 @@ private slots:
private: private:
void iteratorEditsHelper(OsType osType); void iteratorEditsHelper(OsType osType);
Utils::ProcessReaper processReaper;
Environment envWindows; Environment envWindows;
Environment envLinux; Environment envLinux;

View File

@@ -30,6 +30,7 @@
#include <sqliteglobal.h> #include <sqliteglobal.h>
#include <utils/launcherinterface.h> #include <utils/launcherinterface.h>
#include <utils/processreaper.h>
#include <utils/temporarydirectory.h> #include <utils/temporarydirectory.h>
#include <QGuiApplication> #include <QGuiApplication>
@@ -60,6 +61,7 @@ int main(int argc, char *argv[])
Sqlite::Database::activateLogging(); Sqlite::Database::activateLogging();
QGuiApplication application(argc, argv); QGuiApplication application(argc, argv);
Utils::ProcessReaper processReaper;
Utils::LauncherInterface::startLauncher(qApp->applicationDirPath() + '/' Utils::LauncherInterface::startLauncher(qApp->applicationDirPath() + '/'
+ QLatin1String(TEST_RELATIVE_LIBEXEC_PATH)); + QLatin1String(TEST_RELATIVE_LIBEXEC_PATH));
auto cleanup = qScopeGuard([] { Utils::LauncherInterface::stopLauncher(); }); auto cleanup = qScopeGuard([] { Utils::LauncherInterface::stopLauncher(); });