diff options
Diffstat (limited to 'src/corelib/io/qwindowspipewriter.cpp')
-rw-r--r-- | src/corelib/io/qwindowspipewriter.cpp | 239 |
1 files changed, 136 insertions, 103 deletions
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 |