summaryrefslogtreecommitdiffstats
path: root/src/corelib/io/qwinoverlappedionotifier.cpp
diff options
context:
space:
mode:
authorJoerg Bornemann <joerg.bornemann@digia.com>2013-07-03 16:09:16 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-07-05 13:37:22 +0200
commitee73f7b7db91f6ba32300cb6c16978371012cfb9 (patch)
treeedfda19e2abcb0a1fabbb318fe6cd6cea7271460 /src/corelib/io/qwinoverlappedionotifier.cpp
parent3f605c8b45634c3fae06610b1102cbeae2382bec (diff)
QWinOverlappedIoNotifier: fix race condition
Fix race condition in waitForNotified for zero timeout. A waitForNotified(0) call is typically used for triggering events near the end of life of the I/O resource (e.g. drainOutputPipes in QProcess). In that case we must synchronize the IOCP thread and waitForNotified. Task-number: QTBUG-29391 Change-Id: Iadbd8564ec461cbc48a6ef8c5627e30a5c6eecfd Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@digia.com>
Diffstat (limited to 'src/corelib/io/qwinoverlappedionotifier.cpp')
-rw-r--r--src/corelib/io/qwinoverlappedionotifier.cpp43
1 files changed, 38 insertions, 5 deletions
diff --git a/src/corelib/io/qwinoverlappedionotifier.cpp b/src/corelib/io/qwinoverlappedionotifier.cpp
index 7ba862c602..914264e69e 100644
--- a/src/corelib/io/qwinoverlappedionotifier.cpp
+++ b/src/corelib/io/qwinoverlappedionotifier.cpp
@@ -83,7 +83,9 @@ class QWinIoCompletionPort : protected QThread
{
public:
QWinIoCompletionPort()
- : hPort(INVALID_HANDLE_VALUE)
+ : finishThreadKey(reinterpret_cast<ULONG_PTR>(this)),
+ drainQueueKey(reinterpret_cast<ULONG_PTR>(this + 1)),
+ hPort(INVALID_HANDLE_VALUE)
{
setObjectName(QLatin1String("I/O completion port thread"));
HANDLE hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
@@ -92,13 +94,19 @@ public:
return;
}
hPort = hIOCP;
+ hQueueDrainedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!hQueueDrainedEvent) {
+ qErrnoWarning("CreateEvent failed.");
+ return;
+ }
}
~QWinIoCompletionPort()
{
- PostQueuedCompletionStatus(hPort, 0, 0, NULL);
+ PostQueuedCompletionStatus(hPort, 0, finishThreadKey, NULL);
QThread::wait();
CloseHandle(hPort);
+ CloseHandle(hQueueDrainedEvent);
}
void registerNotifier(QWinOverlappedIoNotifier *notifier)
@@ -122,6 +130,14 @@ public:
mutex.unlock();
}
+ void drainQueue()
+ {
+ QMutexLocker locker(&drainQueueMutex);
+ ResetEvent(hQueueDrainedEvent);
+ PostQueuedCompletionStatus(hPort, 0, drainQueueKey, NULL);
+ WaitForSingleObject(hQueueDrainedEvent, INFINITE);
+ }
+
using QThread::isRunning;
protected:
@@ -130,23 +146,34 @@ protected:
DWORD dwBytesRead;
ULONG_PTR pulCompletionKey;
OVERLAPPED *overlapped;
+ DWORD msecs = INFINITE;
forever {
BOOL success = GetQueuedCompletionStatus(hPort,
&dwBytesRead,
&pulCompletionKey,
&overlapped,
- INFINITE);
+ msecs);
DWORD errorCode = success ? ERROR_SUCCESS : GetLastError();
if (!success && !overlapped) {
+ if (!msecs) {
+ // Time out in drain mode. The completion status queue is empty.
+ msecs = INFINITE;
+ SetEvent(hQueueDrainedEvent);
+ continue;
+ }
qErrnoWarning(errorCode, "GetQueuedCompletionStatus failed.");
return;
}
- if (success && !(dwBytesRead || pulCompletionKey || overlapped)) {
- // We've posted null values via PostQueuedCompletionStatus to end this thread.
+ if (pulCompletionKey == finishThreadKey)
return;
+ if (pulCompletionKey == drainQueueKey) {
+ // Enter drain mode.
+ Q_ASSERT(msecs == INFINITE);
+ msecs = 0;
+ continue;
}
QWinOverlappedIoNotifier *notifier = reinterpret_cast<QWinOverlappedIoNotifier *>(pulCompletionKey);
@@ -158,9 +185,13 @@ protected:
}
private:
+ const ULONG_PTR finishThreadKey;
+ const ULONG_PTR drainQueueKey;
HANDLE hPort;
QSet<QWinOverlappedIoNotifier *> notifiers;
QMutex mutex;
+ QMutex drainQueueMutex;
+ HANDLE hQueueDrainedEvent;
};
QWinIoCompletionPort *QWinOverlappedIoNotifier::iocp = 0;
@@ -224,6 +255,8 @@ bool QWinOverlappedIoNotifier::waitForNotified(int msecs, OVERLAPPED *overlapped
}
forever {
+ if (msecs == 0)
+ iocp->drainQueue();
DWORD result = WaitForSingleObject(hSemaphore, msecs == -1 ? INFINITE : DWORD(msecs));
if (result == WAIT_OBJECT_0) {
ReleaseSemaphore(hSemaphore, 1, NULL);