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
45 QT_BEGIN_NAMESPACE
46
Overlapped(QWindowsPipeReader * reader)47 QWindowsPipeReader::Overlapped::Overlapped(QWindowsPipeReader *reader)
48 : pipeReader(reader)
49 {
50 }
51
clear()52 void QWindowsPipeReader::Overlapped::clear()
53 {
54 ZeroMemory(this, sizeof(OVERLAPPED));
55 }
56
57
QWindowsPipeReader(QObject * parent)58 QWindowsPipeReader::QWindowsPipeReader(QObject *parent)
59 : QObject(parent),
60 handle(INVALID_HANDLE_VALUE),
61 overlapped(this),
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
~QWindowsPipeReader()75 QWindowsPipeReader::~QWindowsPipeReader()
76 {
77 stop();
78 }
79
80 /*!
81 Sets the handle to read from. The handle must be valid.
82 */
setHandle(HANDLE hPipeReadEnd)83 void QWindowsPipeReader::setHandle(HANDLE hPipeReadEnd)
84 {
85 readBuffer.clear();
86 actualReadBufferSize = 0;
87 handle = hPipeReadEnd;
88 pipeBroken = false;
89 }
90
91 /*!
92 Stops the asynchronous read sequence.
93 If the read sequence is running then the I/O operation is canceled.
94 */
stop()95 void QWindowsPipeReader::stop()
96 {
97 stopped = true;
98 if (readSequenceStarted) {
99 if (!CancelIoEx(handle, &overlapped)) {
100 const DWORD dwError = GetLastError();
101 if (dwError != ERROR_NOT_FOUND) {
102 qErrnoWarning(dwError, "QWindowsPipeReader: CancelIoEx on handle %p failed.",
103 handle);
104 }
105 }
106 waitForNotification(-1);
107 }
108 }
109
110 /*!
111 Returns the number of bytes we've read so far.
112 */
bytesAvailable() const113 qint64 QWindowsPipeReader::bytesAvailable() const
114 {
115 return actualReadBufferSize;
116 }
117
118 /*!
119 Copies at most \c{maxlen} bytes from the internal read buffer to \c{data}.
120 */
read(char * data,qint64 maxlen)121 qint64 QWindowsPipeReader::read(char *data, qint64 maxlen)
122 {
123 if (pipeBroken && actualReadBufferSize == 0)
124 return 0; // signal EOF
125
126 qint64 readSoFar;
127 // If startAsyncRead() has read data, copy it to its destination.
128 if (maxlen == 1 && actualReadBufferSize > 0) {
129 *data = readBuffer.getChar();
130 actualReadBufferSize--;
131 readSoFar = 1;
132 } else {
133 readSoFar = readBuffer.read(data, qMin(actualReadBufferSize, maxlen));
134 actualReadBufferSize -= readSoFar;
135 }
136
137 if (!pipeBroken) {
138 if (!readSequenceStarted && !stopped)
139 startAsyncRead();
140 if (readSoFar == 0)
141 return -2; // signal EWOULDBLOCK
142 }
143
144 return readSoFar;
145 }
146
canReadLine() const147 bool QWindowsPipeReader::canReadLine() const
148 {
149 return readBuffer.indexOf('\n', actualReadBufferSize) >= 0;
150 }
151
152 /*!
153 \internal
154 Will be called whenever the read operation completes.
155 */
notified(DWORD errorCode,DWORD numberOfBytesRead)156 void QWindowsPipeReader::notified(DWORD errorCode, DWORD numberOfBytesRead)
157 {
158 notifiedCalled = true;
159 readSequenceStarted = false;
160
161 switch (errorCode) {
162 case ERROR_SUCCESS:
163 break;
164 case ERROR_MORE_DATA:
165 // This is not an error. We're connected to a message mode
166 // pipe and the message didn't fit into the pipe's system
167 // buffer. We will read the remaining data in the next call.
168 break;
169 case ERROR_BROKEN_PIPE:
170 case ERROR_PIPE_NOT_CONNECTED:
171 pipeBroken = true;
172 break;
173 case ERROR_OPERATION_ABORTED:
174 if (stopped)
175 break;
176 Q_FALLTHROUGH();
177 default:
178 emit winError(errorCode, QLatin1String("QWindowsPipeReader::notified"));
179 pipeBroken = true;
180 break;
181 }
182
183 // After the reader was stopped, the only reason why this function can be called is the
184 // completion of a cancellation. No signals should be emitted, and no new read sequence should
185 // be started in this case.
186 if (stopped)
187 return;
188
189 if (pipeBroken) {
190 emit pipeClosed();
191 return;
192 }
193
194 actualReadBufferSize += numberOfBytesRead;
195 readBuffer.truncate(actualReadBufferSize);
196 startAsyncRead();
197 if (!readyReadPending) {
198 readyReadPending = true;
199 emit _q_queueReadyRead(QWindowsPipeReader::QPrivateSignal());
200 }
201 }
202
203 /*!
204 \internal
205 Reads data from the pipe into the readbuffer.
206 */
startAsyncRead()207 void QWindowsPipeReader::startAsyncRead()
208 {
209 const DWORD minReadBufferSize = 4096;
210 qint64 bytesToRead = qMax(checkPipeState(), minReadBufferSize);
211 if (pipeBroken)
212 return;
213
214 if (readBufferMaxSize && bytesToRead > (readBufferMaxSize - readBuffer.size())) {
215 bytesToRead = readBufferMaxSize - readBuffer.size();
216 if (bytesToRead <= 0) {
217 // Buffer is full. User must read data from the buffer
218 // before we can read more from the pipe.
219 return;
220 }
221 }
222
223 char *ptr = readBuffer.reserve(bytesToRead);
224
225 stopped = false;
226 readSequenceStarted = true;
227 overlapped.clear();
228 if (!ReadFileEx(handle, ptr, bytesToRead, &overlapped, &readFileCompleted)) {
229 readSequenceStarted = false;
230
231 const DWORD dwError = GetLastError();
232 switch (dwError) {
233 case ERROR_BROKEN_PIPE:
234 case ERROR_PIPE_NOT_CONNECTED:
235 // It may happen, that the other side closes the connection directly
236 // after writing data. Then we must set the appropriate socket state.
237 pipeBroken = true;
238 emit pipeClosed();
239 break;
240 default:
241 emit winError(dwError, QLatin1String("QWindowsPipeReader::startAsyncRead"));
242 break;
243 }
244 }
245 }
246
247 /*!
248 \internal
249 Called when ReadFileEx finished the read operation.
250 */
readFileCompleted(DWORD errorCode,DWORD numberOfBytesTransfered,OVERLAPPED * overlappedBase)251 void QWindowsPipeReader::readFileCompleted(DWORD errorCode, DWORD numberOfBytesTransfered,
252 OVERLAPPED *overlappedBase)
253 {
254 Overlapped *overlapped = static_cast<Overlapped *>(overlappedBase);
255 overlapped->pipeReader->notified(errorCode, numberOfBytesTransfered);
256 }
257
258 /*!
259 \internal
260 Returns the number of available bytes in the pipe.
261 Sets QWindowsPipeReader::pipeBroken to true if the connection is broken.
262 */
checkPipeState()263 DWORD QWindowsPipeReader::checkPipeState()
264 {
265 DWORD bytes;
266 if (PeekNamedPipe(handle, nullptr, 0, nullptr, &bytes, nullptr))
267 return bytes;
268 if (!pipeBroken) {
269 pipeBroken = true;
270 emit pipeClosed();
271 }
272 return 0;
273 }
274
waitForNotification(int timeout)275 bool QWindowsPipeReader::waitForNotification(int timeout)
276 {
277 QElapsedTimer t;
278 t.start();
279 notifiedCalled = false;
280 int msecs = timeout;
281 while (SleepEx(msecs == -1 ? INFINITE : msecs, TRUE) == WAIT_IO_COMPLETION) {
282 if (notifiedCalled)
283 return true;
284
285 // Some other I/O completion routine was called. Wait some more.
286 msecs = qt_subtract_from_timeout(timeout, t.elapsed());
287 if (!msecs)
288 break;
289 }
290 return notifiedCalled;
291 }
292
emitPendingReadyRead()293 void QWindowsPipeReader::emitPendingReadyRead()
294 {
295 if (readyReadPending) {
296 readyReadPending = false;
297 QScopedValueRollback<bool> guard(inReadyRead, true);
298 emit readyRead();
299 }
300 }
301
302 /*!
303 Waits for the completion of the asynchronous read operation.
304 Returns \c true, if we've emitted the readyRead signal (non-recursive case)
305 or readyRead will be emitted by the event loop (recursive case).
306 */
waitForReadyRead(int msecs)307 bool QWindowsPipeReader::waitForReadyRead(int msecs)
308 {
309 if (readyReadPending) {
310 if (!inReadyRead)
311 emitPendingReadyRead();
312 return true;
313 }
314
315 if (!readSequenceStarted)
316 return false;
317
318 if (!waitForNotification(msecs))
319 return false;
320
321 if (readyReadPending) {
322 if (!inReadyRead)
323 emitPendingReadyRead();
324 return true;
325 }
326
327 return false;
328 }
329
330 /*!
331 Waits until the pipe is closed.
332 */
waitForPipeClosed(int msecs)333 bool QWindowsPipeReader::waitForPipeClosed(int msecs)
334 {
335 const int sleepTime = 10;
336 QElapsedTimer stopWatch;
337 stopWatch.start();
338 forever {
339 waitForReadyRead(0);
340 checkPipeState();
341 if (pipeBroken)
342 return true;
343 if (stopWatch.hasExpired(msecs - sleepTime))
344 return false;
345 Sleep(sleepTime);
346 }
347 }
348
349 QT_END_NAMESPACE
350