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:
parent
36b7c3cd2b
commit
28ee554b37
@ -1006,6 +1006,7 @@ bool QProcessPrivate::_q_processDied()
|
|||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
|
drainOutputPipes();
|
||||||
if (processFinishedNotifier)
|
if (processFinishedNotifier)
|
||||||
processFinishedNotifier->setEnabled(false);
|
processFinishedNotifier->setEnabled(false);
|
||||||
#endif
|
#endif
|
||||||
|
@ -317,6 +317,7 @@ public:
|
|||||||
bool waitForDeadChild();
|
bool waitForDeadChild();
|
||||||
#endif
|
#endif
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
|
bool drainOutputPipes();
|
||||||
void flushPipeWriter();
|
void flushPipeWriter();
|
||||||
qint64 pipeWriterBytesToWrite() const;
|
qint64 pipeWriterBytesToWrite() const;
|
||||||
#endif
|
#endif
|
||||||
|
@ -625,21 +625,21 @@ bool QProcessPrivate::waitForStarted(int)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool drainOutputPipes(QProcessPrivate *d)
|
bool QProcessPrivate::drainOutputPipes()
|
||||||
{
|
{
|
||||||
if (!d->stdoutReader && !d->stderrReader)
|
if (!stdoutReader && !stderrReader)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
bool readyReadEmitted = false;
|
bool readyReadEmitted = false;
|
||||||
forever {
|
forever {
|
||||||
bool readOperationActive = false;
|
bool readOperationActive = false;
|
||||||
if (d->stdoutReader) {
|
if (stdoutReader) {
|
||||||
readyReadEmitted |= d->stdoutReader->waitForReadyRead(0);
|
readyReadEmitted |= stdoutReader->waitForReadyRead(0);
|
||||||
readOperationActive = d->stdoutReader->isReadOperationActive();
|
readOperationActive = stdoutReader->isReadOperationActive();
|
||||||
}
|
}
|
||||||
if (d->stderrReader) {
|
if (stderrReader) {
|
||||||
readyReadEmitted |= d->stderrReader->waitForReadyRead(0);
|
readyReadEmitted |= stderrReader->waitForReadyRead(0);
|
||||||
readOperationActive |= d->stderrReader->isReadOperationActive();
|
readOperationActive |= stderrReader->isReadOperationActive();
|
||||||
}
|
}
|
||||||
if (!readOperationActive)
|
if (!readOperationActive)
|
||||||
break;
|
break;
|
||||||
@ -669,7 +669,7 @@ bool QProcessPrivate::waitForReadyRead(int msecs)
|
|||||||
if (!pid)
|
if (!pid)
|
||||||
return false;
|
return false;
|
||||||
if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) {
|
if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) {
|
||||||
bool readyReadEmitted = drainOutputPipes(this);
|
bool readyReadEmitted = drainOutputPipes();
|
||||||
_q_processDied();
|
_q_processDied();
|
||||||
return readyReadEmitted;
|
return readyReadEmitted;
|
||||||
}
|
}
|
||||||
@ -772,12 +772,12 @@ bool QProcessPrivate::waitForFinished(int msecs)
|
|||||||
timer.resetIncrements();
|
timer.resetIncrements();
|
||||||
|
|
||||||
if (!pid) {
|
if (!pid) {
|
||||||
drainOutputPipes(this);
|
drainOutputPipes();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WaitForSingleObject(pid->hProcess, timer.nextSleepTime()) == WAIT_OBJECT_0) {
|
if (WaitForSingleObject(pid->hProcess, timer.nextSleepTime()) == WAIT_OBJECT_0) {
|
||||||
drainOutputPipes(this);
|
drainOutputPipes();
|
||||||
_q_processDied();
|
_q_processDied();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -152,6 +152,7 @@ private slots:
|
|||||||
void invalidProgramString_data();
|
void invalidProgramString_data();
|
||||||
void invalidProgramString();
|
void invalidProgramString();
|
||||||
void onlyOneStartedSignal();
|
void onlyOneStartedSignal();
|
||||||
|
void finishProcessBeforeReadingDone();
|
||||||
|
|
||||||
// keep these at the end, since they use lots of processes and sometimes
|
// 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)
|
// 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);
|
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
|
#endif //QT_NO_PROCESS
|
||||||
|
|
||||||
QTEST_MAIN(tst_QProcess)
|
QTEST_MAIN(tst_QProcess)
|
||||||
|
Loading…
Reference in New Issue
Block a user