winrt: Fix potential crash when reading closed sockets

Using multiple concurrent requests can cause a delay between a socket
closing and getting deleted. At that point the state was closingDown,
but not wasDeleted yet. Especially on slower arm devices, callbacks are
done from another thread causing synchronization issues.

Hence closingDown needs to be synced and handleReadyRead needs to have
more criterias to return early to avoid invalid access crashes.

Easiest to reproduce is heavy scrolling on the mapviewer example when it
downloads a huge amount of tiles and cancels those requests when not in
view anymore.

Change-Id: I442b6243bbefb3af938b6b1b3739a6a85b4887c0
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
This commit is contained in:
Maurice Kalinowski 2016-05-13 08:40:39 +02:00
parent 6e928460bc
commit 8714c99f65
2 changed files with 20 additions and 12 deletions

View File

@ -458,16 +458,21 @@ void QNativeSocketEngine::close()
}
#if _MSC_VER >= 1900
// To close the connection properly (not with a hard reset) all pending read operation have to
// be finished or cancelled. The API isn't available on Windows 8.1 though.
ComPtr<IStreamSocket3> socket3;
hr = d->tcpSocket()->QueryInterface(IID_PPV_ARGS(&socket3));
Q_ASSERT_SUCCEEDED(hr);
hr = QEventDispatcherWinRT::runOnXamlThread([d]() {
HRESULT hr;
// To close the connection properly (not with a hard reset) all pending read operation have to
// be finished or cancelled. The API isn't available on Windows 8.1 though.
ComPtr<IStreamSocket3> socket3;
hr = d->tcpSocket()->QueryInterface(IID_PPV_ARGS(&socket3));
Q_ASSERT_SUCCEEDED(hr);
ComPtr<IAsyncAction> action;
hr = socket3->CancelIOAsync(&action);
Q_ASSERT_SUCCEEDED(hr);
hr = QWinRTFunctions::await(action);
ComPtr<IAsyncAction> action;
hr = socket3->CancelIOAsync(&action);
Q_ASSERT_SUCCEEDED(hr);
hr = QWinRTFunctions::await(action);
Q_ASSERT_SUCCEEDED(hr);
return S_OK;
});
Q_ASSERT_SUCCEEDED(hr);
#endif // _MSC_VER >= 1900
@ -1261,9 +1266,12 @@ void QNativeSocketEnginePrivate::handleConnectionEstablished(IAsyncAction *actio
HRESULT QNativeSocketEnginePrivate::handleReadyRead(IAsyncBufferOperation *asyncInfo, AsyncStatus status)
{
Q_Q(QNativeSocketEngine);
if (wasDeleted || isDeletingChildren)
if (closingDown || wasDeleted || isDeletingChildren
|| socketState == QAbstractSocket::UnconnectedState) {
return S_OK;
}
Q_Q(QNativeSocketEngine);
// A read in UnconnectedState will close the socket and return -1 and thus tell the caller,
// that the connection was closed. The socket cannot be closed here, as the subsequent read

View File

@ -148,7 +148,7 @@ public:
qintptr socketDescriptor;
bool notifyOnRead, notifyOnWrite, notifyOnException;
bool closingDown;
QAtomicInt closingDown;
enum ErrorString {
NonBlockingInitFailedErrorString,