From b46e48f1b72267d14fc4b9e1969f0fc0a4eb739e Mon Sep 17 00:00:00 2001 From: Andrew Knight Date: Thu, 19 Jun 2014 10:22:16 +0300 Subject: winrt: Use native threading Instead of using std::thread, use the WinRT ThreadPool to manage threads. This allows for setting the scheduling priority, and provides a path to enable XAML integration (which requires Qt run on a background thread). QThread::terminate() is still unsupported, and only the winmain thread can be adopted due to the behavior of the thread pool when creating tasks from the GUI thread. The associated tests are now skipped, and all other QThread tests pass. Task-number: QTBUG-31397 Change-Id: Ib512a328412e1dffecdc836bc39de3ccd37afa13 Reviewed-by: Oliver Wolff Reviewed-by: Friedemann Kleint --- src/corelib/thread/qthread_p.h | 23 +- src/corelib/thread/qthread_win.cpp | 147 +------ src/corelib/thread/qthread_winrt.cpp | 458 ++++++++++++++++++++++ src/corelib/thread/thread.pri | 5 + src/winmain/qtmain_winrt.cpp | 4 + tests/auto/corelib/thread/qthread/tst_qthread.cpp | 57 +-- 6 files changed, 516 insertions(+), 178 deletions(-) create mode 100644 src/corelib/thread/qthread_winrt.cpp diff --git a/src/corelib/thread/qthread_p.h b/src/corelib/thread/qthread_p.h index e2951b125f..aec553113b 100644 --- a/src/corelib/thread/qthread_p.h +++ b/src/corelib/thread/qthread_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -65,11 +65,6 @@ #include - -#ifdef Q_OS_WINRT -#include -#endif - QT_BEGIN_NAMESPACE class QAbstractEventDispatcher; @@ -138,6 +133,10 @@ private: #ifndef QT_NO_THREAD +#ifdef Q_OS_WINRT +namespace ABI { namespace Windows { namespace Foundation { struct IAsyncAction; } } } +#endif + class QThreadPrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QThread) @@ -174,19 +173,23 @@ public: #endif // Q_OS_UNIX #ifdef Q_OS_WIN +# ifndef Q_OS_WINRT static unsigned int __stdcall start(void *); static void finish(void *, bool lockAnyway=true); +# else + HRESULT start(ABI::Windows::Foundation::IAsyncAction *); + void finish(bool lockAnyway = true); +# endif # ifndef Q_OS_WINRT Qt::HANDLE handle; - unsigned int id; # else - std::thread *handle; - std::thread::id id; + ABI::Windows::Foundation::IAsyncAction *handle; # endif + unsigned int id; int waiters; bool terminationEnabled, terminatePending; -# endif +#endif // Q_OS_WIN QThreadData *data; static void createEventDispatcher(QThreadData *data); diff --git a/src/corelib/thread/qthread_win.cpp b/src/corelib/thread/qthread_win.cpp index bdc3463b9f..e950a02162 100644 --- a/src/corelib/thread/qthread_win.cpp +++ b/src/corelib/thread/qthread_win.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -40,7 +40,7 @@ ****************************************************************************/ //#define WINVER 0x0500 -#if (_WIN32_WINNT < 0x0400) && !defined(Q_OS_WINRT) +#if (_WIN32_WINNT < 0x0400) #define _WIN32_WINNT 0x0400 #endif @@ -54,19 +54,10 @@ #include #include -#ifdef Q_OS_WINRT -#include -#else #include -#endif #include -#ifdef Q_OS_WINRT -#include -#include -#endif - #ifndef Q_OS_WINCE #ifndef _MT #define _MT @@ -79,7 +70,6 @@ #ifndef QT_NO_THREAD QT_BEGIN_NAMESPACE -#ifndef Q_OS_WINRT void qt_watch_adopted_thread(const HANDLE adoptedThreadHandle, QThread *qthread); DWORD WINAPI qt_adopted_thread_watcher_function(LPVOID); @@ -101,38 +91,6 @@ static void qt_free_tls() } } Q_DESTRUCTOR_FUNCTION(qt_free_tls) -#else // !Q_OS_WINRT - -__declspec(thread) static QThreadData* qt_current_thread_data_tls_index = 0; -void qt_create_tls() -{ -} - -static void qt_free_tls() -{ - if (qt_current_thread_data_tls_index) { - qt_current_thread_data_tls_index->deref(); - qt_current_thread_data_tls_index = 0; - } -} - -QThreadData* TlsGetValue(QThreadData*& tls) -{ - Q_ASSERT(tls == qt_current_thread_data_tls_index); - return tls; -} - -void TlsSetValue(QThreadData*& tls, QThreadData* data) -{ - Q_ASSERT(tls == qt_current_thread_data_tls_index); - if (tls) - tls->deref(); - tls = data; - if (tls) - tls->ref(); -} -Q_DESTRUCTOR_FUNCTION(qt_free_tls) -#endif // Q_OS_WINRT /* QThreadData @@ -165,7 +123,6 @@ QThreadData *QThreadData::current(bool createIfNecessary) if (!QCoreApplicationPrivate::theMainThread) { QCoreApplicationPrivate::theMainThread = threadData->thread; -#ifndef Q_OS_WINRT // TODO: is there a way to reflect the branch's behavior using // WinRT API? } else { @@ -182,7 +139,6 @@ QThreadData *QThreadData::current(bool createIfNecessary) realHandle = reinterpret_cast(GetCurrentThreadId()); #endif qt_watch_adopted_thread(realHandle, threadData->thread); -#endif // !Q_OS_WINRT } } return threadData; @@ -190,16 +146,10 @@ QThreadData *QThreadData::current(bool createIfNecessary) void QAdoptedThread::init() { -#ifndef Q_OS_WINRT d_func()->handle = GetCurrentThread(); d_func()->id = GetCurrentThreadId(); -#else - d_func()->handle = nullptr; - d_func()->id = std::this_thread::get_id(); -#endif } -#ifndef Q_OS_WINRT static QVector qt_adopted_thread_handles; static QVector qt_adopted_qthreads; static QMutex qt_adopted_thread_watcher_mutex; @@ -352,7 +302,6 @@ void qt_set_thread_name(HANDLE threadId, LPCSTR threadName) } } #endif // !QT_NO_DEBUG && Q_CC_MSVC && !Q_OS_WINCE -#endif // !Q_OS_WINRT /************************************************************************** ** QThreadPrivate @@ -362,11 +311,7 @@ void qt_set_thread_name(HANDLE threadId, LPCSTR threadName) void QThreadPrivate::createEventDispatcher(QThreadData *data) { -#ifdef Q_OS_WINRT - QEventDispatcherWinRT *theEventDispatcher = new QEventDispatcherWinRT; -#else QEventDispatcherWin32 *theEventDispatcher = new QEventDispatcherWin32; -#endif data->eventDispatcher.storeRelease(theEventDispatcher); theEventDispatcher->startingUp(); } @@ -394,7 +339,7 @@ unsigned int __stdcall QT_ENSURE_STACK_ALIGNED_FOR_SSE QThreadPrivate::start(voi else createEventDispatcher(data); -#if !defined(QT_NO_DEBUG) && defined(Q_CC_MSVC) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT) +#if !defined(QT_NO_DEBUG) && defined(Q_CC_MSVC) && !defined(Q_OS_WINCE) // sets the name of the current thread. QByteArray objectName = thr->objectName().toLocal8Bit(); qt_set_thread_name((HANDLE)-1, @@ -440,21 +385,11 @@ void QThreadPrivate::finish(void *arg, bool lockAnyway) d->interruptionRequested = false; if (!d->waiters) { -#ifndef Q_OS_WINRT CloseHandle(d->handle); -#else - d->handle->detach(); - delete d->handle; -#endif d->handle = 0; } -#ifndef Q_OS_WINRT d->id = 0; -#else - d->id = std::thread::id(); -#endif - } /************************************************************************** @@ -469,15 +404,10 @@ Qt::HANDLE QThread::currentThreadId() Q_DECL_NOTHROW int QThread::idealThreadCount() Q_DECL_NOTHROW { SYSTEM_INFO sysinfo; -#ifndef Q_OS_WINRT GetSystemInfo(&sysinfo); -#else - GetNativeSystemInfo(&sysinfo); -#endif return sysinfo.dwNumberOfProcessors; } -#ifndef Q_OS_WINRT void QThread::yieldCurrentThread() { #ifndef Q_OS_WINCE @@ -501,28 +431,6 @@ void QThread::usleep(unsigned long usecs) { ::Sleep((usecs / 1000) + 1); } -#else // !Q_OS_WINRT - -void QThread::yieldCurrentThread() -{ - msleep(1); -} - -void QThread::sleep(unsigned long secs) -{ - msleep(secs * 1000); -} - -void QThread::msleep(unsigned long msecs) -{ - WaitForSingleObjectEx(GetCurrentThread(), msecs, FALSE); -} - -void QThread::usleep(unsigned long usecs) -{ - msleep((usecs / 1000) + 1); -} -#endif // Q_OS_WINRT void QThread::start(Priority priority) { @@ -544,7 +452,6 @@ void QThread::start(Priority priority) d->returnCode = 0; d->interruptionRequested = false; -#ifndef Q_OS_WINRT /* NOTE: we create the thread in the suspended state, set the priority and then resume the thread. @@ -609,23 +516,6 @@ void QThread::start(Priority priority) if (ResumeThread(d->handle) == (DWORD) -1) { qErrnoWarning("QThread::start: Failed to resume new thread"); } -#else // !Q_OS_WINRT - d->handle = new std::thread(QThreadPrivate::start, this); - - if (!d->handle) { - qErrnoWarning(errno, "QThread::start: Failed to create thread"); - d->running = false; - d->finished = true; - return; - } - - d->id = d->handle->get_id(); - - if (priority != NormalPriority || priority != InheritPriority) { - qWarning("QThread::start: Failed to set thread priority (not implemented)"); - d->priority = NormalPriority; - } -#endif // Q_OS_WINRT } void QThread::terminate() @@ -639,11 +529,7 @@ void QThread::terminate() return; } -#ifndef Q_OS_WINRT TerminateThread(d->handle, 0); -#else // !Q_OS_WINRT - qWarning("QThread::terminate: Terminate is not supported on WinRT"); -#endif // Q_OS_WINRT QThreadPrivate::finish(this, false); } @@ -652,11 +538,7 @@ bool QThread::wait(unsigned long time) Q_D(QThread); QMutexLocker locker(&d->mutex); -#ifndef Q_OS_WINRT if (d->id == GetCurrentThreadId()) { -#else - if (d->id == std::this_thread::get_id()) { -#endif qWarning("QThread::wait: Thread tried to wait on itself"); return false; } @@ -667,7 +549,6 @@ bool QThread::wait(unsigned long time) locker.mutex()->unlock(); bool ret = false; -#ifndef Q_OS_WINRT switch (WaitForSingleObject(d->handle, time)) { case WAIT_OBJECT_0: ret = true; @@ -680,14 +561,6 @@ bool QThread::wait(unsigned long time) default: break; } -#else // !Q_OS_WINRT - if (!d->finished) { - QElapsedTimer timer; - timer.start(); - while (timer.elapsed() < time && !d->finished) - yieldCurrentThread(); - } -#endif // Q_OS_WINRT locker.mutex()->lock(); --d->waiters; @@ -699,12 +572,7 @@ bool QThread::wait(unsigned long time) } if (d->finished && !d->waiters) { -#ifndef Q_OS_WINRT CloseHandle(d->handle); -#else - d->handle->detach(); - delete d->handle; -#endif d->handle = 0; } @@ -722,16 +590,13 @@ void QThread::setTerminationEnabled(bool enabled) if (enabled && d->terminatePending) { QThreadPrivate::finish(thr, false); locker.unlock(); // don't leave the mutex locked! -#ifndef Q_OS_WINRT _endthreadex(0); -#endif } } // Caller must hold the mutex void QThreadPrivate::setPriority(QThread::Priority threadPriority) { -#ifndef Q_OS_WINRT // copied from start() with a few modifications: int prio; @@ -774,12 +639,6 @@ void QThreadPrivate::setPriority(QThread::Priority threadPriority) if (!SetThreadPriority(handle, prio)) { qErrnoWarning("QThread::setPriority: Failed to set thread priority"); } -#else // !Q_OS_WINRT - if (priority != threadPriority) { - qWarning("QThread::setPriority: Failed to set thread priority (not implemented)"); - return; - } -#endif // Q_OS_WINRT } QT_END_NAMESPACE diff --git a/src/corelib/thread/qthread_winrt.cpp b/src/corelib/thread/qthread_winrt.cpp new file mode 100644 index 0000000000..215f04f744 --- /dev/null +++ b/src/corelib/thread/qthread_winrt.cpp @@ -0,0 +1,458 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qthread.h" +#include "qthread_p.h" +#include "qthreadstorage.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::System::Threading; +using namespace ABI::Windows::System::Threading::Core; + +#ifndef QT_NO_THREAD +QT_BEGIN_NAMESPACE + +static WorkItemPriority nativePriority(QThread::Priority priority) +{ + switch (priority) { + default: + case QThread::NormalPriority: + return WorkItemPriority_Normal; + case QThread::IdlePriority: + case QThread::LowestPriority: + case QThread::LowPriority: + return WorkItemPriority_Low; + case QThread::HighPriority: + case QThread::HighestPriority: + case QThread::TimeCriticalPriority: + return WorkItemPriority_High; + } +} + +class QWinRTThreadGlobal +{ +public: + QWinRTThreadGlobal() + { + HRESULT hr; + + hr = RoGetActivationFactory( + HString::MakeReference(RuntimeClass_Windows_System_Threading_Core_PreallocatedWorkItem).Get(), + IID_PPV_ARGS(&workItemFactory)); + Q_ASSERT_SUCCEEDED(hr); + + hr = RoGetActivationFactory( + HString::MakeReference(RuntimeClass_Windows_System_Threading_Core_SignalNotifier).Get(), + IID_PPV_ARGS(¬ifierFactory)); + Q_ASSERT_SUCCEEDED(hr); + + QString eventName = QUuid::createUuid().toString(); + dispatchEvent = CreateEventEx(NULL, reinterpret_cast(eventName.utf16()), 0, EVENT_ALL_ACCESS); + + hr = notifierFactory->AttachToEvent( + HStringReference(reinterpret_cast(eventName.utf16())).Get(), + Callback(this, &QWinRTThreadGlobal::dispatch).Get(), ¬ifier); + Q_ASSERT_SUCCEEDED(hr); + hr = notifier->Enable(); + Q_ASSERT_SUCCEEDED(hr); + } + + ~QWinRTThreadGlobal() + { + CloseHandle(dispatchEvent); + } + + void dispatch() + { + SetEvent(dispatchEvent); + } + + void push(QThreadPrivate *d) + { + threads.append(d); + } + +private: + HRESULT dispatch(ISignalNotifier *notifier, boolean timedOut) + { + Q_UNUSED(timedOut); + notifier->Enable(); + if (threads.isEmpty()) + return S_OK; + + QThreadPrivate *thread = threads.takeFirst(); + ComPtr workItem; + HRESULT hr = workItemFactory->CreateWorkItemWithPriority( + Callback(thread, &QThreadPrivate::start).Get(), + nativePriority(thread->priority), &workItem); + if (FAILED(hr)) { + qErrnoWarning(hr, "Failed to create thread work item"); + thread->finish(); + return hr; + } + + hr = workItem->RunAsync(&thread->handle); + if (FAILED(hr)) { + qErrnoWarning(hr, "Failed to run work item"); + thread->finish(); + return hr; + } + + return S_OK; + } + + HANDLE dispatchEvent; + ComPtr notifier; + ComPtr notifierFactory; + ComPtr workItemFactory; + + QList threads; +}; +Q_GLOBAL_STATIC(QWinRTThreadGlobal, g) + +/************************************************************************** + ** QThreadData + *************************************************************************/ + +__declspec(thread) static QThreadData *qt_current_thread_data = 0; + +void QThreadData::clearCurrentThreadData() +{ + qt_current_thread_data = 0; +} + +QThreadData *QThreadData::current(bool createIfNecessary) +{ + static bool winmainThread = true; + QThreadData *threadData = qt_current_thread_data; + if (!threadData && createIfNecessary) { + threadData = new QThreadData; + // This needs to be called prior to new AdoptedThread() to + // avoid recursion. + qt_current_thread_data = threadData; + QT_TRY { + threadData->thread = new QAdoptedThread(threadData); + } QT_CATCH(...) { + qt_current_thread_data = 0; + threadData->deref(); + threadData = 0; + QT_RETHROW; + } + threadData->deref(); + threadData->isAdopted = true; + threadData->threadId = reinterpret_cast(GetCurrentThreadId()); + + if (!QCoreApplicationPrivate::theMainThread && !winmainThread) + QCoreApplicationPrivate::theMainThread = threadData->thread; + + if (winmainThread) { + g->dispatch(); + winmainThread = false; + } + } + + return threadData; +} + +void QAdoptedThread::init() +{ + Q_D(QThread); + + d->handle = Q_NULLPTR; + d->id = 0; + d->createEventDispatcher(d->data); +} + +/************************************************************************** + ** QThreadPrivate + *************************************************************************/ + +#endif // QT_NO_THREAD + +void QThreadPrivate::createEventDispatcher(QThreadData *data) +{ + QEventDispatcherWinRT *eventDispatcher = new QEventDispatcherWinRT; + data->eventDispatcher.storeRelease(eventDispatcher); + eventDispatcher->startingUp(); +} + +#ifndef QT_NO_THREAD + +HRESULT QThreadPrivate::start(IAsyncAction *) +{ + Q_Q(QThread); + + qt_current_thread_data = data; + id = GetCurrentThreadId(); + data->threadId = reinterpret_cast(id); + QThread::setTerminationEnabled(false); + + { + QMutexLocker locker(&mutex); + data->quitNow = exited; + } + + if (data->eventDispatcher.load()) + data->eventDispatcher.load()->startingUp(); + else + createEventDispatcher(data); + + running = true; + emit q->started(QThread::QPrivateSignal()); + + QThread::setTerminationEnabled(true); + + q->run(); + + finish(); + + return S_OK; +} + +void QThreadPrivate::finish(bool lockAnyway) +{ + Q_Q(QThread); + + QMutexLocker locker(lockAnyway ? &mutex : 0); + isInFinish = true; + priority = QThread::InheritPriority; + void **tls_data = reinterpret_cast(&data->tls); + locker.unlock(); + emit q->finished(QThread::QPrivateSignal()); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QThreadStorageData::finish(tls_data); + locker.relock(); + + QAbstractEventDispatcher *eventDispatcher = data->eventDispatcher.load(); + if (eventDispatcher) { + data->eventDispatcher = 0; + locker.unlock(); + eventDispatcher->closingDown(); + delete eventDispatcher; + locker.relock(); + } + + running = false; + finished = true; + isInFinish = false; + interruptionRequested = false; + + if (!waiters) { + if (handle) + handle->Release(); + handle = Q_NULLPTR; + } + + id = 0; +} + +/************************************************************************** + ** QThread + *************************************************************************/ + +Qt::HANDLE QThread::currentThreadId() Q_DECL_NOTHROW +{ + return reinterpret_cast(GetCurrentThreadId()); +} + +int QThread::idealThreadCount() Q_DECL_NOTHROW +{ + SYSTEM_INFO sysinfo; + GetNativeSystemInfo(&sysinfo); + return sysinfo.dwNumberOfProcessors; +} + +void QThread::yieldCurrentThread() +{ + msleep(1); +} + +void QThread::sleep(unsigned long secs) +{ + msleep(secs * 1000); +} + +void QThread::msleep(unsigned long msecs) +{ + WaitForSingleObjectEx(GetCurrentThread(), msecs, FALSE); +} + +void QThread::usleep(unsigned long usecs) +{ + msleep((usecs / 1000) + 1); +} + +void QThread::start(Priority priority) +{ + Q_D(QThread); + QMutexLocker locker(&d->mutex); + + if (d->isInFinish) { + locker.unlock(); + wait(); + locker.relock(); + } + + if (d->running) + return; + + d->finished = false; + d->exited = false; + d->returnCode = 0; + d->interruptionRequested = false; + d->priority = priority == QThread::InheritPriority ? currentThread()->priority() : priority; + g->push(d); + g->dispatch(); + + locker.unlock(); + while (!d->running && !d->finished) { + QAbstractEventDispatcher *eventDispatcher = QThread::currentThread()->eventDispatcher(); + if (eventDispatcher) + eventDispatcher->processEvents(QEventLoop::AllEvents); + else + yieldCurrentThread(); + } +} + +void QThread::terminate() +{ + Q_D(QThread); + QMutexLocker locker(&d->mutex); + if (!d->running) + return; + if (!d->terminationEnabled) { + d->terminatePending = true; + return; + } + + if (d->handle) { + ComPtr info; + HRESULT hr = d->handle->QueryInterface(IID_PPV_ARGS(&info)); + Q_ASSERT_SUCCEEDED(hr); + hr = info->Cancel(); + if (FAILED(hr)) + qErrnoWarning(hr, "Failed to cancel thread action"); + } + + d->finish(false); +} + +bool QThread::wait(unsigned long time) +{ + Q_D(QThread); + QMutexLocker locker(&d->mutex); + + if (d->id == GetCurrentThreadId()) { + qWarning("QThread::wait: Thread tried to wait on itself"); + return false; + } + if (d->finished || !d->running) + return true; + + ++d->waiters; + locker.mutex()->unlock(); + + // Alternatively, we could check the handle + bool ret = false; + if (!d->finished) { + QElapsedTimer timer; + timer.start(); + while (timer.elapsed() < time && !d->finished) + yieldCurrentThread(); + + ret = d->finished; + } + + locker.mutex()->lock(); + --d->waiters; + + if (ret && !d->finished) { + // thread was terminated by someone else + + d->finish(false); + } + + if (d->finished && !d->waiters) { + if (d->handle) + d->handle->Release(); + d->handle = Q_NULLPTR; + } + + return ret; +} + +void QThread::setTerminationEnabled(bool enabled) +{ + QThread *thr = currentThread(); + Q_ASSERT_X(thr != 0, "QThread::setTerminationEnabled()", + "Current thread was not started with QThread."); + QThreadPrivate *d = thr->d_func(); + QMutexLocker locker(&d->mutex); + d->terminationEnabled = enabled; + if (enabled && d->terminatePending) { + d->finish(false); + locker.unlock(); // don't leave the mutex locked! + } +} + +// Caller must hold the mutex +void QThreadPrivate::setPriority(QThread::Priority threadPriority) +{ + if (running) + qWarning("WinRT threads can't change priority while running."); + + priority = threadPriority; +} + +QT_END_NAMESPACE +#endif // QT_NO_THREAD diff --git a/src/corelib/thread/thread.pri b/src/corelib/thread/thread.pri index 3c1ddd984a..2e027c8e21 100644 --- a/src/corelib/thread/thread.pri +++ b/src/corelib/thread/thread.pri @@ -50,6 +50,11 @@ win32:SOURCES += thread/qmutex_win.cpp \ thread/qthread_win.cpp \ thread/qwaitcondition_win.cpp +winrt { + SOURCES -= thread/qthread_win.cpp + SOURCES += thread/qthread_winrt.cpp +} + integrity:SOURCES += thread/qmutex_unix.cpp \ thread/qthread_unix.cpp \ thread/qwaitcondition_unix.cpp diff --git a/src/winmain/qtmain_winrt.cpp b/src/winmain/qtmain_winrt.cpp index bcb3445bcd..43bce0862a 100644 --- a/src/winmain/qtmain_winrt.cpp +++ b/src/winmain/qtmain_winrt.cpp @@ -67,6 +67,7 @@ extern "C" { #include #include #include +#include #include #include @@ -237,6 +238,9 @@ int __stdcall WinMain(HINSTANCE, HINSTANCE, LPSTR, int) if (FAILED(RoInitialize(RO_INIT_MULTITHREADED))) return 1; + // Mark the main thread + QThread::currentThread(); + Core::ICoreApplication *appFactory; if (FAILED(RoGetActivationFactory(qHString(CoreApplicationClass), IID_PPV_ARGS(&appFactory)))) return 2; diff --git a/tests/auto/corelib/thread/qthread/tst_qthread.cpp b/tests/auto/corelib/thread/qthread/tst_qthread.cpp index 0e53139414..a8c052119c 100644 --- a/tests/auto/corelib/thread/qthread/tst_qthread.cpp +++ b/tests/auto/corelib/thread/qthread/tst_qthread.cpp @@ -53,13 +53,11 @@ #ifdef Q_OS_UNIX #include #endif -#if defined(Q_OS_WINCE) +#if defined(Q_OS_WIN) #include -#elif defined(Q_OS_WINRT) -#include -#elif defined(Q_OS_WIN) +#if defined(Q_OS_WIN32) #include -#include +#endif #endif class tst_QThread : public QObject @@ -328,9 +326,6 @@ void tst_QThread::isRunning() void tst_QThread::setPriority() { -#if defined(Q_OS_WINRT) - QSKIP("Thread priority is not supported on WinRT"); -#endif Simple_Thread thread; // cannot change the priority, since the thread is not running @@ -465,10 +460,6 @@ void tst_QThread::start() QVERIFY(!thread.isFinished()); QVERIFY(!thread.isRunning()); QMutexLocker locker(&thread.mutex); -#ifdef Q_OS_WINRT - if (priorities[i] != QThread::NormalPriority && priorities[i] != QThread::InheritPriority) - QTest::ignoreMessage(QtWarningMsg, "QThread::start: Failed to set thread priority (not implemented)"); -#endif thread.start(priorities[i]); QVERIFY(thread.isRunning()); QVERIFY(!thread.isFinished()); @@ -482,7 +473,7 @@ void tst_QThread::start() void tst_QThread::terminate() { #if defined(Q_OS_WINRT) - QSKIP("Terminate is not supported on WinRT"); + QSKIP("Thread termination is not supported on WinRT."); #endif Terminate_Thread thread; { @@ -548,7 +539,7 @@ void tst_QThread::finished() void tst_QThread::terminated() { #if defined(Q_OS_WINRT) - QSKIP("Terminate is not supported on WinRT"); + QSKIP("Thread termination is not supported on WinRT."); #endif SignalRecorder recorder; Terminate_Thread thread; @@ -645,8 +636,6 @@ void noop(void*) { } #if defined Q_OS_UNIX typedef pthread_t ThreadHandle; -#elif defined Q_OS_WINRT - typedef std::thread ThreadHandle; #elif defined Q_OS_WIN typedef HANDLE ThreadHandle; #endif @@ -689,7 +678,7 @@ void NativeThreadWrapper::start(FunctionPointer functionPointer, void *data) const int state = pthread_create(&nativeThreadHandle, 0, NativeThreadWrapper::runUnix, this); Q_UNUSED(state); #elif defined(Q_OS_WINRT) - nativeThreadHandle = std::thread(NativeThreadWrapper::runWin, this); + // creating a new worker from within the GUI thread is not supported #elif defined(Q_OS_WINCE) nativeThreadHandle = CreateThread(NULL, 0 , (LPTHREAD_START_ROUTINE)NativeThreadWrapper::runWin , this, 0, NULL); #elif defined Q_OS_WIN @@ -710,7 +699,7 @@ void NativeThreadWrapper::join() #if defined Q_OS_UNIX pthread_join(nativeThreadHandle, 0); #elif defined Q_OS_WINRT - nativeThreadHandle.join(); + // not supported #elif defined Q_OS_WIN WaitForSingleObject(nativeThreadHandle, INFINITE); CloseHandle(nativeThreadHandle); @@ -766,6 +755,9 @@ void testNativeThreadAdoption(void *) } void tst_QThread::nativeThreadAdoption() { +#ifdef Q_OS_WINRT + QSKIP("Native thread adoption is not supported on WinRT."); +#endif threadAdoptedOk = false; mainThread = QThread::currentThread(); NativeThreadWrapper nativeThread; @@ -789,6 +781,9 @@ void adoptedThreadAffinityFunction(void *arg) void tst_QThread::adoptedThreadAffinity() { +#ifdef Q_OS_WINRT + QSKIP("Native thread adoption is not supported on WinRT."); +#endif QThread *affinity[2] = { 0, 0 }; NativeThreadWrapper thread; @@ -801,10 +796,9 @@ void tst_QThread::adoptedThreadAffinity() void tst_QThread::adoptedThreadSetPriority() { -#if defined(Q_OS_WINRT) - QSKIP("Thread priority is not supported on WinRT"); +#ifdef Q_OS_WINRT + QSKIP("Native thread adoption is not supported on WinRT."); #endif - NativeThreadWrapper nativeThread; nativeThread.setWaitForStop(); nativeThread.startAndWait(); @@ -832,6 +826,9 @@ void tst_QThread::adoptedThreadSetPriority() void tst_QThread::adoptedThreadExit() { +#ifdef Q_OS_WINRT + QSKIP("Native thread adoption is not supported on WinRT."); +#endif NativeThreadWrapper nativeThread; nativeThread.setWaitForStop(); @@ -861,6 +858,9 @@ void adoptedThreadExecFunction(void *) void tst_QThread::adoptedThreadExec() { +#ifdef Q_OS_WINRT + QSKIP("Native thread adoption is not supported on WinRT."); +#endif NativeThreadWrapper nativeThread; nativeThread.start(adoptedThreadExecFunction); nativeThread.join(); @@ -871,6 +871,9 @@ void tst_QThread::adoptedThreadExec() */ void tst_QThread::adoptedThreadFinished() { +#ifdef Q_OS_WINRT + QSKIP("Native thread adoption is not supported on WinRT."); +#endif NativeThreadWrapper nativeThread; nativeThread.setWaitForStop(); nativeThread.startAndWait(); @@ -889,6 +892,9 @@ void tst_QThread::adoptedThreadFinished() void tst_QThread::adoptedThreadExecFinished() { +#ifdef Q_OS_WINRT + QSKIP("Native thread adoption is not supported on WinRT."); +#endif NativeThreadWrapper nativeThread; nativeThread.setWaitForStop(); nativeThread.startAndWait(adoptedThreadExecFunction); @@ -899,14 +905,14 @@ void tst_QThread::adoptedThreadExecFinished() nativeThread.join(); QTestEventLoop::instance().enterLoop(5); -#if defined(Q_OS_WINRT) - QEXPECT_FAIL("", "QTBUG-31397: Known not to work on WinRT", Abort); -#endif QVERIFY(!QTestEventLoop::instance().timeout()); } void tst_QThread::adoptMultipleThreads() { +#ifdef Q_OS_WINRT + QSKIP("Native thread adoption is not supported on WinRT."); +#endif #if defined(Q_OS_WIN) // Windows CE is not capable of handling that many threads. On the emulator it is dead with 26 threads already. # if defined(Q_OS_WINCE) @@ -947,6 +953,9 @@ void tst_QThread::adoptMultipleThreads() void tst_QThread::adoptMultipleThreadsOverlap() { +#ifdef Q_OS_WINRT + QSKIP("Native thread adoption is not supported on WinRT."); +#endif #if defined(Q_OS_WIN) // Windows CE is not capable of handling that many threads. On the emulator it is dead with 26 threads already. # if defined(Q_OS_WINCE) -- cgit v1.2.3