Commit 1628856d authored by Tobias Hunger's avatar Tobias Hunger

Core: Fix Reaper blocking shutdown

The eventloop is blocked while the destructor of ReaperPrivate is running.
So drive the ProcessReaper by hand instead.

Change-Id: I691a28f27455f58ae5807540746ffa1aa2783fed
Reviewed-by: Tim Jenssen's avatarTim Jenssen <tim.jenssen@qt.io>
parent 04aa6f67
......@@ -29,6 +29,8 @@
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
#include <QThread>
namespace Core {
namespace Internal {
......@@ -36,6 +38,8 @@ static ReaperPrivate *d = nullptr;
ProcessReaper::ProcessReaper(QProcess *p, int timeoutMs) : m_process(p)
{
d->m_reapers.append(this);
m_iterationTimer.setInterval(timeoutMs);
m_iterationTimer.setSingleShot(true);
connect(&m_iterationTimer, &QTimer::timeout, this, &ProcessReaper::nextIteration);
......@@ -44,11 +48,31 @@ ProcessReaper::ProcessReaper(QProcess *p, int timeoutMs) : m_process(p)
m_futureInterface.reportStarted();
}
ProcessReaper::~ProcessReaper()
{
d->m_reapers.removeOne(this);
}
int ProcessReaper::timeoutMs() const
{
const int remaining = m_iterationTimer.remainingTime();
if (remaining < 0)
return m_iterationTimer.interval();
m_iterationTimer.stop();
return remaining;
}
bool ProcessReaper::isFinished() const
{
return !m_process;
}
void ProcessReaper::nextIteration()
{
QProcess::ProcessState state = m_process->state();
if (state == QProcess::NotRunning || m_emergencyCounter > 5) {
delete m_process;
m_process = nullptr;
m_futureInterface.reportFinished();
return;
}
......@@ -76,6 +100,30 @@ ReaperPrivate::ReaperPrivate()
ReaperPrivate::~ReaperPrivate()
{
while (!m_reapers.isEmpty()) {
int alreadyWaited = 0;
QList<ProcessReaper *> toDelete;
// push reapers along:
foreach (ProcessReaper *pr, m_reapers) {
const int timeoutMs = pr->timeoutMs();
if (alreadyWaited < timeoutMs) {
const unsigned long toSleep = static_cast<unsigned long>(timeoutMs - alreadyWaited);
QThread::msleep(toSleep);
alreadyWaited += toSleep;
}
pr->nextIteration();
if (pr->isFinished())
toDelete.append(pr);
}
// Clean out reapers that finished in the meantime
qDeleteAll(toDelete);
toDelete.clear();
}
d = nullptr;
}
......@@ -90,23 +138,7 @@ void reap(QProcess *process, int timeoutMs)
QTC_ASSERT(Internal::d, return);
auto reaper = new Internal::ProcessReaper(process, timeoutMs);
QFuture<void> f = reaper->future();
Internal::d->m_synchronizer.addFuture(f);
auto watcher = new QFutureWatcher<void>();
QObject::connect(watcher, &QFutureWatcher<void>::finished, [watcher, reaper]() {
watcher->deleteLater();
const QList<QFuture<void>> futures = Utils::filtered(Internal::d->m_synchronizer.futures(),
[reaper](const QFuture<void> &f) { return reaper->future() != f; });
for (const QFuture<void> &f : futures)
Internal::d->m_synchronizer.addFuture(f);
delete reaper;
});
watcher->setFuture(f);
new Internal::ProcessReaper(process, timeoutMs);
}
} // namespace Reaper
......
......@@ -26,8 +26,7 @@
#pragma once
#include <QObject>
#include <QFutureSynchronizer>
#include <QFutureWatcher>
#include <QFutureInterface>
#include <QProcess>
#include <QTimer>
......@@ -42,13 +41,14 @@ class ProcessReaper : public QObject
public:
ProcessReaper(QProcess *p, int timeoutMs);
~ProcessReaper();
QFuture<void> future() { return m_futureInterface.future(); }
private:
int timeoutMs() const;
bool isFinished() const;
void nextIteration();
QTimer m_iterationTimer;
private:
mutable QTimer m_iterationTimer;
QFutureInterface<void> m_futureInterface;
QProcess *m_process;
int m_emergencyCounter = 0;
......@@ -59,7 +59,7 @@ class ReaperPrivate {
public:
~ReaperPrivate();
QFutureSynchronizer<void> m_synchronizer;
QList<ProcessReaper *> m_reapers;
private:
ReaperPrivate();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment