7#include <QtCore/QCoreApplication>
8#include <QtCore/QDebug>
9#include <QtCore/QMutex>
10#include <QtCore/QThread>
11#include <QtCore/QTimer>
12#include <QtCore/QWaitCondition>
20class ProcessReaperPrivate;
22class ProcessReaper final
25 static void reap(QProcess *process,
int timeoutMs = 500);
30 static ProcessReaper *instance();
33 ProcessReaperPrivate *m_private;
36static const int s_timeoutThreshold = 10000;
38static QString execWithArguments(QProcess *process)
41 commandLine.append(process->program());
42 commandLine.append(process->arguments());
43 return commandLine.join(QChar::Space);
48 QProcess *m_process =
nullptr;
57 Reaper(
const ReaperSetup &reaperSetup) : m_reaperSetup(reaperSetup) {}
62 connect(m_reaperSetup.m_process, &QProcess::finished,
this, &Reaper::handleFinished);
74 m_reaperSetup.m_process->terminate();
78 void kill() { m_reaperSetup.m_process->kill(); }
82 if (m_reaperSetup.m_process->state() != QProcess::NotRunning)
86 const int timeout = m_timer.elapsed();
87 if (
timeout > s_timeoutThreshold) {
88 qWarning() <<
"Finished parallel reaping of" << execWithArguments(m_reaperSetup.m_process)
89 <<
"in" << (
timeout / 1000.0) <<
"seconds.";
102 qWarning() <<
"Finished process still running...";
107 void handleTerminateTimeout()
114 bool m_finished =
false;
116 const ReaperSetup m_reaperSetup;
119class ProcessReaperPrivate :
public QObject
125 void scheduleReap(
const ReaperSetup &reaperSetup)
128 qWarning() <<
"Can't schedule reap from the reaper internal thread.";
131 m_reaperSetupList.append(reaperSetup);
139 qWarning() <<
"Can't wait for finished from the reaper internal thread.";
144 if (m_reaperList.isEmpty())
147 m_waitCondition.wait(&m_mutex);
152 QList<ReaperSetup> takeReaperSetupList()
155 return std::exchange(m_reaperSetupList, {});
161 const QList<ReaperSetup> reaperSetupList = takeReaperSetupList();
162 if (reaperSetupList.isEmpty())
164 for (
const ReaperSetup &reaperSetup : reaperSetupList)
169 void reap(
const ReaperSetup &reaperSetup)
171 Reaper *reaper =
new Reaper(reaperSetup);
172 connect(reaper, &Reaper::finished,
this, [
this, reaper, process = reaperSetup.m_process] {
173 QMutexLocker locker(&m_mutex);
174 const bool isRemoved = m_reaperList.removeOne(reaper);
176 qWarning() <<
"Reaper list doesn't contain the finished process.";
180 if (m_reaperList.isEmpty())
181 m_waitCondition.wakeOne();
186 m_reaperList.append(reaper);
194 QList<ReaperSetup> m_reaperSetupList;
195 QList<Reaper *> m_reaperList;
198static ProcessReaper *s_instance =
nullptr;
199static QMutex s_instanceMutex;
202ProcessReaper *ProcessReaper::instance()
205 s_instance =
new ProcessReaper;
209ProcessReaper::ProcessReaper()
210 : m_private(new ProcessReaperPrivate)
212 m_private->moveToThread(&m_thread);
215 m_thread.moveToThread(
qApp->thread());
218ProcessReaper::~ProcessReaper()
221 qWarning() <<
"Destructing process reaper from non-main thread.";
223 instance()->m_private->waitForFinished();
228void ProcessReaper::reap(QProcess *process,
int timeoutMs)
234 qWarning() <<
"Can't reap process from non-process's thread.";
238 process->disconnect();
239 if (process->state() == QProcess::NotRunning) {
246 process->setParent(
nullptr);
249 ProcessReaperPrivate *
priv = instance()->m_private;
251 process->moveToThread(
priv->thread());
252 ReaperSetup reaperSetup {process, timeoutMs};
253 priv->scheduleReap(reaperSetup);
256void QProcessDeleter::deleteAll()
260 s_instance =
nullptr;
263void QProcessDeleter::operator()(QProcess *process)
265 ProcessReaper::reap(process);
274#if QT_CONFIG(process)
276#include "qprocesstask.moc"
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
void deleteLater()
\threadsafe
\macro QT_RESTRICTED_CAST_FROM_ASCII
static QThread * currentThread()
void finished(QPrivateSignal)
bool singleShot
whether the timer is a single-shot timer
Combined button and popup list for selecting options.
QTextStream & flush(QTextStream &stream)
Calls QTextStream::flush() on stream and returns stream.
@ BlockingQueuedConnection
\inmodule TaskingSolution
GLbitfield GLuint64 timeout
[4]
connect(quitButton, &QPushButton::clicked, &app, &QCoreApplication::quit, Qt::QueuedConnection)