summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/io/qprocess.cpp3
-rw-r--r--src/corelib/io/qprocess_win.cpp28
-rw-r--r--src/corelib/io/qwindowspipereader.cpp82
-rw-r--r--src/corelib/io/qwindowspipereader_p.h8
4 files changed, 89 insertions, 32 deletions
diff --git a/src/corelib/io/qprocess.cpp b/src/corelib/io/qprocess.cpp
index 9170b72e56..05aa0f021f 100644
--- a/src/corelib/io/qprocess.cpp
+++ b/src/corelib/io/qprocess.cpp
@@ -1142,9 +1142,10 @@ void QProcessPrivate::_q_processDied()
// so the data is made available before we announce death.
#ifdef Q_OS_WIN
drainOutputPipes();
-#endif
+#else
_q_canReadStandardOutput();
_q_canReadStandardError();
+#endif
// Slots connected to signals emitted by the functions called above
// might call waitFor*(), which would synchronously reap the process.
diff --git a/src/corelib/io/qprocess_win.cpp b/src/corelib/io/qprocess_win.cpp
index e65d4683be..dc8c50127d 100644
--- a/src/corelib/io/qprocess_win.cpp
+++ b/src/corelib/io/qprocess_win.cpp
@@ -642,28 +642,18 @@ bool QProcessPrivate::waitForStarted(const QDeadlineTimer &)
bool QProcessPrivate::drainOutputPipes()
{
- if (!stdoutChannel.reader && !stderrChannel.reader)
- return false;
+ bool readyReadEmitted = false;
- bool someReadyReadEmitted = false;
- forever {
- bool readyReadEmitted = false;
- bool readOperationActive = false;
- if (stdoutChannel.reader) {
- readyReadEmitted |= stdoutChannel.reader->waitForReadyRead(0);
- readOperationActive = stdoutChannel.reader && stdoutChannel.reader->isReadOperationActive();
- }
- if (stderrChannel.reader) {
- readyReadEmitted |= stderrChannel.reader->waitForReadyRead(0);
- readOperationActive |= stderrChannel.reader && stderrChannel.reader->isReadOperationActive();
- }
- someReadyReadEmitted |= readyReadEmitted;
- if (!readOperationActive || !readyReadEmitted)
- break;
- QThread::yieldCurrentThread();
+ if (stdoutChannel.reader) {
+ stdoutChannel.reader->drainAndStop();
+ readyReadEmitted = _q_canReadStandardOutput();
+ }
+ if (stderrChannel.reader) {
+ stderrChannel.reader->drainAndStop();
+ readyReadEmitted |= _q_canReadStandardError();
}
- return someReadyReadEmitted;
+ return readyReadEmitted;
}
bool QProcessPrivate::waitForReadyRead(const QDeadlineTimer &deadline)
diff --git a/src/corelib/io/qwindowspipereader.cpp b/src/corelib/io/qwindowspipereader.cpp
index c20909766d..b525e88282 100644
--- a/src/corelib/io/qwindowspipereader.cpp
+++ b/src/corelib/io/qwindowspipereader.cpp
@@ -44,6 +44,8 @@
QT_BEGIN_NAMESPACE
+static const DWORD minReadBufferSize = 4096;
+
QWindowsPipeReader::Overlapped::Overlapped(QWindowsPipeReader *reader)
: pipeReader(reader)
{
@@ -61,7 +63,8 @@ QWindowsPipeReader::QWindowsPipeReader(QObject *parent)
overlapped(this),
readBufferMaxSize(0),
actualReadBufferSize(0),
- stopped(true),
+ bytesPending(0),
+ state(Stopped),
readSequenceStarted(false),
notifiedCalled(false),
pipeBroken(false),
@@ -84,6 +87,7 @@ void QWindowsPipeReader::setHandle(HANDLE hPipeReadEnd)
{
readBuffer.clear();
actualReadBufferSize = 0;
+ bytesPending = 0;
handle = hPipeReadEnd;
pipeBroken = false;
}
@@ -94,7 +98,25 @@ void QWindowsPipeReader::setHandle(HANDLE hPipeReadEnd)
*/
void QWindowsPipeReader::stop()
{
- stopped = true;
+ state = Stopped;
+ cancelAsyncRead();
+}
+
+/*!
+ Stops the asynchronous read sequence.
+ Reads all pending bytes into the internal buffer.
+ */
+void QWindowsPipeReader::drainAndStop()
+{
+ state = Draining;
+ cancelAsyncRead();
+}
+
+/*!
+ Stops the asynchronous read sequence.
+ */
+void QWindowsPipeReader::cancelAsyncRead()
+{
if (readSequenceStarted) {
if (!CancelIoEx(handle, &overlapped)) {
const DWORD dwError = GetLastError();
@@ -135,7 +157,7 @@ qint64 QWindowsPipeReader::read(char *data, qint64 maxlen)
}
if (!pipeBroken) {
- if (!readSequenceStarted && !stopped)
+ if (state == Running)
startAsyncRead();
if (readSoFar == 0)
return -2; // signal EWOULDBLOCK
@@ -171,7 +193,7 @@ void QWindowsPipeReader::notified(DWORD errorCode, DWORD numberOfBytesRead)
pipeBroken = true;
break;
case ERROR_OPERATION_ABORTED:
- if (stopped)
+ if (state != Running)
break;
Q_FALLTHROUGH();
default:
@@ -183,16 +205,34 @@ void QWindowsPipeReader::notified(DWORD errorCode, DWORD numberOfBytesRead)
// After the reader was stopped, the only reason why this function can be called is the
// completion of a cancellation. No signals should be emitted, and no new read sequence should
// be started in this case.
- if (stopped)
+ if (state == Stopped)
return;
if (pipeBroken) {
- emit pipeClosed();
+ emitPipeClosed();
return;
}
actualReadBufferSize += numberOfBytesRead;
readBuffer.truncate(actualReadBufferSize);
+
+ // Read all pending data from the pipe's buffer in 'Draining' state.
+ if (state == Draining) {
+ // Determine the number of pending bytes on the first iteration.
+ if (bytesPending == 0)
+ bytesPending = checkPipeState();
+ else
+ bytesPending -= numberOfBytesRead;
+
+ if (bytesPending == 0) // all data received
+ return; // unblock waitForNotification() in cancelAsyncRead()
+
+ startAsyncReadHelper(bytesPending);
+ if (readSequenceStarted)
+ notifiedCalled = false; // wait for more data
+ return;
+ }
+
startAsyncRead();
if (!readyReadPending) {
readyReadPending = true;
@@ -202,12 +242,25 @@ void QWindowsPipeReader::notified(DWORD errorCode, DWORD numberOfBytesRead)
/*!
\internal
- Reads data from the pipe into the readbuffer.
+ Starts an asynchronous read sequence on the pipe.
*/
void QWindowsPipeReader::startAsyncRead()
{
- const DWORD minReadBufferSize = 4096;
- qint64 bytesToRead = qMax(checkPipeState(), minReadBufferSize);
+ if (readSequenceStarted)
+ return;
+
+ state = Running;
+ startAsyncReadHelper(qMax(checkPipeState(), minReadBufferSize));
+}
+
+/*!
+ \internal
+ Starts a new read sequence.
+ */
+void QWindowsPipeReader::startAsyncReadHelper(qint64 bytesToRead)
+{
+ Q_ASSERT(bytesToRead != 0);
+
if (pipeBroken)
return;
@@ -222,7 +275,6 @@ void QWindowsPipeReader::startAsyncRead()
char *ptr = readBuffer.reserve(bytesToRead);
- stopped = false;
readSequenceStarted = true;
overlapped.clear();
if (!ReadFileEx(handle, ptr, bytesToRead, &overlapped, &readFileCompleted)) {
@@ -267,7 +319,7 @@ DWORD QWindowsPipeReader::checkPipeState()
return bytes;
if (!pipeBroken) {
pipeBroken = true;
- emit pipeClosed();
+ emitPipeClosed();
}
return 0;
}
@@ -299,6 +351,14 @@ void QWindowsPipeReader::emitPendingReadyRead()
}
}
+void QWindowsPipeReader::emitPipeClosed()
+{
+ // We are not allowed to emit signals in either 'Stopped'
+ // or 'Draining' state.
+ if (state == Running)
+ emit pipeClosed();
+}
+
/*!
Waits for the completion of the asynchronous read operation.
Returns \c true, if we've emitted the readyRead signal (non-recursive case)
diff --git a/src/corelib/io/qwindowspipereader_p.h b/src/corelib/io/qwindowspipereader_p.h
index 2842343597..c61018d87d 100644
--- a/src/corelib/io/qwindowspipereader_p.h
+++ b/src/corelib/io/qwindowspipereader_p.h
@@ -68,6 +68,7 @@ public:
void setHandle(HANDLE hPipeReadEnd);
void startAsyncRead();
void stop();
+ void drainAndStop();
void setMaxReadBufferSize(qint64 size) { readBufferMaxSize = size; }
qint64 maxReadBufferSize() const { return readBufferMaxSize; }
@@ -88,12 +89,15 @@ Q_SIGNALS:
void _q_queueReadyRead(QPrivateSignal);
private:
+ void startAsyncReadHelper(qint64 bytesToRead);
+ void cancelAsyncRead();
static void CALLBACK readFileCompleted(DWORD errorCode, DWORD numberOfBytesTransfered,
OVERLAPPED *overlappedBase);
void notified(DWORD errorCode, DWORD numberOfBytesRead);
DWORD checkPipeState();
bool waitForNotification(int timeout);
void emitPendingReadyRead();
+ void emitPipeClosed();
class Overlapped : public OVERLAPPED
{
@@ -109,7 +113,9 @@ private:
qint64 readBufferMaxSize;
QRingBuffer readBuffer;
qint64 actualReadBufferSize;
- bool stopped;
+ qint64 bytesPending;
+
+ enum State { Stopped, Running, Draining } state;
bool readSequenceStarted;
bool notifiedCalled;
bool pipeBroken;