Details
-
Bug
-
Resolution: Done
-
P1: Critical
-
4.8.4, 4.8.5, 5.2.0, 5.2.1
-
None
-
OS X 10.9
-
-
54491a2571669f0c232d2ab9926991ff9ecf3403 f15d9e6ccd006a2b47b50ae755fbe9534748a2eb b2216bbe06b8be2bef6d8bc2ffff1337f6d23358
Description
The following example runs a system tool and prints its output on finished().
Then it does the same again. A thousand times.
At some point the QProcess will not be able to start the tool anymore and the example vainly waits for the finished() signal (started() or error() are also not emitted).
#include <QCoreApplication> #include <QTimer> #include <QProcess> #include <QDebug> #include <QThread> #include <stdio.h> static const bool reuseProcess = false; // true or false here doesn't make a difference class ProcessStarter : public QObject { Q_OBJECT public: ProcessStarter(int count) : processesToStart(count) , current(0) { createProcess(); } public slots: QProcess *createProcess() { QProcess *process = new QProcess(this); connect(process, SIGNAL(finished(int)), SLOT(onProcessFinished())); connect(process, SIGNAL(error(QProcess::ProcessError)), SLOT(onProcessError())); return process; } void start() { QProcess *process = 0; if (reuseProcess) { process = findChild<QProcess *>(); } else { process = createProcess(); } ++current; qDebug() << "start" << current; process->start("/usr/bin/sw_vers"); } void onProcessError() { QProcess *process = qobject_cast<QProcess*>(sender()); qDebug() << "error" << current << process->errorString(); if (!reuseProcess) { process->disconnect(); process->deleteLater(); } emit finished(); } void onProcessFinished() { QProcess *process = qobject_cast<QProcess*>(sender()); qDebug() << "finish" << current; QString str = QString::fromLocal8Bit(process->readAllStandardOutput()); if (!reuseProcess) { process->disconnect(); process->deleteLater(); } emit output(str); if (processesToStart == current) { qDebug() << "all processes finished"; emit finished(); } else { QMetaObject::invokeMethod(this, "start", Qt::QueuedConnection); } } signals: void finished(); void output(QString); private: int processesToStart; int current; }; class OutputSink : public QObject { Q_OBJECT int count; public: OutputSink() : count(0) { } public slots: void printOutput(QString str) { ++count; // Doing anything else here but printing to stdout/stderr doesn't cause the problem. fprintf(stdout, "%d %s\n", count, str.toUtf8().data()); fflush(stdout); } }; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); ProcessStarter starter(1000); OutputSink sink; #if 1 // Move the ProcessStarter to a different thread. QThread t; t.connect(&starter, SIGNAL(finished()), SLOT(quit())); a.connect(&t, SIGNAL(finished()), SLOT(quit())); starter.moveToThread(&t); t.start(); #else // Everything in the main thread. Works fine. a.connect(&starter, SIGNAL(finished()), SLOT(quit())); #endif sink.connect(&starter, SIGNAL(output(QString)), SLOT(printOutput(QString)), Qt::QueuedConnection); QMetaObject::invokeMethod(&starter, "start", Qt::QueuedConnection); return a.exec(); } #include "main.moc"
Attaching to the forked child reveals that the child is locked in the fileno(stdout) call in QProcessPrivate::execChild.
Note that the hang does not happen on OS X 10.8 or older.