Warning: That file was not part of the compilation database. It may have many parsing errors.

1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtCore module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qwindowspipereader_p.h"
41#include "qiodevice_p.h"
42#include <qelapsedtimer.h>
43#include <qscopedvaluerollback.h>
44
45QT_BEGIN_NAMESPACE
46
47QWindowsPipeReader::Overlapped::Overlapped(QWindowsPipeReader *reader)
48 : pipeReader(reader)
49{
50}
51
52void QWindowsPipeReader::Overlapped::clear()
53{
54 ZeroMemory(this, sizeof(OVERLAPPED));
55}
56
57
58QWindowsPipeReader::QWindowsPipeReader(QObject *parent)
59 : QObject(parent),
60 handle(INVALID_HANDLE_VALUE),
61 overlapped(nullptr),
62 readBufferMaxSize(0),
63 actualReadBufferSize(0),
64 stopped(true),
65 readSequenceStarted(false),
66 notifiedCalled(false),
67 pipeBroken(false),
68 readyReadPending(false),
69 inReadyRead(false)
70{
71 connect(this, &QWindowsPipeReader::_q_queueReadyRead,
72 this, &QWindowsPipeReader::emitPendingReadyRead, Qt::QueuedConnection);
73}
74
75QWindowsPipeReader::~QWindowsPipeReader()
76{
77 stop();
78 delete overlapped;
79}
80
81/*!
82 Sets the handle to read from. The handle must be valid.
83 */
84void QWindowsPipeReader::setHandle(HANDLE hPipeReadEnd)
85{
86 readBuffer.clear();
87 actualReadBufferSize = 0;
88 handle = hPipeReadEnd;
89 pipeBroken = false;
90}
91
92/*!
93 Stops the asynchronous read sequence.
94 If the read sequence is running then the I/O operation is canceled.
95 */
96void QWindowsPipeReader::stop()
97{
98 stopped = true;
99 if (readSequenceStarted) {
100 overlapped->pipeReader = nullptr;
101 if (!CancelIoEx(handle, overlapped)) {
102 const DWORD dwError = GetLastError();
103 if (dwError != ERROR_NOT_FOUND) {
104 qErrnoWarning(dwError, "QWindowsPipeReader: CancelIoEx on handle %p failed.",
105 handle);
106 }
107 }
108 overlapped = nullptr; // The object will be deleted in the I/O callback.
109 readSequenceStarted = false;
110 }
111}
112
113/*!
114 Returns the number of bytes we've read so far.
115 */
116qint64 QWindowsPipeReader::bytesAvailable() const
117{
118 return actualReadBufferSize;
119}
120
121/*!
122 Copies at most \c{maxlen} bytes from the internal read buffer to \c{data}.
123 */
124qint64 QWindowsPipeReader::read(char *data, qint64 maxlen)
125{
126 if (pipeBroken && actualReadBufferSize == 0)
127 return 0; // signal EOF
128
129 qint64 readSoFar;
130 // If startAsyncRead() has read data, copy it to its destination.
131 if (maxlen == 1 && actualReadBufferSize > 0) {
132 *data = readBuffer.getChar();
133 actualReadBufferSize--;
134 readSoFar = 1;
135 } else {
136 readSoFar = readBuffer.read(data, qMin(actualReadBufferSize, maxlen));
137 actualReadBufferSize -= readSoFar;
138 }
139
140 if (!pipeBroken) {
141 if (!readSequenceStarted && !stopped)
142 startAsyncRead();
143 if (readSoFar == 0)
144 return -2; // signal EWOULDBLOCK
145 }
146
147 return readSoFar;
148}
149
150bool QWindowsPipeReader::canReadLine() const
151{
152 return readBuffer.indexOf('\n', actualReadBufferSize) >= 0;
153}
154
155/*!
156 \internal
157 Will be called whenever the read operation completes.
158 */
159void QWindowsPipeReader::notified(DWORD errorCode, DWORD numberOfBytesRead)
160{
161 notifiedCalled = true;
162 readSequenceStarted = false;
163
164 switch (errorCode) {
165 case ERROR_SUCCESS:
166 break;
167 case ERROR_MORE_DATA:
168 // This is not an error. We're connected to a message mode
169 // pipe and the message didn't fit into the pipe's system
170 // buffer. We will read the remaining data in the next call.
171 break;
172 case ERROR_BROKEN_PIPE:
173 case ERROR_PIPE_NOT_CONNECTED:
174 pipeBroken = true;
175 break;
176 case ERROR_OPERATION_ABORTED:
177 if (stopped)
178 break;
179 Q_FALLTHROUGH();
180 default:
181 emit winError(errorCode, QLatin1String("QWindowsPipeReader::notified"));
182 pipeBroken = true;
183 break;
184 }
185
186 // After the reader was stopped, the only reason why this function can be called is the
187 // completion of a cancellation. No signals should be emitted, and no new read sequence should
188 // be started in this case.
189 if (stopped)
190 return;
191
192 if (pipeBroken) {
193 emit pipeClosed();
194 return;
195 }
196
197 actualReadBufferSize += numberOfBytesRead;
198 readBuffer.truncate(actualReadBufferSize);
199 startAsyncRead();
200 if (!readyReadPending) {
201 readyReadPending = true;
202 emit _q_queueReadyRead(QWindowsPipeReader::QPrivateSignal());
203 }
204}
205
206/*!
207 \internal
208 Reads data from the pipe into the readbuffer.
209 */
210void QWindowsPipeReader::startAsyncRead()
211{
212 const DWORD minReadBufferSize = 4096;
213 qint64 bytesToRead = qMax(checkPipeState(), minReadBufferSize);
214 if (pipeBroken)
215 return;
216
217 if (readBufferMaxSize && bytesToRead > (readBufferMaxSize - readBuffer.size())) {
218 bytesToRead = readBufferMaxSize - readBuffer.size();
219 if (bytesToRead <= 0) {
220 // Buffer is full. User must read data from the buffer
221 // before we can read more from the pipe.
222 return;
223 }
224 }
225
226 char *ptr = readBuffer.reserve(bytesToRead);
227
228 stopped = false;
229 readSequenceStarted = true;
230 if (!overlapped)
231 overlapped = new Overlapped(this);
232 overlapped->clear();
233 if (!ReadFileEx(handle, ptr, bytesToRead, overlapped, &readFileCompleted)) {
234 readSequenceStarted = false;
235
236 const DWORD dwError = GetLastError();
237 switch (dwError) {
238 case ERROR_BROKEN_PIPE:
239 case ERROR_PIPE_NOT_CONNECTED:
240 // It may happen, that the other side closes the connection directly
241 // after writing data. Then we must set the appropriate socket state.
242 pipeBroken = true;
243 emit pipeClosed();
244 break;
245 default:
246 emit winError(dwError, QLatin1String("QWindowsPipeReader::startAsyncRead"));
247 break;
248 }
249 }
250}
251
252/*!
253 \internal
254 Called when ReadFileEx finished the read operation.
255 */
256void QWindowsPipeReader::readFileCompleted(DWORD errorCode, DWORD numberOfBytesTransfered,
257 OVERLAPPED *overlappedBase)
258{
259 Overlapped *overlapped = static_cast<Overlapped *>(overlappedBase);
260 if (overlapped->pipeReader)
261 overlapped->pipeReader->notified(errorCode, numberOfBytesTransfered);
262 else
263 delete overlapped;
264}
265
266/*!
267 \internal
268 Returns the number of available bytes in the pipe.
269 Sets QWindowsPipeReader::pipeBroken to true if the connection is broken.
270 */
271DWORD QWindowsPipeReader::checkPipeState()
272{
273 DWORD bytes;
274 if (PeekNamedPipe(handle, nullptr, 0, nullptr, &bytes, nullptr))
275 return bytes;
276 if (!pipeBroken) {
277 pipeBroken = true;
278 emit pipeClosed();
279 }
280 return 0;
281}
282
283bool QWindowsPipeReader::waitForNotification(int timeout)
284{
285 QElapsedTimer t;
286 t.start();
287 notifiedCalled = false;
288 int msecs = timeout;
289 while (SleepEx(msecs == -1 ? INFINITE : msecs, TRUE) == WAIT_IO_COMPLETION) {
290 if (notifiedCalled)
291 return true;
292
293 // Some other I/O completion routine was called. Wait some more.
294 msecs = qt_subtract_from_timeout(timeout, t.elapsed());
295 if (!msecs)
296 break;
297 }
298 return notifiedCalled;
299}
300
301void QWindowsPipeReader::emitPendingReadyRead()
302{
303 if (readyReadPending) {
304 readyReadPending = false;
305 QScopedValueRollback<bool> guard(inReadyRead, true);
306 emit readyRead();
307 }
308}
309
310/*!
311 Waits for the completion of the asynchronous read operation.
312 Returns \c true, if we've emitted the readyRead signal (non-recursive case)
313 or readyRead will be emitted by the event loop (recursive case).
314 */
315bool QWindowsPipeReader::waitForReadyRead(int msecs)
316{
317 if (readyReadPending) {
318 if (!inReadyRead)
319 emitPendingReadyRead();
320 return true;
321 }
322
323 if (!readSequenceStarted)
324 return false;
325
326 if (!waitForNotification(msecs))
327 return false;
328
329 if (readyReadPending) {
330 if (!inReadyRead)
331 emitPendingReadyRead();
332 return true;
333 }
334
335 return false;
336}
337
338/*!
339 Waits until the pipe is closed.
340 */
341bool QWindowsPipeReader::waitForPipeClosed(int msecs)
342{
343 const int sleepTime = 10;
344 QElapsedTimer stopWatch;
345 stopWatch.start();
346 forever {
347 waitForReadyRead(0);
348 checkPipeState();
349 if (pipeBroken)
350 return true;
351 if (stopWatch.hasExpired(msecs - sleepTime))
352 return false;
353 Sleep(sleepTime);
354 }
355}
356
357QT_END_NAMESPACE
358

Warning: That file was not part of the compilation database. It may have many parsing errors.