Do not wait in QWindowsPipe{Reader|Writer}::stop()

A deadlock can occur if the user does

    QLocalSocket *ls = new QLocalSocket;
    ls->moveToThread(t);
    ...
    delete ls;

Then QLocalSocket calls QWindowsPipeReader::stop() in a different thread
than the I/O operation is running in. The waitForNotified(-1) call would
then wait indefinitely until the I/O thread is in alertable wait state
again. Especially on application shut down this might never be the case,
and the application would deadlock.

Solve this by detaching the Overlapped object from the
QWindowsPipe{Reader|Writer} in stop() and delete it in the callback.

Task-number: QTBUG-61643
Change-Id: Ie262d75c5fd92ac7cf7dfcdbf1519050be9fd3c4
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@qt.io>
This commit is contained in:
Joerg Bornemann 2017-07-12 14:20:54 +02:00
parent 0345608d16
commit c7ec07d401
4 changed files with 30 additions and 14 deletions

View File

@ -57,7 +57,7 @@ void QWindowsPipeReader::Overlapped::clear()
QWindowsPipeReader::QWindowsPipeReader(QObject *parent) QWindowsPipeReader::QWindowsPipeReader(QObject *parent)
: QObject(parent), : QObject(parent),
handle(INVALID_HANDLE_VALUE), handle(INVALID_HANDLE_VALUE),
overlapped(this), overlapped(nullptr),
readBufferMaxSize(0), readBufferMaxSize(0),
actualReadBufferSize(0), actualReadBufferSize(0),
stopped(true), stopped(true),
@ -74,6 +74,7 @@ QWindowsPipeReader::QWindowsPipeReader(QObject *parent)
QWindowsPipeReader::~QWindowsPipeReader() QWindowsPipeReader::~QWindowsPipeReader()
{ {
stop(); stop();
delete overlapped;
} }
/*! /*!
@ -95,14 +96,16 @@ void QWindowsPipeReader::stop()
{ {
stopped = true; stopped = true;
if (readSequenceStarted) { if (readSequenceStarted) {
if (!CancelIoEx(handle, &overlapped)) { overlapped->pipeReader = nullptr;
if (!CancelIoEx(handle, overlapped)) {
const DWORD dwError = GetLastError(); const DWORD dwError = GetLastError();
if (dwError != ERROR_NOT_FOUND) { if (dwError != ERROR_NOT_FOUND) {
qErrnoWarning(dwError, "QWindowsPipeReader: CancelIoEx on handle %p failed.", qErrnoWarning(dwError, "QWindowsPipeReader: CancelIoEx on handle %p failed.",
handle); handle);
} }
} }
waitForNotification(-1); overlapped = nullptr; // The object will be deleted in the I/O callback.
readSequenceStarted = false;
} }
} }
@ -232,8 +235,10 @@ void QWindowsPipeReader::startAsyncRead()
stopped = false; stopped = false;
readSequenceStarted = true; readSequenceStarted = true;
overlapped.clear(); if (!overlapped)
if (!ReadFileEx(handle, ptr, bytesToRead, &overlapped, &readFileCompleted)) { overlapped = new Overlapped(this);
overlapped->clear();
if (!ReadFileEx(handle, ptr, bytesToRead, overlapped, &readFileCompleted)) {
readSequenceStarted = false; readSequenceStarted = false;
const DWORD dwError = GetLastError(); const DWORD dwError = GetLastError();
@ -260,7 +265,10 @@ void QWindowsPipeReader::readFileCompleted(DWORD errorCode, DWORD numberOfBytesT
OVERLAPPED *overlappedBase) OVERLAPPED *overlappedBase)
{ {
Overlapped *overlapped = static_cast<Overlapped *>(overlappedBase); Overlapped *overlapped = static_cast<Overlapped *>(overlappedBase);
overlapped->pipeReader->notified(errorCode, numberOfBytesTransfered); if (overlapped->pipeReader)
overlapped->pipeReader->notified(errorCode, numberOfBytesTransfered);
else
delete overlapped;
} }
/*! /*!

View File

@ -105,7 +105,7 @@ private:
}; };
HANDLE handle; HANDLE handle;
Overlapped overlapped; Overlapped *overlapped;
qint64 readBufferMaxSize; qint64 readBufferMaxSize;
QRingBuffer readBuffer; QRingBuffer readBuffer;
qint64 actualReadBufferSize; qint64 actualReadBufferSize;

View File

@ -56,7 +56,7 @@ void QWindowsPipeWriter::Overlapped::clear()
QWindowsPipeWriter::QWindowsPipeWriter(HANDLE pipeWriteEnd, QObject *parent) QWindowsPipeWriter::QWindowsPipeWriter(HANDLE pipeWriteEnd, QObject *parent)
: QObject(parent), : QObject(parent),
handle(pipeWriteEnd), handle(pipeWriteEnd),
overlapped(this), overlapped(nullptr),
numberOfBytesToWrite(0), numberOfBytesToWrite(0),
pendingBytesWrittenValue(0), pendingBytesWrittenValue(0),
stopped(true), stopped(true),
@ -72,6 +72,7 @@ QWindowsPipeWriter::QWindowsPipeWriter(HANDLE pipeWriteEnd, QObject *parent)
QWindowsPipeWriter::~QWindowsPipeWriter() QWindowsPipeWriter::~QWindowsPipeWriter()
{ {
stop(); stop();
delete overlapped;
} }
bool QWindowsPipeWriter::waitForWrite(int msecs) bool QWindowsPipeWriter::waitForWrite(int msecs)
@ -122,7 +123,10 @@ void QWindowsPipeWriter::writeFileCompleted(DWORD errorCode, DWORD numberOfBytes
OVERLAPPED *overlappedBase) OVERLAPPED *overlappedBase)
{ {
Overlapped *overlapped = static_cast<Overlapped *>(overlappedBase); Overlapped *overlapped = static_cast<Overlapped *>(overlappedBase);
overlapped->pipeWriter->notified(errorCode, numberOfBytesTransfered); if (overlapped->pipeWriter)
overlapped->pipeWriter->notified(errorCode, numberOfBytesTransfered);
else
delete overlapped;
} }
/*! /*!
@ -185,13 +189,15 @@ bool QWindowsPipeWriter::write(const QByteArray &ba)
if (writeSequenceStarted) if (writeSequenceStarted)
return false; return false;
overlapped.clear(); if (!overlapped)
overlapped = new Overlapped(this);
overlapped->clear();
buffer = ba; buffer = ba;
numberOfBytesToWrite = buffer.size(); numberOfBytesToWrite = buffer.size();
stopped = false; stopped = false;
writeSequenceStarted = true; writeSequenceStarted = true;
if (!WriteFileEx(handle, buffer.constData(), numberOfBytesToWrite, if (!WriteFileEx(handle, buffer.constData(), numberOfBytesToWrite,
&overlapped, &writeFileCompleted)) { overlapped, &writeFileCompleted)) {
writeSequenceStarted = false; writeSequenceStarted = false;
numberOfBytesToWrite = 0; numberOfBytesToWrite = 0;
buffer.clear(); buffer.clear();
@ -208,14 +214,16 @@ void QWindowsPipeWriter::stop()
bytesWrittenPending = false; bytesWrittenPending = false;
pendingBytesWrittenValue = 0; pendingBytesWrittenValue = 0;
if (writeSequenceStarted) { if (writeSequenceStarted) {
if (!CancelIoEx(handle, &overlapped)) { overlapped->pipeWriter = nullptr;
if (!CancelIoEx(handle, overlapped)) {
const DWORD dwError = GetLastError(); const DWORD dwError = GetLastError();
if (dwError != ERROR_NOT_FOUND) { if (dwError != ERROR_NOT_FOUND) {
qErrnoWarning(dwError, "QWindowsPipeWriter: CancelIoEx on handle %p failed.", qErrnoWarning(dwError, "QWindowsPipeWriter: CancelIoEx on handle %p failed.",
handle); handle);
} }
} }
waitForNotification(-1); overlapped = nullptr; // The object will be deleted in the I/O callback.
writeSequenceStarted = false;
} }
} }

View File

@ -143,7 +143,7 @@ private:
}; };
HANDLE handle; HANDLE handle;
Overlapped overlapped; Overlapped *overlapped;
QByteArray buffer; QByteArray buffer;
qint64 numberOfBytesToWrite; qint64 numberOfBytesToWrite;
qint64 pendingBytesWrittenValue; qint64 pendingBytesWrittenValue;