diff options
Diffstat (limited to 'src/network/socket/qlocalsocket_win.cpp')
-rw-r--r-- | src/network/socket/qlocalsocket_win.cpp | 224 |
1 files changed, 114 insertions, 110 deletions
diff --git a/src/network/socket/qlocalsocket_win.cpp b/src/network/socket/qlocalsocket_win.cpp index 912ebf412c..b3f3f9002a 100644 --- a/src/network/socket/qlocalsocket_win.cpp +++ b/src/network/socket/qlocalsocket_win.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtNetwork module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qlocalsocket_p.h" #include <qscopedvaluerollback.h> @@ -43,30 +7,48 @@ QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + namespace { struct QSocketPoller { QSocketPoller(const QLocalSocketPrivate &socket); + qint64 getRemainingTime(const QDeadlineTimer &deadline) const; bool poll(const QDeadlineTimer &deadline); enum { maxHandles = 2 }; HANDLE handles[maxHandles]; DWORD handleCount = 0; bool waitForClose = false; + bool writePending = false; }; QSocketPoller::QSocketPoller(const QLocalSocketPrivate &socket) { - if (socket.pipeWriter) + if (socket.pipeWriter && socket.pipeWriter->isWriteOperationActive()) { handles[handleCount++] = socket.pipeWriter->syncEvent(); + writePending = true; + } if (socket.pipeReader->isReadOperationActive()) handles[handleCount++] = socket.pipeReader->syncEvent(); else waitForClose = true; } +qint64 QSocketPoller::getRemainingTime(const QDeadlineTimer &deadline) const +{ + const qint64 sleepTime = 10; + qint64 remainingTime = deadline.remainingTime(); + if (waitForClose && (remainingTime > sleepTime || remainingTime == -1)) + return sleepTime; + + return remainingTime; +} + /*! + \internal + 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 @@ -74,12 +56,11 @@ QSocketPoller::QSocketPoller(const QLocalSocketPrivate &socket) \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()); + Q_ASSERT(handleCount != 0); + QDeadlineTimer timer(getRemainingTime(deadline)); DWORD waitRet; do { @@ -88,7 +69,7 @@ bool QSocketPoller::poll(const QDeadlineTimer &deadline) } while (waitRet == WAIT_IO_COMPLETION); if (waitRet == WAIT_TIMEOUT) - return !deadline.hasExpired(); + return waitForClose || !deadline.hasExpired(); return waitRet - WAIT_OBJECT_0 < handleCount; } @@ -161,15 +142,9 @@ QLocalSocketPrivate::QLocalSocketPrivate() : QIODevicePrivate(), QLocalSocketPrivate::~QLocalSocketPrivate() { - destroyPipeHandles(); -} - -void QLocalSocketPrivate::destroyPipeHandles() -{ - if (handle != INVALID_HANDLE_VALUE) { - DisconnectNamedPipe(handle); - CloseHandle(handle); - } + Q_ASSERT(state == QLocalSocket::UnconnectedState); + Q_ASSERT(handle == INVALID_HANDLE_VALUE); + Q_ASSERT(pipeWriter == nullptr); } void QLocalSocket::connectToServer(OpenMode openMode) @@ -188,14 +163,14 @@ void QLocalSocket::connectToServer(OpenMode openMode) emit stateChanged(d->state); if (d->serverName.isEmpty()) { d->error = ServerNotFoundError; - d->errorString = tr("%1: Invalid name").arg(QLatin1String("QLocalSocket::connectToServer")); + d->errorString = tr("%1: Invalid name").arg("QLocalSocket::connectToServer"_L1); d->state = UnconnectedState; emit errorOccurred(d->error); emit stateChanged(d->state); return; } - const QLatin1String pipePath("\\\\.\\pipe\\"); + const auto pipePath = "\\\\.\\pipe\\"_L1; if (d->serverName.startsWith(pipePath)) d->fullServerName = d->serverName; else @@ -228,7 +203,7 @@ void QLocalSocket::connectToServer(OpenMode openMode) if (localSocket == INVALID_HANDLE_VALUE) { const DWORD winError = GetLastError(); - d->_q_winError(winError, QLatin1String("QLocalSocket::connectToServer")); + d->_q_winError(winError, "QLocalSocket::connectToServer"_L1); d->fullServerName = QString(); return; } @@ -238,6 +213,20 @@ void QLocalSocket::connectToServer(OpenMode openMode) emit connected(); } +static qint64 transformPipeReaderResult(qint64 res) +{ + // QWindowsPipeReader's reading functions return error codes + // that don't match what we need. + switch (res) { + case 0: // EOF -> transform to error + return -1; + case -2: // EWOULDBLOCK -> no error, just no bytes + return 0; + default: + return res; + } +} + // This is reading from the buffer qint64 QLocalSocket::readData(char *data, qint64 maxSize) { @@ -246,22 +235,29 @@ qint64 QLocalSocket::readData(char *data, qint64 maxSize) if (!maxSize) return 0; - qint64 ret = d->pipeReader->read(data, maxSize); + return transformPipeReaderResult(d->pipeReader->read(data, maxSize)); +} - // QWindowsPipeReader::read() returns error codes that don't match what we need - switch (ret) { - case 0: // EOF -> transform to error - return -1; - case -2: // EWOULDBLOCK -> no error, just no bytes +qint64 QLocalSocket::readLineData(char *data, qint64 maxSize) +{ + Q_D(QLocalSocket); + + if (!maxSize) return 0; - default: - return ret; - } + + // QIODevice::readLine() reserves space for the trailing '\0' byte, + // so we must read 'maxSize + 1' bytes. + return transformPipeReaderResult(d->pipeReader->readLine(data, maxSize + 1)); } qint64 QLocalSocket::skipData(qint64 maxSize) { - return QIODevice::skipData(maxSize); + Q_D(QLocalSocket); + + if (!maxSize) + return 0; + + return transformPipeReaderResult(d->pipeReader->skip(maxSize)); } qint64 QLocalSocket::writeData(const char *data, qint64 len) @@ -280,6 +276,8 @@ qint64 QLocalSocket::writeData(const char *data, qint64 len) d->pipeWriter = new QWindowsPipeWriter(d->handle, this); QObjectPrivate::connect(d->pipeWriter, &QWindowsPipeWriter::bytesWritten, d, &QLocalSocketPrivate::_q_bytesWritten); + QObjectPrivate::connect(d->pipeWriter, &QWindowsPipeWriter::writeFailed, + d, &QLocalSocketPrivate::_q_writeFailed); } if (d->isWriteChunkCached(data, len)) @@ -315,22 +313,28 @@ void QLocalSocketPrivate::_q_pipeClosed() if (state == QLocalSocket::UnconnectedState) return; - emit q->readChannelFinished(); if (state != QLocalSocket::ClosingState) { state = QLocalSocket::ClosingState; emit q->stateChanged(state); if (state != QLocalSocket::ClosingState) return; } - state = QLocalSocket::UnconnectedState; - emit q->stateChanged(state); - emit q->disconnected(); + serverName.clear(); + fullServerName.clear(); pipeReader->stop(); delete pipeWriter; pipeWriter = nullptr; - destroyPipeHandles(); - handle = INVALID_HANDLE_VALUE; + if (handle != INVALID_HANDLE_VALUE) { + DisconnectNamedPipe(handle); + CloseHandle(handle); + handle = INVALID_HANDLE_VALUE; + } + + state = QLocalSocket::UnconnectedState; + emit q->stateChanged(state); + emit q->readChannelFinished(); + emit q->disconnected(); } qint64 QLocalSocket::bytesAvailable() const @@ -356,51 +360,30 @@ bool QLocalSocket::canReadLine() const void QLocalSocket::close() { Q_D(QLocalSocket); - if (openMode() == NotOpen) - return; - d->setWriteChannelCount(0); QIODevice::close(); + d->pipeReader->stopAndClear(); d->serverName = QString(); d->fullServerName = QString(); - - if (state() != UnconnectedState) { - if (bytesToWrite() > 0) { - disconnectFromServer(); - return; - } - - d->_q_pipeClosed(); - } + disconnectFromServer(); } bool QLocalSocket::flush() { Q_D(QLocalSocket); - bool written = false; - while (d->pipeWriter && d->pipeWriter->waitForWrite(0)) - written = true; - return written; + + return d->pipeWriter && d->pipeWriter->checkForWrite(); } void QLocalSocket::disconnectFromServer() { Q_D(QLocalSocket); - // Are we still connected? - if (!isValid()) { - // If we have unwritten data, the pipeWriter is still present. - // It must be destroyed before close() to prevent an infinite loop. - delete d->pipeWriter; - d->pipeWriter = 0; - } - - flush(); - if (bytesToWrite() != 0) { + if (bytesToWrite() == 0) { + d->_q_pipeClosed(); + } else if (d->state != QLocalSocket::ClosingState) { d->state = QLocalSocket::ClosingState; emit stateChanged(d->state); - } else { - close(); } } @@ -437,8 +420,18 @@ void QLocalSocketPrivate::_q_bytesWritten(qint64 bytes) QScopedValueRollback<bool> guard(emittedBytesWritten, true); emit q->bytesWritten(bytes); } - if (state == QLocalSocket::ClosingState && pipeWriterBytesToWrite() == 0) - q->close(); + if (state == QLocalSocket::ClosingState) + q->disconnectFromServer(); +} + +void QLocalSocketPrivate::_q_writeFailed() +{ + Q_Q(QLocalSocket); + error = QLocalSocket::PeerClosedError; + errorString = QLocalSocket::tr("Remote closed"); + emit q->errorOccurred(error); + + _q_pipeClosed(); } qintptr QLocalSocket::socketDescriptor() const @@ -472,16 +465,24 @@ bool QLocalSocket::waitForDisconnected(int msecs) qWarning("QLocalSocket::waitForDisconnected() is not allowed in UnconnectedState"); return false; } - if (!openMode().testFlag(QIODevice::ReadOnly)) { - qWarning("QLocalSocket::waitForDisconnected isn't supported for write only pipes."); - return false; - } QDeadlineTimer deadline(msecs); + bool wasChecked = false; while (!d->pipeReader->isPipeClosed()) { + if (wasChecked && deadline.hasExpired()) + return false; + QSocketPoller poller(*d); - if (!poller.poll(deadline)) + // The first parameter of the WaitForMultipleObjectsEx() call cannot + // be zero. So we have to call SleepEx() here. + if (!poller.writePending && poller.waitForClose) { + // Prevent waiting on the first pass, if both the pipe reader + // and the pipe writer are inactive. + if (wasChecked) + SleepEx(poller.getRemainingTime(deadline), TRUE); + } else if (!poller.poll(deadline)) { return false; + } if (d->pipeWriter) d->pipeWriter->checkForWrite(); @@ -492,6 +493,7 @@ bool QLocalSocket::waitForDisconnected(int msecs) d->pipeReader->checkPipeState(); d->pipeReader->checkForReadyRead(); + wasChecked = true; } d->_q_pipeClosed(); return true; @@ -534,22 +536,24 @@ bool QLocalSocket::waitForBytesWritten(int msecs) return false; QDeadlineTimer deadline(msecs); + bool wasChecked = false; while (!d->pipeReader->isPipeClosed()) { - if (bytesToWrite() == 0) + if (wasChecked && deadline.hasExpired()) return false; QSocketPoller poller(*d); - if (!poller.poll(deadline)) + if (!poller.writePending || !poller.poll(deadline)) return false; Q_ASSERT(d->pipeWriter); if (d->pipeWriter->checkForWrite()) return true; - if (poller.waitForClose) + if (poller.waitForClose && isValid()) d->pipeReader->checkPipeState(); d->pipeReader->checkForReadyRead(); + wasChecked = true; } d->_q_pipeClosed(); return false; |