summaryrefslogtreecommitdiffstats
path: root/src/network/socket
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/socket')
-rw-r--r--src/network/socket/qlocalsocket_p.h1
-rw-r--r--src/network/socket/qlocalsocket_win.cpp139
2 files changed, 117 insertions, 23 deletions
diff --git a/src/network/socket/qlocalsocket_p.h b/src/network/socket/qlocalsocket_p.h
index 34dc37870c..1a429cdefa 100644
--- a/src/network/socket/qlocalsocket_p.h
+++ b/src/network/socket/qlocalsocket_p.h
@@ -133,6 +133,7 @@ public:
#elif defined(Q_OS_WIN)
~QLocalSocketPrivate();
void destroyPipeHandles();
+ qint64 pipeWriterBytesToWrite() const;
void _q_canRead();
void _q_bytesWritten(qint64 bytes);
void _q_canWrite();
diff --git a/src/network/socket/qlocalsocket_win.cpp b/src/network/socket/qlocalsocket_win.cpp
index 66eed86501..37ca09e5d3 100644
--- a/src/network/socket/qlocalsocket_win.cpp
+++ b/src/network/socket/qlocalsocket_win.cpp
@@ -39,9 +39,61 @@
#include "qlocalsocket_p.h"
#include <qscopedvaluerollback.h>
+#include <qdeadlinetimer.h>
QT_BEGIN_NAMESPACE
+namespace {
+struct QSocketPoller
+{
+ QSocketPoller(const QLocalSocketPrivate &socket);
+
+ bool poll(const QDeadlineTimer &deadline);
+
+ enum { maxHandles = 2 };
+ HANDLE handles[maxHandles];
+ DWORD handleCount = 0;
+ bool waitForClose = false;
+};
+
+QSocketPoller::QSocketPoller(const QLocalSocketPrivate &socket)
+{
+ if (socket.pipeWriter)
+ handles[handleCount++] = socket.pipeWriter->syncEvent();
+ if (socket.pipeReader->isReadOperationActive())
+ handles[handleCount++] = socket.pipeReader->syncEvent();
+ else
+ waitForClose = true;
+}
+
+/*!
+ Waits until new data is available for reading or write operation
+ completes. Returns \c true, if we need to check pipe workers;
+ otherwise it returns \c false (if an error occurred or the operation
+ timed out).
+
+ \note If the read operation is inactive, it succeeds after
+ a short wait, allowing the caller to check the state of the socket.
+ */
+bool QSocketPoller::poll(const QDeadlineTimer &deadline)
+{
+ const qint64 sleepTime = 10;
+ QDeadlineTimer timer(waitForClose ? qMin(deadline.remainingTime(), sleepTime)
+ : deadline.remainingTime());
+ DWORD waitRet;
+
+ do {
+ waitRet = WaitForMultipleObjectsEx(handleCount, handles, FALSE,
+ timer.remainingTime(), TRUE);
+ } while (waitRet == WAIT_IO_COMPLETION);
+
+ if (waitRet == WAIT_TIMEOUT)
+ return !deadline.hasExpired();
+
+ return waitRet - WAIT_OBJECT_0 < handleCount;
+}
+} // anonymous namespace
+
void QLocalSocketPrivate::init()
{
Q_Q(QLocalSocket);
@@ -288,7 +340,7 @@ qint64 QLocalSocket::bytesAvailable() const
qint64 QLocalSocket::bytesToWrite() const
{
Q_D(const QLocalSocket);
- return d->writeBuffer.size() + (d->pipeWriter ? d->pipeWriter->bytesToWrite() : 0);
+ return d->writeBuffer.size() + d->pipeWriterBytesToWrite();
}
bool QLocalSocket::canReadLine() const
@@ -370,6 +422,11 @@ bool QLocalSocket::setSocketDescriptor(qintptr socketDescriptor,
return true;
}
+qint64 QLocalSocketPrivate::pipeWriterBytesToWrite() const
+{
+ return pipeWriter ? pipeWriter->bytesToWrite() : qint64(0);
+}
+
void QLocalSocketPrivate::_q_bytesWritten(qint64 bytes)
{
Q_Q(QLocalSocket);
@@ -384,7 +441,7 @@ void QLocalSocketPrivate::_q_canWrite()
{
Q_Q(QLocalSocket);
if (writeBuffer.isEmpty()) {
- if (state == QLocalSocket::ClosingState)
+ if (state == QLocalSocket::ClosingState && pipeWriterBytesToWrite() == 0)
q->close();
} else {
Q_ASSERT(pipeWriter);
@@ -428,11 +485,27 @@ bool QLocalSocket::waitForDisconnected(int msecs)
qWarning("QLocalSocket::waitForDisconnected isn't supported for write only pipes.");
return false;
}
- if (d->pipeReader->waitForPipeClosed(msecs)) {
- d->_q_pipeClosed();
- return true;
+
+ QDeadlineTimer deadline(msecs);
+ while (!d->pipeReader->isPipeClosed()) {
+ d->_q_canWrite();
+
+ QSocketPoller poller(*d);
+ if (!poller.poll(deadline))
+ return false;
+
+ if (d->pipeWriter)
+ d->pipeWriter->checkForWrite();
+
+ // When the read buffer is full, the read sequence is not running,
+ // so we need to peek the pipe to detect disconnection.
+ if (poller.waitForClose)
+ d->pipeReader->checkPipeState();
+
+ d->pipeReader->checkForReadyRead();
}
- return false;
+ d->_q_pipeClosed();
+ return true;
}
bool QLocalSocket::isValid() const
@@ -448,32 +521,52 @@ bool QLocalSocket::waitForReadyRead(int msecs)
if (d->state != QLocalSocket::ConnectedState)
return false;
- // We already know that the pipe is gone, but did not enter the event loop yet.
- if (d->pipeReader->isPipeClosed()) {
- d->_q_pipeClosed();
- return false;
- }
+ QDeadlineTimer deadline(msecs);
+ while (!d->pipeReader->isPipeClosed()) {
+ d->_q_canWrite();
- bool result = d->pipeReader->waitForReadyRead(msecs);
+ QSocketPoller poller(*d);
+ if (poller.waitForClose || !poller.poll(deadline))
+ return false;
- // We just noticed that the pipe is gone.
- if (d->pipeReader->isPipeClosed())
- d->_q_pipeClosed();
+ if (d->pipeWriter)
+ d->pipeWriter->checkForWrite();
- return result;
+ if (d->pipeReader->checkForReadyRead())
+ return true;
+ }
+ d->_q_pipeClosed();
+ return false;
}
bool QLocalSocket::waitForBytesWritten(int msecs)
{
- Q_D(const QLocalSocket);
- if (!d->pipeWriter)
+ Q_D(QLocalSocket);
+
+ if (d->state == QLocalSocket::UnconnectedState)
return false;
- // Wait for the pipe writer to acknowledge that it has
- // written. This will succeed if either the pipe writer has
- // already written the data, or if it manages to write data
- // within the given timeout.
- return d->pipeWriter->waitForWrite(msecs);
+ QDeadlineTimer deadline(msecs);
+ while (!d->pipeReader->isPipeClosed()) {
+ if (bytesToWrite() == 0)
+ return false;
+ d->_q_canWrite();
+
+ QSocketPoller poller(*d);
+ if (!poller.poll(deadline))
+ return false;
+
+ Q_ASSERT(d->pipeWriter);
+ if (d->pipeWriter->checkForWrite())
+ return true;
+
+ if (poller.waitForClose)
+ d->pipeReader->checkPipeState();
+
+ d->pipeReader->checkForReadyRead();
+ }
+ d->_q_pipeClosed();
+ return false;
}
QT_END_NAMESPACE