use QWinOverlappedIoNotifier in QWindowsPipeReader

Change-Id: I71d5feaa07e8febd6c562be1dc6d7790e3a1606c
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@nokia.com>
This commit is contained in:
Joerg Bornemann 2012-01-16 12:26:40 +01:00 committed by Qt by Nokia
parent 096b49bc1e
commit 3bc42e860d
2 changed files with 83 additions and 91 deletions

View File

@ -40,11 +40,11 @@
****************************************************************************/
#include "qwindowspipereader_p.h"
#include "qwinoverlappedionotifier_p.h"
#include <qdebug.h>
#include <qelapsedtimer.h>
#include <qeventloop.h>
#include <qtimer.h>
#include <qwineventnotifier.h>
QT_BEGIN_NAMESPACE
@ -53,21 +53,23 @@ QWindowsPipeReader::QWindowsPipeReader(QObject *parent)
handle(INVALID_HANDLE_VALUE),
readBufferMaxSize(0),
actualReadBufferSize(0),
readSequenceStarted(false),
emitReadyReadTimer(new QTimer(this)),
pipeBroken(false)
pipeBroken(false),
readyReadEmitted(false)
{
emitReadyReadTimer->setSingleShot(true);
connect(emitReadyReadTimer, SIGNAL(timeout()), SIGNAL(readyRead()));
ZeroMemory(&overlapped, sizeof(overlapped));
overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
dataReadNotifier = new QWinEventNotifier(overlapped.hEvent, this);
connect(dataReadNotifier, SIGNAL(activated(HANDLE)), SLOT(readEventSignalled()));
dataReadNotifier = new QWinOverlappedIoNotifier(this);
connect(dataReadNotifier, &QWinOverlappedIoNotifier::notified, this, &QWindowsPipeReader::notified);
}
QWindowsPipeReader::~QWindowsPipeReader()
{
CloseHandle(overlapped.hEvent);
if (readSequenceStarted) {
CancelIoEx(handle, &overlapped);
dataReadNotifier->waitForNotified(-1);
}
}
/*!
@ -78,8 +80,13 @@ void QWindowsPipeReader::setHandle(HANDLE hPipeReadEnd)
readBuffer.clear();
actualReadBufferSize = 0;
handle = hPipeReadEnd;
ZeroMemory(&overlapped, sizeof(overlapped));
pipeBroken = false;
dataReadNotifier->setEnabled(true);
readyReadEmitted = false;
if (hPipeReadEnd != INVALID_HANDLE_VALUE) {
dataReadNotifier->setHandle(hPipeReadEnd);
dataReadNotifier->setEnabled(true);
}
}
/*!
@ -92,7 +99,6 @@ void QWindowsPipeReader::stop()
dataReadNotifier->setEnabled(false);
readSequenceStarted = false;
handle = INVALID_HANDLE_VALUE;
ResetEvent(overlapped.hEvent);
}
/*!
@ -149,19 +155,18 @@ bool QWindowsPipeReader::canReadLine() const
/*!
\internal
Will be called whenever the read operation completes.
Returns true, if readyRead() has been emitted.
*/
bool QWindowsPipeReader::readEventSignalled()
void QWindowsPipeReader::notified(DWORD numberOfBytesRead, DWORD errorCode)
{
if (!completeAsyncRead()) {
if (!completeAsyncRead(numberOfBytesRead, errorCode)) {
pipeBroken = true;
emit pipeClosed();
return false;
return;
}
startAsyncRead();
emitReadyReadTimer->stop();
readyReadEmitted = true;
emit readyRead();
return true;
}
/*!
@ -170,56 +175,49 @@ bool QWindowsPipeReader::readEventSignalled()
*/
void QWindowsPipeReader::startAsyncRead()
{
do {
DWORD bytesToRead = checkPipeState();
if (pipeBroken)
return;
DWORD bytesToRead = qMax(checkPipeState(), minReadBufferSize);
if (pipeBroken)
return;
if (readBufferMaxSize && bytesToRead > (readBufferMaxSize - readBuffer.size())) {
bytesToRead = readBufferMaxSize - readBuffer.size();
if (bytesToRead == 0) {
// There are no bytes in the pipe but we need to
// start the overlapped read with some buffer size.
bytesToRead = initialReadBufferSize;
// Buffer is full. User must read data from the buffer
// before we can read more from the pipe.
return;
}
}
if (readBufferMaxSize && bytesToRead > (readBufferMaxSize - readBuffer.size())) {
bytesToRead = readBufferMaxSize - readBuffer.size();
if (bytesToRead == 0) {
// Buffer is full. User must read data from the buffer
// before we can read more from the pipe.
char *ptr = readBuffer.reserve(bytesToRead);
readSequenceStarted = true;
if (ReadFile(handle, ptr, bytesToRead, NULL, &overlapped)) {
// We get notified by the QWinOverlappedIoNotifier - even in the synchronous case.
return;
} else {
switch (GetLastError()) {
case ERROR_IO_PENDING:
// This is not an error. We're getting notified, when data arrives.
return;
case ERROR_MORE_DATA:
// This is not an error. The synchronous read succeeded.
// We're connected to a message mode pipe and the message
// didn't fit into the pipe's system buffer.
// We're getting notified by the QWinOverlappedIoNotifier.
break;
case ERROR_PIPE_NOT_CONNECTED:
{
// It may happen, that the other side closes the connection directly
// after writing data. Then we must set the appropriate socket state.
pipeBroken = true;
emit pipeClosed();
return;
}
default:
emit winError(GetLastError(), QLatin1String("QWindowsPipeReader::startAsyncRead"));
return;
}
char *ptr = readBuffer.reserve(bytesToRead);
readSequenceStarted = true;
if (ReadFile(handle, ptr, bytesToRead, NULL, &overlapped)) {
completeAsyncRead();
} else {
switch (GetLastError()) {
case ERROR_IO_PENDING:
// This is not an error. We're getting notified, when data arrives.
return;
case ERROR_MORE_DATA:
// This is not an error. The synchronous read succeeded.
// We're connected to a message mode pipe and the message
// didn't fit into the pipe's system buffer.
completeAsyncRead();
break;
case ERROR_PIPE_NOT_CONNECTED:
{
// It may happen, that the other side closes the connection directly
// after writing data. Then we must set the appropriate socket state.
pipeBroken = true;
emit pipeClosed();
return;
}
default:
emit winError(GetLastError(), QLatin1String("QWindowsPipeReader::startAsyncRead"));
return;
}
}
} while (!readSequenceStarted);
}
}
/*!
@ -227,26 +225,24 @@ void QWindowsPipeReader::startAsyncRead()
Sets the correct size of the read buffer after a read operation.
Returns false, if an error occurred or the connection dropped.
*/
bool QWindowsPipeReader::completeAsyncRead()
bool QWindowsPipeReader::completeAsyncRead(DWORD bytesRead, DWORD errorCode)
{
ResetEvent(overlapped.hEvent);
readSequenceStarted = false;
DWORD bytesRead;
if (!GetOverlappedResult(handle, &overlapped, &bytesRead, TRUE)) {
switch (GetLastError()) {
case ERROR_MORE_DATA:
// This is not an error. We're connected to a message mode
// pipe and the message didn't fit into the pipe's system
// buffer. We will read the remaining data in the next call.
break;
case ERROR_BROKEN_PIPE:
case ERROR_PIPE_NOT_CONNECTED:
return false;
default:
emit winError(GetLastError(), QLatin1String("QWindowsPipeReader::completeAsyncRead"));
return false;
}
switch (errorCode) {
case ERROR_SUCCESS:
break;
case ERROR_MORE_DATA:
// This is not an error. We're connected to a message mode
// pipe and the message didn't fit into the pipe's system
// buffer. We will read the remaining data in the next call.
break;
case ERROR_BROKEN_PIPE:
case ERROR_PIPE_NOT_CONNECTED:
return false;
default:
emit winError(errorCode, QLatin1String("QWindowsPipeReader::completeAsyncRead"));
return false;
}
actualReadBufferSize += bytesRead;
@ -281,17 +277,11 @@ DWORD QWindowsPipeReader::checkPipeState()
*/
bool QWindowsPipeReader::waitForReadyRead(int msecs)
{
Q_ASSERT(readSequenceStarted);
DWORD result = WaitForSingleObject(overlapped.hEvent, msecs == -1 ? INFINITE : msecs);
switch (result) {
case WAIT_OBJECT_0:
return readEventSignalled();
case WAIT_TIMEOUT:
return false;
}
qWarning("QWindowsPipeReader::waitForReadyRead WaitForSingleObject failed with error code %d.", int(GetLastError()));
return false;
if (!readSequenceStarted)
return false;
readyReadEmitted = false;
dataReadNotifier->waitForNotified(msecs);
return readyReadEmitted;
}
/*!

View File

@ -66,7 +66,7 @@ QT_BEGIN_NAMESPACE
QT_MODULE(Core)
class QWinEventNotifier;
class QWinOverlappedIoNotifier;
class Q_CORE_EXPORT QWindowsPipeReader : public QObject
{
@ -89,7 +89,7 @@ public:
bool waitForPipeClosed(int msecs);
void startAsyncRead();
bool completeAsyncRead();
bool isReadOperationActive() const { return readSequenceStarted; }
Q_SIGNALS:
void winError(ulong, const QString &);
@ -97,22 +97,24 @@ Q_SIGNALS:
void pipeClosed();
private Q_SLOTS:
bool readEventSignalled();
void notified(DWORD numberOfBytesRead, DWORD errorCode);
private:
bool completeAsyncRead(DWORD bytesRead, DWORD errorCode);
DWORD checkPipeState();
private:
HANDLE handle;
OVERLAPPED overlapped;
QWinEventNotifier *dataReadNotifier;
QWinOverlappedIoNotifier *dataReadNotifier;
qint64 readBufferMaxSize;
QRingBuffer readBuffer;
int actualReadBufferSize;
bool readSequenceStarted;
QTimer *emitReadyReadTimer;
bool pipeBroken;
static const qint64 initialReadBufferSize = 4096;
bool readyReadEmitted;
static const DWORD minReadBufferSize = 4096;
};
QT_END_NAMESPACE