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