summaryrefslogtreecommitdiffstats
path: root/src/corelib/io/qwindowspipewriter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/io/qwindowspipewriter.cpp')
-rw-r--r--src/corelib/io/qwindowspipewriter.cpp258
1 files changed, 121 insertions, 137 deletions
diff --git a/src/corelib/io/qwindowspipewriter.cpp b/src/corelib/io/qwindowspipewriter.cpp
index 5ed584c6e3..9d0f6a8a3e 100644
--- a/src/corelib/io/qwindowspipewriter.cpp
+++ b/src/corelib/io/qwindowspipewriter.cpp
@@ -1,47 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2021 Alex Trotsenko <alex1973tr@gmail.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore 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.
+// Copyright (C) 2021 Alex Trotsenko <alex1973tr@gmail.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qwindowspipewriter_p.h"
-#include <qscopedvaluerollback.h>
#include <qcoreapplication.h>
#include <QMutexLocker>
+#include <QPointer>
QT_BEGIN_NAMESPACE
@@ -49,15 +13,15 @@ QWindowsPipeWriter::QWindowsPipeWriter(HANDLE pipeWriteEnd, QObject *parent)
: QObject(parent),
handle(pipeWriteEnd),
eventHandle(CreateEvent(NULL, FALSE, FALSE, NULL)),
- syncHandle(CreateEvent(NULL, FALSE, FALSE, NULL)),
+ syncHandle(CreateEvent(NULL, TRUE, FALSE, NULL)),
waitObject(NULL),
pendingBytesWrittenValue(0),
lastError(ERROR_SUCCESS),
+ completionState(NoError),
stopped(true),
writeSequenceStarted(false),
bytesWrittenPending(false),
- winEventActPosted(false),
- inBytesWritten(false)
+ winEventActPosted(false)
{
ZeroMemory(&overlapped, sizeof(OVERLAPPED));
overlapped.hEvent = eventHandle;
@@ -75,6 +39,19 @@ QWindowsPipeWriter::~QWindowsPipeWriter()
}
/*!
+ Assigns the handle to this writer. The handle must be valid.
+ Call this function if data was buffered before getting the handle.
+ */
+void QWindowsPipeWriter::setHandle(HANDLE hPipeWriteEnd)
+{
+ Q_ASSERT(!stopped);
+
+ handle = hPipeWriteEnd;
+ QMutexLocker locker(&mutex);
+ startAsyncWriteHelper(&locker);
+}
+
+/*!
Stops the asynchronous write sequence.
If the write sequence is running then the I/O operation is canceled.
*/
@@ -104,59 +81,71 @@ void QWindowsPipeWriter::stop()
}
/*!
- Returns \c true if async operation is in progress or a bytesWritten
- signal is pending.
+ Returns the number of bytes that are waiting to be written.
*/
-bool QWindowsPipeWriter::isWriteOperationActive() const
+qint64 QWindowsPipeWriter::bytesToWrite() const
{
QMutexLocker locker(&mutex);
- return writeSequenceStarted || bytesWrittenPending;
+ return writeBuffer.size() + pendingBytesWrittenValue;
}
/*!
- Returns the number of bytes that are waiting to be written.
+ Returns \c true if async operation is in progress.
+*/
+bool QWindowsPipeWriter::isWriteOperationActive() const
+{
+ return completionState == NoError && bytesToWrite() != 0;
+}
+
+/*!
+ Writes a shallow copy of \a ba to the internal buffer.
*/
-qint64 QWindowsPipeWriter::bytesToWrite() const
+void QWindowsPipeWriter::write(const QByteArray &ba)
{
- QMutexLocker locker(&mutex);
- return writeBuffer.size() + pendingBytesWrittenValue;
+ if (completionState != WriteDisabled)
+ writeImpl(ba);
}
/*!
- Writes data to the pipe.
+ Writes data to the internal buffer.
*/
-bool QWindowsPipeWriter::write(const QByteArray &ba)
+void QWindowsPipeWriter::write(const char *data, qint64 size)
+{
+ if (completionState != WriteDisabled)
+ writeImpl(data, size);
+}
+
+template <typename... Args>
+inline void QWindowsPipeWriter::writeImpl(Args... args)
{
QMutexLocker locker(&mutex);
- if (lastError != ERROR_SUCCESS)
- return false;
+ writeBuffer.append(args...);
- writeBuffer.append(ba);
- if (writeSequenceStarted)
- return true;
+ if (writeSequenceStarted || (lastError != ERROR_SUCCESS))
+ return;
stopped = false;
+
+ // If we don't have an assigned handle yet, defer writing until
+ // setHandle() is called.
+ if (handle != INVALID_HANDLE_VALUE)
+ startAsyncWriteHelper(&locker);
+}
+
+void QWindowsPipeWriter::startAsyncWriteHelper(QMutexLocker<QMutex> *locker)
+{
startAsyncWriteLocked();
// Do not post the event, if the write operation will be completed asynchronously.
- if (!bytesWrittenPending)
- return true;
-
- if (!winEventActPosted) {
- winEventActPosted = true;
- locker.unlock();
- QCoreApplication::postEvent(this, new QEvent(QEvent::WinEventAct));
- } else {
- locker.unlock();
- }
+ if (!bytesWrittenPending && lastError == ERROR_SUCCESS)
+ return;
- SetEvent(syncHandle);
- return true;
+ notifyCompleted(locker);
}
/*!
- Starts a new write sequence. Thread-safety should be ensured by the caller.
+ Starts a new write sequence.
*/
void QWindowsPipeWriter::startAsyncWriteLocked()
{
@@ -173,16 +162,17 @@ void QWindowsPipeWriter::startAsyncWriteLocked()
// Operation has been queued and will complete in the future.
writeSequenceStarted = true;
SetThreadpoolWait(waitObject, eventHandle, NULL);
- return;
+ break;
}
}
if (!writeCompleted(errorCode, numberOfBytesWritten))
- return;
+ break;
}
}
/*!
+ \internal
Thread pool callback procedure.
*/
void QWindowsPipeWriter::waitCallback(PTP_CALLBACK_INSTANCE instance, PVOID context,
@@ -213,17 +203,9 @@ void QWindowsPipeWriter::waitCallback(PTP_CALLBACK_INSTANCE instance, PVOID cont
if (pipeWriter->writeCompleted(errorCode, numberOfBytesTransfered))
pipeWriter->startAsyncWriteLocked();
- if (pipeWriter->lastError == ERROR_SUCCESS && !pipeWriter->winEventActPosted) {
- pipeWriter->winEventActPosted = true;
- locker.unlock();
- QCoreApplication::postEvent(pipeWriter, new QEvent(QEvent::WinEventAct));
- } else {
- locker.unlock();
- }
-
- // We set the event only after unlocking to avoid additional context
- // switches due to the released thread immediately running into the lock.
- SetEvent(pipeWriter->syncHandle);
+ // We post the notification even if the write operation failed,
+ // to unblock the main thread, in case it is waiting for the event.
+ pipeWriter->notifyCompleted(&locker);
}
/*!
@@ -232,24 +214,46 @@ void QWindowsPipeWriter::waitCallback(PTP_CALLBACK_INSTANCE instance, PVOID cont
*/
bool QWindowsPipeWriter::writeCompleted(DWORD errorCode, DWORD numberOfBytesWritten)
{
- if (errorCode == ERROR_SUCCESS) {
- Q_ASSERT(numberOfBytesWritten == DWORD(writeBuffer.nextDataBlockSize()));
-
+ switch (errorCode) {
+ case ERROR_SUCCESS:
bytesWrittenPending = true;
pendingBytesWrittenValue += numberOfBytesWritten;
writeBuffer.free(numberOfBytesWritten);
return true;
+ case ERROR_PIPE_NOT_CONNECTED: // the other end has closed the pipe
+ case ERROR_OPERATION_ABORTED: // the operation was canceled
+ case ERROR_NO_DATA: // the pipe is being closed
+ break;
+ default:
+ qErrnoWarning(errorCode, "QWindowsPipeWriter: write failed.");
+ break;
}
+ // The buffer is not cleared here, because the write progress
+ // should appear on the main thread synchronously.
lastError = errorCode;
- writeBuffer.clear();
- // The other end has closed the pipe. This can happen in QLocalSocket. Do not warn.
- if (errorCode != ERROR_OPERATION_ABORTED && errorCode != ERROR_NO_DATA)
- qErrnoWarning(errorCode, "QWindowsPipeWriter: write failed.");
return false;
}
/*!
+ Posts a notification event to the main thread.
+ */
+void QWindowsPipeWriter::notifyCompleted(QMutexLocker<QMutex> *locker)
+{
+ if (!winEventActPosted) {
+ winEventActPosted = true;
+ locker->unlock();
+ QCoreApplication::postEvent(this, new QEvent(QEvent::WinEventAct));
+ } else {
+ locker->unlock();
+ }
+
+ // We set the event only after unlocking to avoid additional context
+ // switches due to the released thread immediately running into the lock.
+ SetEvent(syncHandle);
+}
+
+/*!
Receives notification that the write operation has completed.
*/
bool QWindowsPipeWriter::event(QEvent *e)
@@ -267,20 +271,20 @@ bool QWindowsPipeWriter::event(QEvent *e)
*/
bool QWindowsPipeWriter::consumePendingAndEmit(bool allowWinActPosting)
{
+ ResetEvent(syncHandle);
QMutexLocker locker(&mutex);
// Enable QEvent::WinEventAct posting.
if (allowWinActPosting)
winEventActPosted = false;
- if (!bytesWrittenPending)
- return false;
-
- // Reset the state even if we don't emit bytesWritten().
- // It's a defined behavior to not re-emit this signal recursively.
- bytesWrittenPending = false;
- qint64 numberOfBytesWritten = pendingBytesWrittenValue;
- pendingBytesWrittenValue = 0;
+ const qint64 numberOfBytesWritten = pendingBytesWrittenValue;
+ const bool emitBytesWritten = bytesWrittenPending;
+ if (emitBytesWritten) {
+ bytesWrittenPending = false;
+ pendingBytesWrittenValue = 0;
+ }
+ const DWORD dwError = lastError;
locker.unlock();
@@ -288,47 +292,27 @@ bool QWindowsPipeWriter::consumePendingAndEmit(bool allowWinActPosting)
if (stopped)
return false;
- emit canWrite();
- if (!inBytesWritten) {
- QScopedValueRollback<bool> guard(inBytesWritten, true);
+ // Trigger 'ErrorDetected' state only once. This state must be set before
+ // emitting the bytesWritten() signal. Otherwise, the write sequence will
+ // be considered not finished, and we may hang if a slot connected
+ // to bytesWritten() calls waitForBytesWritten().
+ if (dwError != ERROR_SUCCESS && completionState == NoError) {
+ QPointer<QWindowsPipeWriter> alive(this);
+ completionState = ErrorDetected;
+ if (emitBytesWritten)
+ emit bytesWritten(numberOfBytesWritten);
+ if (alive) {
+ writeBuffer.clear();
+ completionState = WriteDisabled;
+ emit writeFailed();
+ }
+ } else if (emitBytesWritten) {
emit bytesWritten(numberOfBytesWritten);
}
- return true;
-}
-
-bool QWindowsPipeWriter::waitForNotification(const QDeadlineTimer &deadline)
-{
- do {
- DWORD waitRet = WaitForSingleObjectEx(syncHandle, deadline.remainingTime(), TRUE);
- if (waitRet == WAIT_OBJECT_0)
- return true;
-
- if (waitRet != WAIT_IO_COMPLETION)
- return false;
-
- // Some I/O completion routine was called. Wait some more.
- } while (!deadline.hasExpired());
-
- return false;
-}
-
-/*!
- Waits for the completion of the asynchronous write operation.
- Returns \c true, if we've emitted the bytesWritten signal (non-recursive case)
- or bytesWritten will be emitted by the event loop (recursive case).
- */
-bool QWindowsPipeWriter::waitForWrite(int msecs)
-{
- QDeadlineTimer timer(msecs);
-
- // Make sure that 'syncHandle' was triggered by the thread pool callback.
- while (isWriteOperationActive() && waitForNotification(timer)) {
- if (consumePendingAndEmit(false))
- return true;
- }
-
- return false;
+ return emitBytesWritten;
}
QT_END_NAMESPACE
+
+#include "moc_qwindowspipewriter_p.cpp"