diff options
author | Liang Qi <liang.qi@theqtcompany.com> | 2016-03-22 07:24:57 +0100 |
---|---|---|
committer | Liang Qi <liang.qi@theqtcompany.com> | 2016-03-22 07:28:42 +0100 |
commit | a02863234d76abb6c9f289026ae4ea3145924f30 (patch) | |
tree | aef6381d0000a78ba69ac80eb03739b1c8ca5fc3 /src/corelib/io | |
parent | e77b13621f0057374d83a2b884f03dd2e5b7b88c (diff) | |
parent | e4d79e1fdeb6b26ba0b12b578daacf7cd672b960 (diff) |
Merge remote-tracking branch 'origin/5.7' into dev
Conflicts:
configure
mkspecs/common/wince/qplatformdefs.h
src/plugins/platforms/directfb/qdirectfbbackingstore.cpp
src/plugins/platforms/xcb/qxcbbackingstore.cpp
Change-Id: Ied4d31264a9afca9514b51a7eb1494c28712793c
Diffstat (limited to 'src/corelib/io')
-rw-r--r-- | src/corelib/io/qfilesystemiterator_win.cpp | 7 | ||||
-rw-r--r-- | src/corelib/io/qfilesystemwatcher.cpp | 4 | ||||
-rw-r--r-- | src/corelib/io/qfilesystemwatcher_inotify.cpp | 2 | ||||
-rw-r--r-- | src/corelib/io/qprocess.h | 2 | ||||
-rw-r--r-- | src/corelib/io/qprocess_win.cpp | 12 | ||||
-rw-r--r-- | src/corelib/io/qsettings_win.cpp | 6 | ||||
-rw-r--r-- | src/corelib/io/qstandardpaths_win.cpp | 22 | ||||
-rw-r--r-- | src/corelib/io/qtextstream.cpp | 19 | ||||
-rw-r--r-- | src/corelib/io/qwindowspipereader.cpp | 154 | ||||
-rw-r--r-- | src/corelib/io/qwindowspipereader_p.h | 32 | ||||
-rw-r--r-- | src/corelib/io/qwindowspipewriter.cpp | 239 | ||||
-rw-r--r-- | src/corelib/io/qwindowspipewriter_p.h | 70 |
12 files changed, 336 insertions, 233 deletions
diff --git a/src/corelib/io/qfilesystemiterator_win.cpp b/src/corelib/io/qfilesystemiterator_win.cpp index 2caf87a7b4..ba195b2330 100644 --- a/src/corelib/io/qfilesystemiterator_win.cpp +++ b/src/corelib/io/qfilesystemiterator_win.cpp @@ -37,13 +37,6 @@ ** ****************************************************************************/ -#if !defined(WINAPI_FAMILY) -# if _WIN32_WINNT < 0x0500 -# undef _WIN32_WINNT -# define _WIN32_WINNT 0x0500 -# endif // _WIN32_WINNT < 0x500 -#endif // !WINAPI_FAMILY - #include "qfilesystemiterator_p.h" #include "qfilesystemengine_p.h" #include "qplatformdefs.h" diff --git a/src/corelib/io/qfilesystemwatcher.cpp b/src/corelib/io/qfilesystemwatcher.cpp index e26d412cf9..8b11830fbe 100644 --- a/src/corelib/io/qfilesystemwatcher.cpp +++ b/src/corelib/io/qfilesystemwatcher.cpp @@ -317,11 +317,11 @@ QStringList QFileSystemWatcher::addPaths(const QStringList &paths) // Autotest override case - use the explicitly selected engine only const QStringRef forceName = on.midRef(26); if(forceName == QLatin1String("poller")) { - qDebug() << "QFileSystemWatcher: skipping native engine, using only polling engine"; + qDebug("QFileSystemWatcher: skipping native engine, using only polling engine"); d_func()->initPollerEngine(); engine = d->poller; } else if(forceName == QLatin1String("native")) { - qDebug() << "QFileSystemWatcher: skipping polling engine, using only native engine"; + qDebug("QFileSystemWatcher: skipping polling engine, using only native engine"); engine = d->native; } } diff --git a/src/corelib/io/qfilesystemwatcher_inotify.cpp b/src/corelib/io/qfilesystemwatcher_inotify.cpp index 791429cc5a..5564bc7dca 100644 --- a/src/corelib/io/qfilesystemwatcher_inotify.cpp +++ b/src/corelib/io/qfilesystemwatcher_inotify.cpp @@ -344,7 +344,7 @@ QStringList QInotifyFileSystemWatcherEngine::removePaths(const QStringList &path void QInotifyFileSystemWatcherEngine::readFromInotify() { - // qDebug() << "QInotifyFileSystemWatcherEngine::readFromInotify"; + // qDebug("QInotifyFileSystemWatcherEngine::readFromInotify"); int buffSize = 0; ioctl(inotifyFd, FIONREAD, (char *) &buffSize); diff --git a/src/corelib/io/qprocess.h b/src/corelib/io/qprocess.h index fd835d18b1..4ce0503761 100644 --- a/src/corelib/io/qprocess.h +++ b/src/corelib/io/qprocess.h @@ -269,7 +269,7 @@ Q_SIGNALS: void finished(int exitCode); // ### Qt 6: merge the two signals with a default value void finished(int exitCode, QProcess::ExitStatus exitStatus); #if QT_DEPRECATED_SINCE(5,6) - QT_MOC_COMPAT void error(QProcess::ProcessError error); + void error(QProcess::ProcessError error); #endif void errorOccurred(QProcess::ProcessError error); void stateChanged(QProcess::ProcessState state, QPrivateSignal); diff --git a/src/corelib/io/qprocess_win.cpp b/src/corelib/io/qprocess_win.cpp index 611ce34550..5d8b567c8c 100644 --- a/src/corelib/io/qprocess_win.cpp +++ b/src/corelib/io/qprocess_win.cpp @@ -665,7 +665,8 @@ bool QProcessPrivate::waitForReadyRead(int msecs) return false; if (WaitForSingleObjectEx(pid->hProcess, 0, false) == WAIT_OBJECT_0) { bool readyReadEmitted = drainOutputPipes(); - _q_processDied(); + if (pid) + _q_processDied(); return readyReadEmitted; } @@ -683,10 +684,7 @@ bool QProcessPrivate::waitForBytesWritten(int msecs) QIncrementalSleepTimer timer(msecs); forever { - // Check if we have any data pending: the pipe writer has - // bytes waiting to written, or it has written data since the - // last time we called stdinChannel.writer->waitForWrite(). - bool pendingDataInPipe = stdinChannel.writer && (stdinChannel.writer->bytesToWrite() || stdinChannel.writer->hadWritten()); + bool pendingDataInPipe = stdinChannel.writer && stdinChannel.writer->bytesToWrite(); // If we don't have pending data, and our write buffer is // empty, we fail. @@ -770,7 +768,8 @@ bool QProcessPrivate::waitForFinished(int msecs) if (WaitForSingleObject(pid->hProcess, timer.nextSleepTime()) == WAIT_OBJECT_0) { drainOutputPipes(); - _q_processDied(); + if (pid) + _q_processDied(); return true; } @@ -813,7 +812,6 @@ qint64 QProcessPrivate::writeToStdin(const char *data, qint64 maxlen) stdinChannel.writer = new QWindowsPipeWriter(stdinChannel.pipe[1], q); QObjectPrivate::connect(stdinChannel.writer, &QWindowsPipeWriter::canWrite, this, &QProcessPrivate::_q_canWrite); - stdinChannel.writer->start(); } return stdinChannel.writer->write(data, maxlen); diff --git a/src/corelib/io/qsettings_win.cpp b/src/corelib/io/qsettings_win.cpp index f74f52df89..05ed51e999 100644 --- a/src/corelib/io/qsettings_win.cpp +++ b/src/corelib/io/qsettings_win.cpp @@ -510,6 +510,12 @@ bool QWinSettingsPrivate::readKey(HKEY parentHandle, const QString &rSubKey, QVa return false; } + // workaround for rare cases where trailing '\0' are missing in registry + if (dataType == REG_SZ || dataType == REG_EXPAND_SZ) + dataSize += 2; + else if (dataType == REG_MULTI_SZ) + dataSize += 4; + // get the value QByteArray data(dataSize, 0); res = RegQueryValueEx(handle, reinterpret_cast<const wchar_t *>(rSubkeyName.utf16()), 0, 0, diff --git a/src/corelib/io/qstandardpaths_win.cpp b/src/corelib/io/qstandardpaths_win.cpp index 9e3cb9ab4d..9bd5a9e3b6 100644 --- a/src/corelib/io/qstandardpaths_win.cpp +++ b/src/corelib/io/qstandardpaths_win.cpp @@ -117,6 +117,7 @@ static inline void appendTestMode(QString &path) // Map QStandardPaths::StandardLocation to CLSID of SHGetSpecialFolderPath() static int writableSpecialFolderClsid(QStandardPaths::StandardLocation type) { +#ifndef Q_OS_WINCE static const int clsids[] = { CSIDL_DESKTOPDIRECTORY, // DesktopLocation CSIDL_PERSONAL, // DocumentsLocation @@ -136,6 +137,27 @@ static int writableSpecialFolderClsid(QStandardPaths::StandardLocation type) CSIDL_APPDATA, // AppDataLocation ("Roaming" path) CSIDL_LOCAL_APPDATA, // AppConfigLocation ("Local" path) }; +#else // !Q_OS_WINCE + static const int clsids[] = { + CSIDL_DESKTOPDIRECTORY, // DesktopLocation + CSIDL_PERSONAL, // DocumentsLocation + CSIDL_FONTS, // FontsLocation + CSIDL_PROGRAMS, // ApplicationsLocation + CSIDL_MYMUSIC, // MusicLocation + CSIDL_MYVIDEO, // MoviesLocation + CSIDL_MYPICTURES, // PicturesLocation + -1, -1, // TempLocation/HomeLocation + CSIDL_APPDATA, // AppLocalDataLocation, AppLocalDataLocation = DataLocation + -1, // CacheLocation + CSIDL_APPDATA, // GenericDataLocation + -1, // RuntimeLocation + CSIDL_APPDATA, // ConfigLocation + -1, -1, // DownloadLocation/GenericCacheLocation + CSIDL_APPDATA, // GenericConfigLocation + CSIDL_APPDATA, // AppDataLocation + CSIDL_APPDATA, // AppConfigLocation + }; +#endif // Q_OS_WINCE Q_STATIC_ASSERT(sizeof(clsids) / sizeof(clsids[0]) == size_t(QStandardPaths::AppConfigLocation + 1)); return size_t(type) < sizeof(clsids) / sizeof(clsids[0]) ? clsids[type] : -1; diff --git a/src/corelib/io/qtextstream.cpp b/src/corelib/io/qtextstream.cpp index bc4a5fa538..27b7570226 100644 --- a/src/corelib/io/qtextstream.cpp +++ b/src/corelib/io/qtextstream.cpp @@ -456,6 +456,10 @@ bool QTextStreamPrivate::fillReadBuffer(qint64 maxBytes) bytesRead = device->read(buf, sizeof(buf)); } + // reset the Text flag. + if (textModeEnabled) + device->setTextModeEnabled(true); + if (bytesRead <= 0) return false; @@ -491,10 +495,6 @@ bool QTextStreamPrivate::fillReadBuffer(qint64 maxBytes) readBuffer += QString::fromLatin1(buf, bytesRead); #endif - // reset the Text flag. - if (textModeEnabled) - device->setTextModeEnabled(true); - // remove all '\r\n' in the string. if (readBuffer.size() > oldReadBufferSize && textModeEnabled) { QChar CR = QLatin1Char('\r'); @@ -593,17 +593,18 @@ void QTextStreamPrivate::flushWriteBuffer() qDebug("QTextStreamPrivate::flushWriteBuffer(), device->write(\"%s\") == %d", qt_prettyDebug(data.constData(), qMin(data.size(),32), data.size()).constData(), int(bytesWritten)); #endif - if (bytesWritten <= 0) { - status = QTextStream::WriteFailed; - return; - } #if defined (Q_OS_WIN) - // replace the text flag + // reset the text flag if (textModeEnabled) device->setTextModeEnabled(true); #endif + if (bytesWritten <= 0) { + status = QTextStream::WriteFailed; + return; + } + // flush the file #ifndef QT_NO_QOBJECT QFileDevice *file = qobject_cast<QFileDevice *>(device); diff --git a/src/corelib/io/qwindowspipereader.cpp b/src/corelib/io/qwindowspipereader.cpp index 95232385b2..15fb276be9 100644 --- a/src/corelib/io/qwindowspipereader.cpp +++ b/src/corelib/io/qwindowspipereader.cpp @@ -38,28 +38,40 @@ ****************************************************************************/ #include "qwindowspipereader_p.h" -#include "qwinoverlappedionotifier_p.h" -#include <qdebug.h> +#include "qiodevice_p.h" #include <qelapsedtimer.h> -#include <qeventloop.h> QT_BEGIN_NAMESPACE +QWindowsPipeReader::Overlapped::Overlapped(QWindowsPipeReader *reader) + : pipeReader(reader) +{ +} + +void QWindowsPipeReader::Overlapped::clear() +{ + ZeroMemory(this, sizeof(OVERLAPPED)); +} + + QWindowsPipeReader::QWindowsPipeReader(QObject *parent) : QObject(parent), handle(INVALID_HANDLE_VALUE), + overlapped(this), readBufferMaxSize(0), actualReadBufferSize(0), stopped(true), readSequenceStarted(false), + notifiedCalled(false), pipeBroken(false), - readyReadEmitted(false) + readyReadPending(false), + inReadyRead(false) { - dataReadNotifier = new QWinOverlappedIoNotifier(this); - connect(dataReadNotifier, &QWinOverlappedIoNotifier::notified, this, &QWindowsPipeReader::notified); + connect(this, &QWindowsPipeReader::_q_queueReadyRead, + this, &QWindowsPipeReader::emitPendingReadyRead, Qt::QueuedConnection); } -static bool qt_cancelIo(HANDLE handle, OVERLAPPED *overlapped) +bool qt_cancelIo(HANDLE handle, OVERLAPPED *overlapped) { typedef BOOL (WINAPI *PtrCancelIoEx)(HANDLE, LPOVERLAPPED); static PtrCancelIoEx ptrCancelIoEx = 0; @@ -88,12 +100,6 @@ void QWindowsPipeReader::setHandle(HANDLE hPipeReadEnd) actualReadBufferSize = 0; handle = hPipeReadEnd; pipeBroken = false; - readyReadEmitted = false; - stopped = false; - if (hPipeReadEnd != INVALID_HANDLE_VALUE) { - dataReadNotifier->setHandle(hPipeReadEnd); - dataReadNotifier->setEnabled(true); - } } /*! @@ -104,19 +110,15 @@ void QWindowsPipeReader::stop() { stopped = true; if (readSequenceStarted) { - if (qt_cancelIo(handle, &overlapped)) { - dataReadNotifier->waitForNotified(-1, &overlapped); - } else { + if (!qt_cancelIo(handle, &overlapped)) { const DWORD dwError = GetLastError(); if (dwError != ERROR_NOT_FOUND) { qErrnoWarning(dwError, "QWindowsPipeReader: qt_cancelIo on handle %x failed.", handle); } } + waitForNotification(-1); } - readSequenceStarted = false; - dataReadNotifier->setEnabled(false); - handle = INVALID_HANDLE_VALUE; } /*! @@ -174,11 +176,10 @@ bool QWindowsPipeReader::canReadLine() const \internal Will be called whenever the read operation completes. */ -void QWindowsPipeReader::notified(quint32 numberOfBytesRead, quint32 errorCode, - OVERLAPPED *notifiedOverlapped) +void QWindowsPipeReader::notified(DWORD errorCode, DWORD numberOfBytesRead) { - if (&overlapped != notifiedOverlapped) - return; + notifiedCalled = true; + readSequenceStarted = false; switch (errorCode) { case ERROR_SUCCESS: @@ -202,8 +203,6 @@ void QWindowsPipeReader::notified(quint32 numberOfBytesRead, quint32 errorCode, break; } - readSequenceStarted = false; - // After the reader was stopped, the only reason why this function can be called is the // completion of a cancellation. No signals should be emitted, and no new read sequence should // be started in this case. @@ -218,13 +217,15 @@ void QWindowsPipeReader::notified(quint32 numberOfBytesRead, quint32 errorCode, actualReadBufferSize += numberOfBytesRead; readBuffer.truncate(actualReadBufferSize); startAsyncRead(); - readyReadEmitted = true; - emit readyRead(); + if (!readyReadPending) { + readyReadPending = true; + emit _q_queueReadyRead(QWindowsPipeReader::QPrivateSignal()); + } } /*! \internal - Reads data from the socket into the readbuffer + Reads data from the pipe into the readbuffer. */ void QWindowsPipeReader::startAsyncRead() { @@ -244,43 +245,41 @@ void QWindowsPipeReader::startAsyncRead() char *ptr = readBuffer.reserve(bytesToRead); + stopped = false; readSequenceStarted = true; - ZeroMemory(&overlapped, sizeof(overlapped)); - if (ReadFile(handle, ptr, bytesToRead, NULL, &overlapped)) { - // We get notified by the QWinOverlappedIoNotifier - even in the synchronous case. - return; - } else { + overlapped.clear(); + if (!ReadFileEx(handle, ptr, bytesToRead, &overlapped, &readFileCompleted)) { + readSequenceStarted = false; + const DWORD dwError = GetLastError(); switch (dwError) { - case ERROR_IO_PENDING: - // This is not an error. We're getting notified, when data arrives. - return; - case ERROR_MORE_DATA: - // This is not an error. The synchronous read succeeded. - // We're connected to a message mode pipe and the message - // didn't fit into the pipe's system buffer. - // We're getting notified by the QWinOverlappedIoNotifier. - break; case ERROR_BROKEN_PIPE: case ERROR_PIPE_NOT_CONNECTED: - { - // It may happen, that the other side closes the connection directly - // after writing data. Then we must set the appropriate socket state. - readSequenceStarted = false; - pipeBroken = true; - emit pipeClosed(); - return; - } + // It may happen, that the other side closes the connection directly + // after writing data. Then we must set the appropriate socket state. + pipeBroken = true; + emit pipeClosed(); + break; default: - readSequenceStarted = false; emit winError(dwError, QLatin1String("QWindowsPipeReader::startAsyncRead")); - return; + break; } } } /*! \internal + Called when ReadFileEx finished the read operation. + */ +void QWindowsPipeReader::readFileCompleted(DWORD errorCode, DWORD numberOfBytesTransfered, + OVERLAPPED *overlappedBase) +{ + Overlapped *overlapped = static_cast<Overlapped *>(overlappedBase); + overlapped->pipeReader->notified(errorCode, numberOfBytesTransfered); +} + +/*! + \internal Returns the number of available bytes in the pipe. Sets QWindowsPipeReader::pipeBroken to true if the connection is broken. */ @@ -298,17 +297,60 @@ DWORD QWindowsPipeReader::checkPipeState() return 0; } +bool QWindowsPipeReader::waitForNotification(int timeout) +{ + QElapsedTimer t; + t.start(); + notifiedCalled = false; + int msecs = timeout; + while (SleepEx(msecs == -1 ? INFINITE : msecs, TRUE) == WAIT_IO_COMPLETION) { + if (notifiedCalled) + return true; + + // Some other I/O completion routine was called. Wait some more. + msecs = qt_subtract_from_timeout(timeout, t.elapsed()); + if (!msecs) + break; + } + return notifiedCalled; +} + +void QWindowsPipeReader::emitPendingReadyRead() +{ + if (readyReadPending) { + readyReadPending = false; + inReadyRead = true; + emit readyRead(); + inReadyRead = false; + } +} + /*! Waits for the completion of the asynchronous read operation. - Returns \c true, if we've emitted the readyRead signal. + Returns \c true, if we've emitted the readyRead signal (non-recursive case) + or readyRead will be emitted by the event loop (recursive case). */ bool QWindowsPipeReader::waitForReadyRead(int msecs) { if (!readSequenceStarted) return false; - readyReadEmitted = false; - dataReadNotifier->waitForNotified(msecs, &overlapped); - return readyReadEmitted; + + if (readyReadPending) { + if (!inReadyRead) + emitPendingReadyRead(); + return true; + } + + if (!waitForNotification(msecs)) + return false; + + if (readyReadPending) { + if (!inReadyRead) + emitPendingReadyRead(); + return true; + } + + return false; } /*! diff --git a/src/corelib/io/qwindowspipereader_p.h b/src/corelib/io/qwindowspipereader_p.h index d389d0cc9f..74ff5250ac 100644 --- a/src/corelib/io/qwindowspipereader_p.h +++ b/src/corelib/io/qwindowspipereader_p.h @@ -51,7 +51,6 @@ // We mean it. // -#include <qbytearray.h> #include <qobject.h> #include <private/qringbuffer_p.h> @@ -59,9 +58,6 @@ QT_BEGIN_NAMESPACE - -class QWinOverlappedIoNotifier; - class Q_CORE_EXPORT QWindowsPipeReader : public QObject { Q_OBJECT @@ -70,6 +66,7 @@ public: ~QWindowsPipeReader(); void setHandle(HANDLE hPipeReadEnd); + void startAsyncRead(); void stop(); void setMaxReadBufferSize(qint64 size) { readBufferMaxSize = size; } @@ -82,31 +79,42 @@ public: bool waitForReadyRead(int msecs); bool waitForPipeClosed(int msecs); - void startAsyncRead(); bool isReadOperationActive() const { return readSequenceStarted; } Q_SIGNALS: void winError(ulong, const QString &); void readyRead(); void pipeClosed(); - -private Q_SLOTS: - void notified(quint32 numberOfBytesRead, quint32 errorCode, OVERLAPPED *notifiedOverlapped); + void _q_queueReadyRead(QPrivateSignal); private: + static void CALLBACK readFileCompleted(DWORD errorCode, DWORD numberOfBytesTransfered, + OVERLAPPED *overlappedBase); + void notified(DWORD errorCode, DWORD numberOfBytesRead); DWORD checkPipeState(); + bool waitForNotification(int timeout); + void emitPendingReadyRead(); + + class Overlapped : public OVERLAPPED + { + Q_DISABLE_COPY(Overlapped) + public: + explicit Overlapped(QWindowsPipeReader *reader); + void clear(); + QWindowsPipeReader *pipeReader; + }; -private: HANDLE handle; - OVERLAPPED overlapped; - QWinOverlappedIoNotifier *dataReadNotifier; + Overlapped overlapped; qint64 readBufferMaxSize; QRingBuffer readBuffer; qint64 actualReadBufferSize; bool stopped; bool readSequenceStarted; + bool notifiedCalled; bool pipeBroken; - bool readyReadEmitted; + bool readyReadPending; + bool inReadyRead; }; QT_END_NAMESPACE diff --git a/src/corelib/io/qwindowspipewriter.cpp b/src/corelib/io/qwindowspipewriter.cpp index c7144d2c6b..79e7d13eb5 100644 --- a/src/corelib/io/qwindowspipewriter.cpp +++ b/src/corelib/io/qwindowspipewriter.cpp @@ -38,144 +38,177 @@ ****************************************************************************/ #include "qwindowspipewriter_p.h" +#include "qiodevice_p.h" QT_BEGIN_NAMESPACE -#ifndef QT_NO_THREAD +extern bool qt_cancelIo(HANDLE handle, OVERLAPPED *overlapped); // from qwindowspipereader.cpp -QWindowsPipeWriter::QWindowsPipeWriter(HANDLE pipe, QObject * parent) - : QThread(parent), - writePipe(INVALID_HANDLE_VALUE), - quitNow(false), - hasWritten(false) + +QWindowsPipeWriter::Overlapped::Overlapped(QWindowsPipeWriter *pipeWriter) + : pipeWriter(pipeWriter) +{ +} + +void QWindowsPipeWriter::Overlapped::clear() { - DuplicateHandle(GetCurrentProcess(), pipe, GetCurrentProcess(), - &writePipe, 0, FALSE, DUPLICATE_SAME_ACCESS); + ZeroMemory(this, sizeof(OVERLAPPED)); +} + + +QWindowsPipeWriter::QWindowsPipeWriter(HANDLE pipeWriteEnd, QObject *parent) + : QObject(parent), + handle(pipeWriteEnd), + overlapped(this), + numberOfBytesToWrite(0), + pendingBytesWrittenValue(0), + stopped(true), + writeSequenceStarted(false), + notifiedCalled(false), + bytesWrittenPending(false), + inBytesWritten(false) +{ + connect(this, &QWindowsPipeWriter::_q_queueBytesWritten, + this, &QWindowsPipeWriter::emitPendingBytesWrittenValue, Qt::QueuedConnection); } QWindowsPipeWriter::~QWindowsPipeWriter() { - lock.lock(); - quitNow = true; - waitCondition.wakeOne(); - lock.unlock(); - if (!wait(30000)) - terminate(); - CloseHandle(writePipe); + stop(); } bool QWindowsPipeWriter::waitForWrite(int msecs) { - QMutexLocker locker(&lock); - bool hadWritten = hasWritten; - hasWritten = false; - if (hadWritten) + if (!writeSequenceStarted) + return false; + + if (bytesWrittenPending) { + if (!inBytesWritten) + emitPendingBytesWrittenValue(); return true; - if (!waitCondition.wait(&lock, msecs)) + } + + if (!waitForNotification(msecs)) return false; - hadWritten = hasWritten; - hasWritten = false; - return hadWritten; + + if (bytesWrittenPending) { + if (!inBytesWritten) + emitPendingBytesWrittenValue(); + return true; + } + + return false; } -qint64 QWindowsPipeWriter::write(const char *ptr, qint64 maxlen) +qint64 QWindowsPipeWriter::bytesToWrite() const { - if (!isRunning()) - return -1; - - QMutexLocker locker(&lock); - data.append(ptr, maxlen); - waitCondition.wakeOne(); - return maxlen; + return numberOfBytesToWrite; } -class QPipeWriterOverlapped +void QWindowsPipeWriter::emitPendingBytesWrittenValue() { -public: - QPipeWriterOverlapped() - { - overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (bytesWrittenPending) { + bytesWrittenPending = false; + const qint64 bytes = pendingBytesWrittenValue; + pendingBytesWrittenValue = 0; + + inBytesWritten = true; + emit bytesWritten(bytes); + inBytesWritten = false; + emit canWrite(); } +} - ~QPipeWriterOverlapped() - { - CloseHandle(overlapped.hEvent); - } +void QWindowsPipeWriter::writeFileCompleted(DWORD errorCode, DWORD numberOfBytesTransfered, + OVERLAPPED *overlappedBase) +{ + Overlapped *overlapped = static_cast<Overlapped *>(overlappedBase); + overlapped->pipeWriter->notified(errorCode, numberOfBytesTransfered); +} - void prepare() - { - const HANDLE hEvent = overlapped.hEvent; - ZeroMemory(&overlapped, sizeof overlapped); - overlapped.hEvent = hEvent; +/*! + \internal + Will be called whenever the write operation completes. + */ +void QWindowsPipeWriter::notified(DWORD errorCode, DWORD numberOfBytesWritten) +{ + notifiedCalled = true; + writeSequenceStarted = false; + numberOfBytesToWrite = 0; + + switch (errorCode) { + case ERROR_SUCCESS: + break; + case ERROR_OPERATION_ABORTED: + if (stopped) + break; + // fall through + default: + qErrnoWarning(errorCode, "QWindowsPipeWriter: asynchronous write failed."); + break; } - OVERLAPPED *operator&() - { - return &overlapped; + // After the writer was stopped, the only reason why this function can be called is the + // completion of a cancellation. No signals should be emitted, and no new write sequence should + // be started in this case. + if (stopped) + return; + + pendingBytesWrittenValue += qint64(numberOfBytesWritten); + if (!bytesWrittenPending) { + bytesWrittenPending = true; + emit _q_queueBytesWritten(QWindowsPipeWriter::QPrivateSignal()); } +} -private: - OVERLAPPED overlapped; -}; +bool QWindowsPipeWriter::waitForNotification(int timeout) +{ + QElapsedTimer t; + t.start(); + notifiedCalled = false; + int msecs = timeout; + while (SleepEx(msecs == -1 ? INFINITE : msecs, TRUE) == WAIT_IO_COMPLETION) { + if (notifiedCalled) + return true; + + // Some other I/O completion routine was called. Wait some more. + msecs = qt_subtract_from_timeout(timeout, t.elapsed()); + if (!msecs) + break; + } + return notifiedCalled; +} -void QWindowsPipeWriter::run() +qint64 QWindowsPipeWriter::write(const char *ptr, qint64 maxlen) { - QPipeWriterOverlapped overl; - forever { - lock.lock(); - while(data.isEmpty() && (!quitNow)) { - waitCondition.wakeOne(); - waitCondition.wait(&lock); - } + if (writeSequenceStarted) + return 0; + + overlapped.clear(); + numberOfBytesToWrite = maxlen; + stopped = false; + writeSequenceStarted = true; + if (!WriteFileEx(handle, ptr, maxlen, &overlapped, &writeFileCompleted)) { + writeSequenceStarted = false; + qErrnoWarning("QWindowsPipeWriter::write failed."); + } - if (quitNow) { - lock.unlock(); - quitNow = false; - break; - } + return maxlen; +} - QByteArray copy = data; - - lock.unlock(); - - const char *ptrData = copy.data(); - qint64 maxlen = copy.size(); - qint64 totalWritten = 0; - overl.prepare(); - while ((!quitNow) && totalWritten < maxlen) { - DWORD written = 0; - if (!WriteFile(writePipe, ptrData + totalWritten, - maxlen - totalWritten, &written, &overl)) { - const DWORD writeError = GetLastError(); - if (writeError == 0xE8/*NT_STATUS_INVALID_USER_BUFFER*/) { - // give the os a rest - msleep(100); - continue; - } - if (writeError != ERROR_IO_PENDING) { - qErrnoWarning(writeError, "QWindowsPipeWriter: async WriteFile failed."); - return; - } - if (!GetOverlappedResult(writePipe, &overl, &written, TRUE)) { - qErrnoWarning(GetLastError(), "QWindowsPipeWriter: GetOverlappedResult failed."); - return; - } +void QWindowsPipeWriter::stop() +{ + stopped = true; + if (writeSequenceStarted) { + if (!qt_cancelIo(handle, &overlapped)) { + const DWORD dwError = GetLastError(); + if (dwError != ERROR_NOT_FOUND) { + qErrnoWarning(dwError, "QWindowsPipeWriter: qt_cancelIo on handle %x failed.", + handle); } - totalWritten += written; -#if defined QPIPEWRITER_DEBUG - qDebug("QWindowsPipeWriter::run() wrote %d %d/%d bytes", - written, int(totalWritten), int(maxlen)); -#endif - lock.lock(); - data.remove(0, written); - hasWritten = true; - lock.unlock(); } - emit bytesWritten(totalWritten); - emit canWrite(); + waitForNotification(-1); } } -#endif //QT_NO_THREAD - QT_END_NAMESPACE diff --git a/src/corelib/io/qwindowspipewriter_p.h b/src/corelib/io/qwindowspipewriter_p.h index 49fb85ebdd..945cbd18bf 100644 --- a/src/corelib/io/qwindowspipewriter_p.h +++ b/src/corelib/io/qwindowspipewriter_p.h @@ -52,16 +52,11 @@ // #include <qelapsedtimer.h> -#include <qthread.h> -#include <qmutex.h> -#include <qwaitcondition.h> +#include <qobject.h> #include <qt_windows.h> QT_BEGIN_NAMESPACE - -#ifndef QT_NO_THREAD - #define SLEEPMIN 10 #define SLEEPMAX 500 @@ -110,45 +105,50 @@ private: int nextSleep; }; -class Q_CORE_EXPORT QWindowsPipeWriter : public QThread +class Q_CORE_EXPORT QWindowsPipeWriter : public QObject { Q_OBJECT - -Q_SIGNALS: - void canWrite(); - void bytesWritten(qint64 bytes); - public: - explicit QWindowsPipeWriter(HANDLE writePipe, QObject * parent = 0); + explicit QWindowsPipeWriter(HANDLE pipeWriteEnd, QObject *parent = 0); ~QWindowsPipeWriter(); - bool waitForWrite(int msecs); qint64 write(const char *data, qint64 maxlen); + void stop(); + bool waitForWrite(int msecs); + qint64 bytesToWrite() const; - qint64 bytesToWrite() const - { - QMutexLocker locker(&lock); - return data.size(); - } - - bool hadWritten() const - { - return hasWritten; - } - -protected: - void run(); +Q_SIGNALS: + void canWrite(); + void bytesWritten(qint64 bytes); + void _q_queueBytesWritten(QPrivateSignal); private: - QByteArray data; - QWaitCondition waitCondition; - mutable QMutex lock; - HANDLE writePipe; - volatile bool quitNow; - bool hasWritten; -}; + static void CALLBACK writeFileCompleted(DWORD errorCode, DWORD numberOfBytesTransfered, + OVERLAPPED *overlappedBase); + void notified(DWORD errorCode, DWORD numberOfBytesWritten); + bool waitForNotification(int timeout); + void emitPendingBytesWrittenValue(); -#endif //QT_NO_THREAD + class Overlapped : public OVERLAPPED + { + Q_DISABLE_COPY(Overlapped) + public: + explicit Overlapped(QWindowsPipeWriter *pipeWriter); + void clear(); + + QWindowsPipeWriter *pipeWriter; + }; + + HANDLE handle; + Overlapped overlapped; + qint64 numberOfBytesToWrite; + qint64 pendingBytesWrittenValue; + bool stopped; + bool writeSequenceStarted; + bool notifiedCalled; + bool bytesWrittenPending; + bool inBytesWritten; +}; QT_END_NAMESPACE |