QProcess/Win: drain output pipes on process finish

If a process dies before all output is read into the internal buffer
of QProcess, we might lose data. Therefore we must drain the output
pipes like we already do in the synchronous wait functions.

Task-number: QTBUG-30843

Change-Id: I8bbc5265275c9ebd33218ba600267ae87d93ed61
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@digia.com>
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
This commit is contained in:
Joerg Bornemann 2013-04-30 15:31:46 +02:00 committed by The Qt Project
parent 36b7c3cd2b
commit 28ee554b37
4 changed files with 47 additions and 11 deletions

View File

@ -1006,6 +1006,7 @@ bool QProcessPrivate::_q_processDied()
return false;
#endif
#ifdef Q_OS_WIN
drainOutputPipes();
if (processFinishedNotifier)
processFinishedNotifier->setEnabled(false);
#endif

View File

@ -317,6 +317,7 @@ public:
bool waitForDeadChild();
#endif
#ifdef Q_OS_WIN
bool drainOutputPipes();
void flushPipeWriter();
qint64 pipeWriterBytesToWrite() const;
#endif

View File

@ -625,21 +625,21 @@ bool QProcessPrivate::waitForStarted(int)
return false;
}
static bool drainOutputPipes(QProcessPrivate *d)
bool QProcessPrivate::drainOutputPipes()
{
if (!d->stdoutReader && !d->stderrReader)
if (!stdoutReader && !stderrReader)
return false;
bool readyReadEmitted = false;
forever {
bool readOperationActive = false;
if (d->stdoutReader) {
readyReadEmitted |= d->stdoutReader->waitForReadyRead(0);
readOperationActive = d->stdoutReader->isReadOperationActive();
if (stdoutReader) {
readyReadEmitted |= stdoutReader->waitForReadyRead(0);
readOperationActive = stdoutReader->isReadOperationActive();
}
if (d->stderrReader) {
readyReadEmitted |= d->stderrReader->waitForReadyRead(0);
readOperationActive |= d->stderrReader->isReadOperationActive();
if (stderrReader) {
readyReadEmitted |= stderrReader->waitForReadyRead(0);
readOperationActive |= stderrReader->isReadOperationActive();
}
if (!readOperationActive)
break;
@ -669,7 +669,7 @@ bool QProcessPrivate::waitForReadyRead(int msecs)
if (!pid)
return false;
if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) {
bool readyReadEmitted = drainOutputPipes(this);
bool readyReadEmitted = drainOutputPipes();
_q_processDied();
return readyReadEmitted;
}
@ -772,12 +772,12 @@ bool QProcessPrivate::waitForFinished(int msecs)
timer.resetIncrements();
if (!pid) {
drainOutputPipes(this);
drainOutputPipes();
return true;
}
if (WaitForSingleObject(pid->hProcess, timer.nextSleepTime()) == WAIT_OBJECT_0) {
drainOutputPipes(this);
drainOutputPipes();
_q_processDied();
return true;
}

View File

@ -152,6 +152,7 @@ private slots:
void invalidProgramString_data();
void invalidProgramString();
void onlyOneStartedSignal();
void finishProcessBeforeReadingDone();
// keep these at the end, since they use lots of processes and sometimes
// caused obscure failures to occur in tests that followed them (esp. on the Mac)
@ -2168,6 +2169,39 @@ void tst_QProcess::onlyOneStartedSignal()
QCOMPARE(spyFinished.count(), 1);
}
//-----------------------------------------------------------------------------
class BlockOnReadStdOut : public QObject
{
Q_OBJECT
public:
BlockOnReadStdOut(QProcess *process)
{
connect(process, SIGNAL(readyReadStandardOutput()), SLOT(block()));
}
public slots:
void block()
{
QThread::sleep(1);
}
};
void tst_QProcess::finishProcessBeforeReadingDone()
{
QProcess process;
BlockOnReadStdOut blocker(&process);
QEventLoop loop;
connect(&process, SIGNAL(finished(int)), &loop, SLOT(quit()));
process.start("testProcessOutput/testProcessOutput");
QVERIFY(process.waitForStarted());
loop.exec();
QStringList lines = QString::fromLocal8Bit(process.readAllStandardOutput()).split(
QRegExp(QStringLiteral("[\r\n]")), QString::SkipEmptyParts);
QVERIFY(!lines.isEmpty());
QCOMPARE(lines.last(), QStringLiteral("10239 -this is a number"));
}
#endif //QT_NO_PROCESS
QTEST_MAIN(tst_QProcess)