winrt: Don't wait for runOnXamlThread to finish for timers.

Fixes possible deadlock that occurs when synchronous WS event handling
(introduced in ee767c8) is used across threads.

Task-Id: QTBUG-49051
Change-Id: Iae973c2d4f4619b9eeb6e9393330b166ec608d27
Reviewed-by: Andrew Knight <andrew.knight@intopalo.com>
This commit is contained in:
Samuel Nevala 2015-11-03 16:25:54 +02:00
parent 39639694c8
commit b0e89845df
2 changed files with 15 additions and 13 deletions

View File

@ -97,8 +97,6 @@ public:
~QEventDispatcherWinRTPrivate(); ~QEventDispatcherWinRTPrivate();
private: private:
ComPtr<IThreadPoolTimerStatics> timerFactory;
QHash<int, QObject *> timerIdToObject; QHash<int, QObject *> timerIdToObject;
QVector<WinRTTimerInfo> timerInfos; QVector<WinRTTimerInfo> timerInfos;
QHash<HANDLE, int> timerHandleToId; QHash<HANDLE, int> timerHandleToId;
@ -167,7 +165,7 @@ QEventDispatcherWinRT::~QEventDispatcherWinRT()
{ {
} }
HRESULT QEventDispatcherWinRT::runOnXamlThread(const std::function<HRESULT ()> &delegate) HRESULT QEventDispatcherWinRT::runOnXamlThread(const std::function<HRESULT ()> &delegate, bool waitForRun)
{ {
static __declspec(thread) ICoreDispatcher *dispatcher = nullptr; static __declspec(thread) ICoreDispatcher *dispatcher = nullptr;
if (!dispatcher) { if (!dispatcher) {
@ -194,7 +192,7 @@ HRESULT QEventDispatcherWinRT::runOnXamlThread(const std::function<HRESULT ()> &
ComPtr<IAsyncAction> op; ComPtr<IAsyncAction> op;
hr = dispatcher->RunAsync(CoreDispatcherPriority_Normal, Make<AgileDispatchedHandler>(delegate).Get(), &op); hr = dispatcher->RunAsync(CoreDispatcherPriority_Normal, Make<AgileDispatchedHandler>(delegate).Get(), &op);
if (FAILED(hr)) if (FAILED(hr) || !waitForRun)
return hr; return hr;
return QWinRTFunctions::await(op); return QWinRTFunctions::await(op);
} }
@ -292,9 +290,16 @@ void QEventDispatcherWinRT::registerTimer(int timerId, int interval, Qt::TimerTy
period.Duration = qMax(qint64(1), qint64(interval) * 10000); period.Duration = qMax(qint64(1), qint64(interval) * 10000);
const HANDLE handle = CreateEventEx(NULL, NULL, CREATE_EVENT_MANUAL_RESET, SYNCHRONIZE | EVENT_MODIFY_STATE); const HANDLE handle = CreateEventEx(NULL, NULL, CREATE_EVENT_MANUAL_RESET, SYNCHRONIZE | EVENT_MODIFY_STATE);
const HANDLE cancelHandle = CreateEventEx(NULL, NULL, CREATE_EVENT_MANUAL_RESET, SYNCHRONIZE|EVENT_MODIFY_STATE); const HANDLE cancelHandle = CreateEventEx(NULL, NULL, CREATE_EVENT_MANUAL_RESET, SYNCHRONIZE|EVENT_MODIFY_STATE);
HRESULT hr = runOnXamlThread([&]() { HRESULT hr = runOnXamlThread([cancelHandle, handle, period]() {
static ComPtr<IThreadPoolTimerStatics> timerFactory;
HRESULT hr;
if (!timerFactory) {
hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_System_Threading_ThreadPoolTimer).Get(),
&timerFactory);
Q_ASSERT_SUCCEEDED(hr);
}
IThreadPoolTimer *timer; IThreadPoolTimer *timer;
HRESULT hr = d->timerFactory->CreatePeriodicTimerWithCompletion( hr = timerFactory->CreatePeriodicTimerWithCompletion(
Callback<ITimerElapsedHandler>([handle, cancelHandle](IThreadPoolTimer *timer) { Callback<ITimerElapsedHandler>([handle, cancelHandle](IThreadPoolTimer *timer) {
DWORD cancelResult = WaitForSingleObjectEx(cancelHandle, 0, TRUE); DWORD cancelResult = WaitForSingleObjectEx(cancelHandle, 0, TRUE);
if (cancelResult == WAIT_OBJECT_0) { if (cancelResult == WAIT_OBJECT_0) {
@ -314,14 +319,14 @@ void QEventDispatcherWinRT::registerTimer(int timerId, int interval, Qt::TimerTy
return S_OK; return S_OK;
}).Get(), &timer); }).Get(), &timer);
RETURN_HR_IF_FAILED("Failed to create periodic timer"); RETURN_HR_IF_FAILED("Failed to create periodic timer");
d->addTimer(timerId, interval, timerType, object, handle, cancelHandle);
return hr; return hr;
}); }, false);
if (FAILED(hr)) { if (FAILED(hr)) {
CloseHandle(handle); CloseHandle(handle);
CloseHandle(cancelHandle); CloseHandle(cancelHandle);
return;
} }
d->addTimer(timerId, interval, timerType, object, handle, cancelHandle);
} }
bool QEventDispatcherWinRT::unregisterTimer(int timerId) bool QEventDispatcherWinRT::unregisterTimer(int timerId)
@ -495,9 +500,6 @@ QEventDispatcherWinRTPrivate::QEventDispatcherWinRTPrivate()
const bool isGuiThread = QCoreApplication::instance() && const bool isGuiThread = QCoreApplication::instance() &&
QThread::currentThread() == QCoreApplication::instance()->thread(); QThread::currentThread() == QCoreApplication::instance()->thread();
CoInitializeEx(NULL, isGuiThread ? COINIT_APARTMENTTHREADED : COINIT_MULTITHREADED); CoInitializeEx(NULL, isGuiThread ? COINIT_APARTMENTTHREADED : COINIT_MULTITHREADED);
HRESULT hr;
hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_System_Threading_ThreadPoolTimer).Get(), &timerFactory);
Q_ASSERT_SUCCEEDED(hr);
HANDLE interruptHandle = CreateEventEx(NULL, NULL, NULL, SYNCHRONIZE|EVENT_MODIFY_STATE); HANDLE interruptHandle = CreateEventEx(NULL, NULL, NULL, SYNCHRONIZE|EVENT_MODIFY_STATE);
timerIdToHandle.insert(INTERRUPT_HANDLE, interruptHandle); timerIdToHandle.insert(INTERRUPT_HANDLE, interruptHandle);
timerHandleToId.insert(interruptHandle, INTERRUPT_HANDLE); timerHandleToId.insert(interruptHandle, INTERRUPT_HANDLE);

View File

@ -67,7 +67,7 @@ public:
explicit QEventDispatcherWinRT(QObject *parent = 0); explicit QEventDispatcherWinRT(QObject *parent = 0);
~QEventDispatcherWinRT(); ~QEventDispatcherWinRT();
static HRESULT runOnXamlThread(const std::function<HRESULT()> &delegate); static HRESULT runOnXamlThread(const std::function<HRESULT()> &delegate, bool waitForRun = true);
bool processEvents(QEventLoop::ProcessEventsFlags flags); bool processEvents(QEventLoop::ProcessEventsFlags flags);
bool hasPendingEvents(); bool hasPendingEvents();