/**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL21$ ** 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 The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** As a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $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(); threadData->thread->d_func()->createEventDispatcher(threadData); winmainThread = false; } } return threadData; } void QAdoptedThread::init() { Q_D(QThread); d->handle = Q_NULLPTR; d->id = 0; } /************************************************************************** ** 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