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 <oliver.wolff@digia.com> Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
This commit is contained in:
parent
50001dc801
commit
b46e48f1b7
@ -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 <algorithm>
|
||||
|
||||
|
||||
#ifdef Q_OS_WINRT
|
||||
#include <thread>
|
||||
#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);
|
||||
|
@ -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 <qpointer.h>
|
||||
|
||||
#include <private/qcoreapplication_p.h>
|
||||
#ifdef Q_OS_WINRT
|
||||
#include <private/qeventdispatcher_winrt_p.h>
|
||||
#else
|
||||
#include <private/qeventdispatcher_win_p.h>
|
||||
#endif
|
||||
|
||||
#include <qt_windows.h>
|
||||
|
||||
#ifdef Q_OS_WINRT
|
||||
#include <qelapsedtimer.h>
|
||||
#include <thread>
|
||||
#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<HANDLE>(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<HANDLE> qt_adopted_thread_handles;
|
||||
static QVector<QThread *> 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
|
||||
|
458
src/corelib/thread/qthread_winrt.cpp
Normal file
458
src/corelib/thread/qthread_winrt.cpp
Normal file
@ -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 <QtCore/QElapsedTimer>
|
||||
#include <QtCore/QUuid>
|
||||
#include <QtCore/qt_windows.h>
|
||||
#include <QtCore/qfunctions_winrt.h>
|
||||
#include <QtCore/private/qcoreapplication_p.h>
|
||||
#include <QtCore/private/qeventdispatcher_winrt_p.h>
|
||||
|
||||
#include <wrl.h>
|
||||
#include <windows.system.threading.h>
|
||||
#include <windows.system.threading.core.h>
|
||||
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<LPCWSTR>(eventName.utf16()), 0, EVENT_ALL_ACCESS);
|
||||
|
||||
hr = notifierFactory->AttachToEvent(
|
||||
HStringReference(reinterpret_cast<LPCWSTR>(eventName.utf16())).Get(),
|
||||
Callback<ISignalHandler>(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<IPreallocatedWorkItem> workItem;
|
||||
HRESULT hr = workItemFactory->CreateWorkItemWithPriority(
|
||||
Callback<IWorkItemHandler>(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<ISignalNotifier> notifier;
|
||||
ComPtr<ISignalNotifierStatics> notifierFactory;
|
||||
ComPtr<IPreallocatedWorkItemFactory> workItemFactory;
|
||||
|
||||
QList<QThreadPrivate *> 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<Qt::HANDLE>(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<Qt::HANDLE>(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<void **>(&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<Qt::HANDLE>(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<IAsyncInfo> 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
|
@ -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
|
||||
|
@ -67,6 +67,7 @@ extern "C" {
|
||||
#include <qvector.h>
|
||||
#include <qdir.h>
|
||||
#include <qstandardpaths.h>
|
||||
#include <qthread.h>
|
||||
|
||||
#include <wrl.h>
|
||||
#include <Windows.ApplicationModel.core.h>
|
||||
@ -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;
|
||||
|
@ -53,13 +53,11 @@
|
||||
#ifdef Q_OS_UNIX
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
#if defined(Q_OS_WINCE)
|
||||
#if defined(Q_OS_WIN)
|
||||
#include <windows.h>
|
||||
#elif defined(Q_OS_WINRT)
|
||||
#include <thread>
|
||||
#elif defined(Q_OS_WIN)
|
||||
#if defined(Q_OS_WIN32)
|
||||
#include <process.h>
|
||||
#include <windows.h>
|
||||
#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)
|
||||
|
Loading…
Reference in New Issue
Block a user