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>
This commit is contained in:
parent
3f605c8b45
commit
ee73f7b7db
@ -83,7 +83,9 @@ class QWinIoCompletionPort : protected QThread
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
QWinIoCompletionPort()
|
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"));
|
setObjectName(QLatin1String("I/O completion port thread"));
|
||||||
HANDLE hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
|
HANDLE hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
|
||||||
@ -92,13 +94,19 @@ public:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
hPort = hIOCP;
|
hPort = hIOCP;
|
||||||
|
hQueueDrainedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||||
|
if (!hQueueDrainedEvent) {
|
||||||
|
qErrnoWarning("CreateEvent failed.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
~QWinIoCompletionPort()
|
~QWinIoCompletionPort()
|
||||||
{
|
{
|
||||||
PostQueuedCompletionStatus(hPort, 0, 0, NULL);
|
PostQueuedCompletionStatus(hPort, 0, finishThreadKey, NULL);
|
||||||
QThread::wait();
|
QThread::wait();
|
||||||
CloseHandle(hPort);
|
CloseHandle(hPort);
|
||||||
|
CloseHandle(hQueueDrainedEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void registerNotifier(QWinOverlappedIoNotifier *notifier)
|
void registerNotifier(QWinOverlappedIoNotifier *notifier)
|
||||||
@ -122,6 +130,14 @@ public:
|
|||||||
mutex.unlock();
|
mutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void drainQueue()
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&drainQueueMutex);
|
||||||
|
ResetEvent(hQueueDrainedEvent);
|
||||||
|
PostQueuedCompletionStatus(hPort, 0, drainQueueKey, NULL);
|
||||||
|
WaitForSingleObject(hQueueDrainedEvent, INFINITE);
|
||||||
|
}
|
||||||
|
|
||||||
using QThread::isRunning;
|
using QThread::isRunning;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -130,23 +146,34 @@ protected:
|
|||||||
DWORD dwBytesRead;
|
DWORD dwBytesRead;
|
||||||
ULONG_PTR pulCompletionKey;
|
ULONG_PTR pulCompletionKey;
|
||||||
OVERLAPPED *overlapped;
|
OVERLAPPED *overlapped;
|
||||||
|
DWORD msecs = INFINITE;
|
||||||
|
|
||||||
forever {
|
forever {
|
||||||
BOOL success = GetQueuedCompletionStatus(hPort,
|
BOOL success = GetQueuedCompletionStatus(hPort,
|
||||||
&dwBytesRead,
|
&dwBytesRead,
|
||||||
&pulCompletionKey,
|
&pulCompletionKey,
|
||||||
&overlapped,
|
&overlapped,
|
||||||
INFINITE);
|
msecs);
|
||||||
|
|
||||||
DWORD errorCode = success ? ERROR_SUCCESS : GetLastError();
|
DWORD errorCode = success ? ERROR_SUCCESS : GetLastError();
|
||||||
if (!success && !overlapped) {
|
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.");
|
qErrnoWarning(errorCode, "GetQueuedCompletionStatus failed.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (success && !(dwBytesRead || pulCompletionKey || overlapped)) {
|
if (pulCompletionKey == finishThreadKey)
|
||||||
// We've posted null values via PostQueuedCompletionStatus to end this thread.
|
|
||||||
return;
|
return;
|
||||||
|
if (pulCompletionKey == drainQueueKey) {
|
||||||
|
// Enter drain mode.
|
||||||
|
Q_ASSERT(msecs == INFINITE);
|
||||||
|
msecs = 0;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
QWinOverlappedIoNotifier *notifier = reinterpret_cast<QWinOverlappedIoNotifier *>(pulCompletionKey);
|
QWinOverlappedIoNotifier *notifier = reinterpret_cast<QWinOverlappedIoNotifier *>(pulCompletionKey);
|
||||||
@ -158,9 +185,13 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
const ULONG_PTR finishThreadKey;
|
||||||
|
const ULONG_PTR drainQueueKey;
|
||||||
HANDLE hPort;
|
HANDLE hPort;
|
||||||
QSet<QWinOverlappedIoNotifier *> notifiers;
|
QSet<QWinOverlappedIoNotifier *> notifiers;
|
||||||
QMutex mutex;
|
QMutex mutex;
|
||||||
|
QMutex drainQueueMutex;
|
||||||
|
HANDLE hQueueDrainedEvent;
|
||||||
};
|
};
|
||||||
|
|
||||||
QWinIoCompletionPort *QWinOverlappedIoNotifier::iocp = 0;
|
QWinIoCompletionPort *QWinOverlappedIoNotifier::iocp = 0;
|
||||||
@ -224,6 +255,8 @@ bool QWinOverlappedIoNotifier::waitForNotified(int msecs, OVERLAPPED *overlapped
|
|||||||
}
|
}
|
||||||
|
|
||||||
forever {
|
forever {
|
||||||
|
if (msecs == 0)
|
||||||
|
iocp->drainQueue();
|
||||||
DWORD result = WaitForSingleObject(hSemaphore, msecs == -1 ? INFINITE : DWORD(msecs));
|
DWORD result = WaitForSingleObject(hSemaphore, msecs == -1 ? INFINITE : DWORD(msecs));
|
||||||
if (result == WAIT_OBJECT_0) {
|
if (result == WAIT_OBJECT_0) {
|
||||||
ReleaseSemaphore(hSemaphore, 1, NULL);
|
ReleaseSemaphore(hSemaphore, 1, NULL);
|
||||||
|
Loading…
Reference in New Issue
Block a user