diff options
author | Joerg Bornemann <joerg.bornemann@nokia.com> | 2012-01-16 12:02:47 +0100 |
---|---|---|
committer | Qt by Nokia <qt-info@nokia.com> | 2012-01-26 18:45:14 +0100 |
commit | dcdab683d6bfb6b0ad81b5903236ae70a71246e4 (patch) | |
tree | fd5767cb3a56b5361290360fc6e254c77f3a4710 /src | |
parent | 9efbc9f60a511e902d3f8b368296212b43222f52 (diff) |
QProcess/Win: use asynchronous I/O for reading stdout and stderr
This saves us from using the 100 ms polling timer to read process output.
Task-number: QTBUG-23012
Change-Id: I3f9398d7a4d30826d2d89ac04bd2fd031500ff57
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@nokia.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/corelib/io/qprocess.cpp | 21 | ||||
-rw-r--r-- | src/corelib/io/qprocess_p.h | 6 | ||||
-rw-r--r-- | src/corelib/io/qprocess_unix.cpp | 5 | ||||
-rw-r--r-- | src/corelib/io/qprocess_win.cpp | 138 | ||||
-rw-r--r-- | src/corelib/io/qprocess_wince.cpp | 5 |
5 files changed, 116 insertions, 59 deletions
diff --git a/src/corelib/io/qprocess.cpp b/src/corelib/io/qprocess.cpp index fe9810e7cb..124be801f5 100644 --- a/src/corelib/io/qprocess.cpp +++ b/src/corelib/io/qprocess.cpp @@ -761,8 +761,6 @@ QProcessPrivate::QProcessPrivate() exitStatus = QProcess::NormalExit; startupSocketNotifier = 0; deathNotifier = 0; - notifier = 0; - pipeWriter = 0; childStartedPipe[0] = INVALID_Q_PIPE; childStartedPipe[1] = INVALID_Q_PIPE; deathPipe[0] = INVALID_Q_PIPE; @@ -773,6 +771,9 @@ QProcessPrivate::QProcessPrivate() emittedReadyRead = false; emittedBytesWritten = false; #ifdef Q_OS_WIN + notifier = 0; + stdoutReader = 0; + stderrReader = 0; pipeWriter = 0; processFinishedNotifier = 0; #endif // Q_OS_WIN @@ -843,13 +844,15 @@ void QProcessPrivate::cleanup() qDeleteInEventHandler(deathNotifier); deathNotifier = 0; } +#ifdef Q_OS_WIN if (notifier) { qDeleteInEventHandler(notifier); notifier = 0; } - destroyPipe(stdoutChannel.pipe); - destroyPipe(stderrChannel.pipe); - destroyPipe(stdinChannel.pipe); +#endif + destroyChannel(&stdoutChannel); + destroyChannel(&stderrChannel); + destroyChannel(&stdinChannel); destroyPipe(childStartedPipe); destroyPipe(deathPipe); #ifdef Q_OS_UNIX @@ -873,7 +876,7 @@ bool QProcessPrivate::_q_canReadStandardOutput() if (available == 0) { if (stdoutChannel.notifier) stdoutChannel.notifier->setEnabled(false); - destroyPipe(stdoutChannel.pipe); + destroyChannel(&stdoutChannel); #if defined QPROCESS_DEBUG qDebug("QProcessPrivate::canReadStandardOutput(), 0 bytes available"); #endif @@ -928,7 +931,7 @@ bool QProcessPrivate::_q_canReadStandardError() if (available == 0) { if (stderrChannel.notifier) stderrChannel.notifier->setEnabled(false); - destroyPipe(stderrChannel.pipe); + destroyChannel(&stderrChannel); return false; } @@ -981,7 +984,7 @@ bool QProcessPrivate::_q_canWrite() qint64 written = writeToStdin(writeBuffer.readPointer(), writeBuffer.nextDataBlockSize()); if (written < 0) { - destroyPipe(stdinChannel.pipe); + destroyChannel(&stdinChannel); processError = QProcess::WriteError; q->setErrorString(QProcess::tr("Error writing to process")); emit q->error(processError); @@ -1125,7 +1128,7 @@ void QProcessPrivate::closeWriteChannel() // instead. flushPipeWriter(); #endif - destroyPipe(stdinChannel.pipe); + destroyChannel(&stdinChannel); } /*! diff --git a/src/corelib/io/qprocess_p.h b/src/corelib/io/qprocess_p.h index a0599564db..bd9d75fa8c 100644 --- a/src/corelib/io/qprocess_p.h +++ b/src/corelib/io/qprocess_p.h @@ -74,6 +74,7 @@ typedef int Q_PIPE; QT_BEGIN_NAMESPACE class QSocketNotifier; +class QWindowsPipeReader; class QWindowsPipeWriter; class QWinEventNotifier; class QTimer; @@ -291,14 +292,19 @@ public: Q_PIPE childStartedPipe[2]; Q_PIPE deathPipe[2]; void destroyPipe(Q_PIPE pipe[2]); + void destroyChannel(Channel *channel); QSocketNotifier *startupSocketNotifier; QSocketNotifier *deathNotifier; +#ifdef Q_OS_WIN // the wonderful windows notifier QTimer *notifier; + QWindowsPipeReader *stdoutReader; + QWindowsPipeReader *stderrReader; QWindowsPipeWriter *pipeWriter; QWinEventNotifier *processFinishedNotifier; +#endif void startProcess(); #if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) diff --git a/src/corelib/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp index a862ce1015..6d5c71af90 100644 --- a/src/corelib/io/qprocess_unix.cpp +++ b/src/corelib/io/qprocess_unix.cpp @@ -357,6 +357,11 @@ void QProcessPrivate::destroyPipe(int *pipe) } } +void QProcessPrivate::destroyChannel(Channel *channel) +{ + destroyPipe(channel->pipe); +} + /* Create the pipes to a QProcessPrivate::Channel. diff --git a/src/corelib/io/qprocess_win.cpp b/src/corelib/io/qprocess_win.cpp index 8981f19e31..251d4f507a 100644 --- a/src/corelib/io/qprocess_win.cpp +++ b/src/corelib/io/qprocess_win.cpp @@ -41,10 +41,12 @@ #include "qprocess.h" #include "qprocess_p.h" +#include "qwindowspipereader_p.h" #include "qwindowspipewriter_p.h" #include <qdatetime.h> #include <qdir.h> +#include <qelapsedtimer.h> #include <qfileinfo.h> #include <qregexp.h> #include <qtimer.h> @@ -152,6 +154,26 @@ bool QProcessPrivate::createChannel(Channel &channel) qt_create_pipe(channel.pipe, isStdInChannel); else duplicateStdWriteChannel(channel.pipe, (&channel == &stdoutChannel) ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE); + + QWindowsPipeReader *pipeReader = 0; + if (&channel == &stdoutChannel) { + if (!stdoutReader) { + stdoutReader = new QWindowsPipeReader(q); + q->connect(stdoutReader, SIGNAL(readyRead()), SLOT(_q_canReadStandardOutput())); + } + pipeReader = stdoutReader; + } else if (&channel == &stderrChannel) { + if (!stderrReader) { + stderrReader = new QWindowsPipeReader(q); + q->connect(stderrReader, SIGNAL(readyRead()), SLOT(_q_canReadStandardError())); + } + pipeReader = stderrReader; + } + if (pipeReader) { + pipeReader->setHandle(channel.pipe[0]); + pipeReader->startAsyncRead(); + } + return true; } else if (channel.type == Channel::Redirect) { // we're redirecting the channel to/from a file @@ -263,11 +285,6 @@ bool QProcessPrivate::createChannel(Channel &channel) void QProcessPrivate::destroyPipe(Q_PIPE pipe[2]) { - if (pipe[0] == stdinChannel.pipe[0] && pipe[1] == stdinChannel.pipe[1] && pipeWriter) { - delete pipeWriter; - pipeWriter = 0; - } - if (pipe[0] != INVALID_Q_PIPE) { CloseHandle(pipe[0]); pipe[0] = INVALID_Q_PIPE; @@ -278,6 +295,26 @@ void QProcessPrivate::destroyPipe(Q_PIPE pipe[2]) } } +void QProcessPrivate::destroyChannel(Channel *channel) +{ + if (channel == &stdinChannel) { + if (pipeWriter) { + delete pipeWriter; + pipeWriter = 0; + } + } else if (channel == &stdoutChannel) { + if (stdoutReader) { + stdoutReader->deleteLater(); + stdoutReader = 0; + } + } else if (channel == &stderrChannel) { + if (stderrReader) { + stderrReader->deleteLater(); + stderrReader = 0; + } + } + destroyPipe(channel->pipe); +} static QString qt_create_commandline(const QString &program, const QStringList &arguments) { @@ -502,8 +539,10 @@ qint64 QProcessPrivate::bytesAvailableFromStdout() const if (stdoutChannel.pipe[0] == INVALID_Q_PIPE) return 0; - DWORD bytesAvail = 0; - PeekNamedPipe(stdoutChannel.pipe[0], 0, 0, 0, &bytesAvail, 0); + if (!stdoutReader) + return 0; + + DWORD bytesAvail = stdoutReader->bytesAvailable(); #if defined QPROCESS_DEBUG qDebug("QProcessPrivate::bytesAvailableFromStdout() == %d", bytesAvail); #endif @@ -515,8 +554,10 @@ qint64 QProcessPrivate::bytesAvailableFromStderr() const if (stderrChannel.pipe[0] == INVALID_Q_PIPE) return 0; - DWORD bytesAvail = 0; - PeekNamedPipe(stderrChannel.pipe[0], 0, 0, 0, &bytesAvail, 0); + if (!stderrReader) + return 0; + + DWORD bytesAvail = stderrReader->bytesAvailable(); #if defined QPROCESS_DEBUG qDebug("QProcessPrivate::bytesAvailableFromStderr() == %d", bytesAvail); #endif @@ -525,22 +566,12 @@ qint64 QProcessPrivate::bytesAvailableFromStderr() const qint64 QProcessPrivate::readFromStdout(char *data, qint64 maxlen) { - DWORD read = qMin(maxlen, bytesAvailableFromStdout()); - DWORD bytesRead = 0; - - if (read > 0 && !ReadFile(stdoutChannel.pipe[0], data, read, &bytesRead, 0)) - return -1; - return bytesRead; + return stdoutReader ? stdoutReader->read(data, maxlen) : 0; } qint64 QProcessPrivate::readFromStderr(char *data, qint64 maxlen) { - DWORD read = qMin(maxlen, bytesAvailableFromStderr()); - DWORD bytesRead = 0; - - if (read > 0 && !ReadFile(stderrChannel.pipe[0], data, read, &bytesRead, 0)) - return -1; - return bytesRead; + return stderrReader ? stderrReader->read(data, maxlen) : 0; } @@ -583,6 +614,30 @@ bool QProcessPrivate::waitForStarted(int) return false; } +static bool drainOutputPipes(QProcessPrivate *d) +{ + if (!d->stdoutReader && !d->stderrReader) + return false; + + bool readyReadEmitted = false; + forever { + bool readOperationActive = false; + if (d->stdoutReader) { + readyReadEmitted |= d->stdoutReader->waitForReadyRead(0); + readOperationActive = d->stdoutReader->isReadOperationActive(); + } + if (d->stderrReader) { + readyReadEmitted |= d->stderrReader->waitForReadyRead(0); + readOperationActive |= d->stderrReader->isReadOperationActive(); + } + if (!readOperationActive) + break; + Sleep(100); + } + + return readyReadEmitted; +} + bool QProcessPrivate::waitForReadyRead(int msecs) { Q_Q(QProcess); @@ -594,26 +649,18 @@ bool QProcessPrivate::waitForReadyRead(int msecs) return false; if (pipeWriter && pipeWriter->waitForWrite(0)) timer.resetIncrements(); - bool readyReadEmitted = false; - if (bytesAvailableFromStdout() != 0) { - readyReadEmitted = _q_canReadStandardOutput() ? true : readyReadEmitted; - timer.resetIncrements(); - } - if (bytesAvailableFromStderr() != 0) { - readyReadEmitted = _q_canReadStandardError() ? true : readyReadEmitted; - timer.resetIncrements(); - } - - if (readyReadEmitted) + if (processChannelMode != QProcess::ForwardedChannels + && ((stdoutReader && stdoutReader->waitForReadyRead(0)) + || (stderrReader && stderrReader->waitForReadyRead(0)))) return true; if (!pid) return false; if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) { - // find the return value if there is noew data to read + bool readyReadEmitted = drainOutputPipes(this); _q_processDied(); - return false; + return readyReadEmitted; } Sleep(timer.nextSleepTime()); @@ -694,7 +741,6 @@ bool QProcessPrivate::waitForBytesWritten(int msecs) return false; } - bool QProcessPrivate::waitForFinished(int msecs) { Q_Q(QProcess); @@ -709,21 +755,18 @@ bool QProcessPrivate::waitForFinished(int msecs) return false; if (pipeWriter && pipeWriter->waitForWrite(0)) timer.resetIncrements(); - - if (bytesAvailableFromStdout() != 0) { - _q_canReadStandardOutput(); + if (stdoutReader && stdoutReader->waitForReadyRead(0)) timer.resetIncrements(); - } - - if (bytesAvailableFromStderr() != 0) { - _q_canReadStandardError(); + if (stderrReader && stderrReader->waitForReadyRead(0)) timer.resetIncrements(); - } - if (!pid) + if (!pid) { + drainOutputPipes(this); return true; + } if (WaitForSingleObject(pid->hProcess, timer.nextSleepTime()) == WAIT_OBJECT_0) { + drainOutputPipes(this); _q_processDied(); return true; } @@ -731,6 +774,7 @@ bool QProcessPrivate::waitForFinished(int msecs) if (timer.hasTimedOut()) break; } + processError = QProcess::Timedout; q->setErrorString(QProcess::tr("Process operation timed out")); return false; @@ -790,12 +834,6 @@ void QProcessPrivate::_q_notified() if (!writeBuffer.isEmpty() && (!pipeWriter || pipeWriter->waitForWrite(0))) _q_canWrite(); - if (bytesAvailableFromStdout()) - _q_canReadStandardOutput(); - - if (bytesAvailableFromStderr()) - _q_canReadStandardError(); - if (processState != QProcess::NotRunning) notifier->start(NOTIFYTIMEOUT); } diff --git a/src/corelib/io/qprocess_wince.cpp b/src/corelib/io/qprocess_wince.cpp index e1148b34b8..9f1b05016d 100644 --- a/src/corelib/io/qprocess_wince.cpp +++ b/src/corelib/io/qprocess_wince.cpp @@ -60,6 +60,11 @@ void QProcessPrivate::destroyPipe(Q_PIPE pipe[2]) Q_UNUSED(pipe); } +void QProcessPrivate::destroyChannel(Channel *channel) +{ + Q_UNUSED(channel); +} + static QString qt_create_commandline(const QString &program, const QStringList &arguments) { QString args; |