Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qwindowspipewriter.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2021 Alex Trotsenko <alex1973tr@gmail.com>
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
6#include <qcoreapplication.h>
7#include <QMutexLocker>
8#include <QPointer>
9
11
13 : QObject(parent),
14 handle(pipeWriteEnd),
15 eventHandle(CreateEvent(NULL, FALSE, FALSE, NULL)),
16 syncHandle(CreateEvent(NULL, TRUE, FALSE, NULL)),
17 waitObject(NULL),
18 pendingBytesWrittenValue(0),
19 lastError(ERROR_SUCCESS),
21 stopped(true),
22 writeSequenceStarted(false),
23 bytesWrittenPending(false),
24 winEventActPosted(false)
25{
26 ZeroMemory(&overlapped, sizeof(OVERLAPPED));
27 overlapped.hEvent = eventHandle;
28 waitObject = CreateThreadpoolWait(waitCallback, this, NULL);
29 if (waitObject == NULL)
30 qErrnoWarning("QWindowsPipeWriter: CreateThreadpollWait failed.");
31}
32
34{
35 stop();
36 CloseThreadpoolWait(waitObject);
37 CloseHandle(eventHandle);
38 CloseHandle(syncHandle);
39}
40
45void QWindowsPipeWriter::setHandle(HANDLE hPipeWriteEnd)
46{
47 Q_ASSERT(!stopped);
48
49 handle = hPipeWriteEnd;
50 QMutexLocker locker(&mutex);
51 startAsyncWriteHelper(&locker);
52}
53
59{
60 if (stopped)
61 return;
62
63 mutex.lock();
64 stopped = true;
65 if (writeSequenceStarted) {
66 // Trying to disable callback before canceling the operation.
67 // Callback invocation is unnecessary here.
68 SetThreadpoolWait(waitObject, NULL, NULL);
69 if (!CancelIoEx(handle, &overlapped)) {
70 const DWORD dwError = GetLastError();
71 if (dwError != ERROR_NOT_FOUND) {
72 qErrnoWarning(dwError, "QWindowsPipeWriter: CancelIoEx on handle %p failed.",
73 handle);
74 }
75 }
76 writeSequenceStarted = false;
77 }
78 mutex.unlock();
79
80 WaitForThreadpoolWaitCallbacks(waitObject, TRUE);
81}
82
87{
88 QMutexLocker locker(&mutex);
89 return writeBuffer.size() + pendingBytesWrittenValue;
90}
91
96{
97 return completionState == NoError && bytesToWrite() != 0;
98}
99
104{
105 if (completionState != WriteDisabled)
106 writeImpl(ba);
107}
108
113{
114 if (completionState != WriteDisabled)
115 writeImpl(data, size);
116}
117
118template <typename... Args>
119inline void QWindowsPipeWriter::writeImpl(Args... args)
120{
121 QMutexLocker locker(&mutex);
122
123 writeBuffer.append(args...);
124
125 if (writeSequenceStarted || (lastError != ERROR_SUCCESS))
126 return;
127
128 stopped = false;
129
130 // If we don't have an assigned handle yet, defer writing until
131 // setHandle() is called.
132 if (handle != INVALID_HANDLE_VALUE)
133 startAsyncWriteHelper(&locker);
134}
135
136void QWindowsPipeWriter::startAsyncWriteHelper(QMutexLocker<QMutex> *locker)
137{
138 startAsyncWriteLocked();
139
140 // Do not post the event, if the write operation will be completed asynchronously.
141 if (!bytesWrittenPending && lastError == ERROR_SUCCESS)
142 return;
143
144 notifyCompleted(locker);
145}
146
150void QWindowsPipeWriter::startAsyncWriteLocked()
151{
152 while (!writeBuffer.isEmpty()) {
153 // WriteFile() returns true, if the write operation completes synchronously.
154 // We don't need to call GetOverlappedResult() additionally, because
155 // 'numberOfBytesWritten' is valid in this case.
156 DWORD numberOfBytesWritten;
157 DWORD errorCode = ERROR_SUCCESS;
158 if (!WriteFile(handle, writeBuffer.readPointer(), writeBuffer.nextDataBlockSize(),
159 &numberOfBytesWritten, &overlapped)) {
160 errorCode = GetLastError();
161 if (errorCode == ERROR_IO_PENDING) {
162 // Operation has been queued and will complete in the future.
163 writeSequenceStarted = true;
164 SetThreadpoolWait(waitObject, eventHandle, NULL);
165 break;
166 }
167 }
168
169 if (!writeCompleted(errorCode, numberOfBytesWritten))
170 break;
171 }
172}
173
178void QWindowsPipeWriter::waitCallback(PTP_CALLBACK_INSTANCE instance, PVOID context,
179 PTP_WAIT wait, TP_WAIT_RESULT waitResult)
180{
181 Q_UNUSED(instance);
182 Q_UNUSED(wait);
183 Q_UNUSED(waitResult);
184 QWindowsPipeWriter *pipeWriter = reinterpret_cast<QWindowsPipeWriter *>(context);
185
186 // Get the result of the asynchronous operation.
187 DWORD numberOfBytesTransfered = 0;
188 DWORD errorCode = ERROR_SUCCESS;
189 if (!GetOverlappedResult(pipeWriter->handle, &pipeWriter->overlapped,
190 &numberOfBytesTransfered, FALSE))
191 errorCode = GetLastError();
192
193 QMutexLocker locker(&pipeWriter->mutex);
194
195 // After the writer was stopped, the only reason why this function can be called is the
196 // completion of a cancellation. No signals should be emitted, and no new write sequence
197 // should be started in this case.
198 if (pipeWriter->stopped)
199 return;
200
201 pipeWriter->writeSequenceStarted = false;
202
203 if (pipeWriter->writeCompleted(errorCode, numberOfBytesTransfered))
204 pipeWriter->startAsyncWriteLocked();
205
206 // We post the notification even if the write operation failed,
207 // to unblock the main thread, in case it is waiting for the event.
208 pipeWriter->notifyCompleted(&locker);
209}
210
215bool QWindowsPipeWriter::writeCompleted(DWORD errorCode, DWORD numberOfBytesWritten)
216{
217 switch (errorCode) {
218 case ERROR_SUCCESS:
219 bytesWrittenPending = true;
220 pendingBytesWrittenValue += numberOfBytesWritten;
221 writeBuffer.free(numberOfBytesWritten);
222 return true;
223 case ERROR_PIPE_NOT_CONNECTED: // the other end has closed the pipe
224 case ERROR_OPERATION_ABORTED: // the operation was canceled
225 case ERROR_NO_DATA: // the pipe is being closed
226 break;
227 default:
228 qErrnoWarning(errorCode, "QWindowsPipeWriter: write failed.");
229 break;
230 }
231
232 // The buffer is not cleared here, because the write progress
233 // should appear on the main thread synchronously.
234 lastError = errorCode;
235 return false;
236}
237
241void QWindowsPipeWriter::notifyCompleted(QMutexLocker<QMutex> *locker)
242{
243 if (!winEventActPosted) {
244 winEventActPosted = true;
245 locker->unlock();
247 } else {
248 locker->unlock();
249 }
250
251 // We set the event only after unlocking to avoid additional context
252 // switches due to the released thread immediately running into the lock.
253 SetEvent(syncHandle);
254}
255
260{
261 if (e->type() == QEvent::WinEventAct) {
262 consumePendingAndEmit(true);
263 return true;
264 }
265 return QObject::event(e);
266}
267
272bool QWindowsPipeWriter::consumePendingAndEmit(bool allowWinActPosting)
273{
274 ResetEvent(syncHandle);
275 QMutexLocker locker(&mutex);
276
277 // Enable QEvent::WinEventAct posting.
278 if (allowWinActPosting)
279 winEventActPosted = false;
280
281 const qint64 numberOfBytesWritten = pendingBytesWrittenValue;
282 const bool emitBytesWritten = bytesWrittenPending;
283 if (emitBytesWritten) {
284 bytesWrittenPending = false;
285 pendingBytesWrittenValue = 0;
286 }
287 const DWORD dwError = lastError;
288
289 locker.unlock();
290
291 // Disable any further processing, if the pipe was stopped.
292 if (stopped)
293 return false;
294
295 // Trigger 'ErrorDetected' state only once. This state must be set before
296 // emitting the bytesWritten() signal. Otherwise, the write sequence will
297 // be considered not finished, and we may hang if a slot connected
298 // to bytesWritten() calls waitForBytesWritten().
299 if (dwError != ERROR_SUCCESS && completionState == NoError) {
300 QPointer<QWindowsPipeWriter> alive(this);
301 completionState = ErrorDetected;
302 if (emitBytesWritten)
303 emit bytesWritten(numberOfBytesWritten);
304 if (alive) {
305 writeBuffer.clear();
306 completionState = WriteDisabled;
308 }
309 } else if (emitBytesWritten) {
310 emit bytesWritten(numberOfBytesWritten);
311 }
312
313 return emitBytesWritten;
314}
315
317
318#include "moc_qwindowspipewriter_p.cpp"
\inmodule QtCore
Definition qbytearray.h:57
static void postEvent(QObject *receiver, QEvent *event, int priority=Qt::NormalEventPriority)
\inmodule QtCore
Definition qcoreevent.h:45
@ WinEventAct
Definition qcoreevent.h:99
Type type() const
Returns the event type.
Definition qcoreevent.h:304
\inmodule QtCore
Definition qmutex.h:313
void unlock() noexcept
Unlocks the mutex.
Definition qmutex.h:289
void lock() noexcept
Locks the mutex.
Definition qmutex.h:286
\inmodule QtCore
Definition qobject.h:103
virtual bool event(QEvent *event)
This virtual function receives events to an object and should return true if the event e was recogniz...
Definition qobject.cpp:1389
void stop()
Stops the asynchronous write sequence.
QWindowsPipeWriter(HANDLE pipeWriteEnd, QObject *parent=nullptr)
bool isWriteOperationActive() const
Returns true if async operation is in progress.
void setHandle(HANDLE hPipeWriteEnd)
Assigns the handle to this writer.
bool event(QEvent *e) override
Receives notification that the write operation has completed.
void write(const QByteArray &ba)
Writes a shallow copy of ba to the internal buffer.
qint64 bytesToWrite() const
Returns the number of bytes that are waiting to be written.
void qErrnoWarning(const char *msg,...)
Combined button and popup list for selecting options.
static void * context
GLenum GLsizei GLuint GLint * bytesWritten
GLuint64 GLenum void * handle
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint writeBuffer
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
@ NoError
Definition main.cpp:34
#define emit
#define Q_UNUSED(x)
long long qint64
Definition qtypes.h:60
static CompletionState completionState(StatementList *list)
QByteArray ba
[0]
QJSValueList args