diff options
Diffstat (limited to 'src/corelib/thread/qthread_win.cpp')
-rw-r--r-- | src/corelib/thread/qthread_win.cpp | 620 |
1 files changed, 620 insertions, 0 deletions
diff --git a/src/corelib/thread/qthread_win.cpp b/src/corelib/thread/qthread_win.cpp new file mode 100644 index 0000000..27193c6 --- /dev/null +++ b/src/corelib/thread/qthread_win.cpp @@ -0,0 +1,620 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//#define WINVER 0x0500 +#define _WIN32_WINNT 0x0400 + + +#include "qthread.h" +#include "qthread_p.h" +#include "qthreadstorage.h" +#include "qmutex.h" + +#include <qcoreapplication.h> +#include <qpointer.h> + +#include <private/qcoreapplication_p.h> +#include <private/qeventdispatcher_win_p.h> + +#include <windows.h> + + +#ifndef Q_OS_WINCE +#ifndef _MT +#define _MT +#endif +#include <process.h> +#else +#include "qfunctions_wince.h" +#endif + +#ifndef QT_NO_THREAD +QT_BEGIN_NAMESPACE + +void qt_watch_adopted_thread(const HANDLE adoptedThreadHandle, QThread *qthread); +void qt_adopted_thread_watcher_function(void *); + +static DWORD qt_current_thread_data_tls_index = TLS_OUT_OF_INDEXES; +void qt_create_tls() +{ + if (qt_current_thread_data_tls_index != TLS_OUT_OF_INDEXES) + return; + static QMutex mutex; + QMutexLocker locker(&mutex); + qt_current_thread_data_tls_index = TlsAlloc(); +} + +static void qt_free_tls() +{ + if (qt_current_thread_data_tls_index != TLS_OUT_OF_INDEXES) { + TlsFree(qt_current_thread_data_tls_index); + qt_current_thread_data_tls_index = TLS_OUT_OF_INDEXES; + } +} +Q_DESTRUCTOR_FUNCTION(qt_free_tls) + +/* + QThreadData +*/ +QThreadData *QThreadData::current() +{ + qt_create_tls(); + QThreadData *threadData = reinterpret_cast<QThreadData *>(TlsGetValue(qt_current_thread_data_tls_index)); + if (!threadData) { + QThread *adopted = 0; + if (QInternal::activateCallbacks(QInternal::AdoptCurrentThread, (void **) &adopted)) { + Q_ASSERT(adopted); + threadData = QThreadData::get2(adopted); + TlsSetValue(qt_current_thread_data_tls_index, threadData); + adopted->d_func()->running = true; + adopted->d_func()->finished = false; + static_cast<QAdoptedThread *>(adopted)->init(); + } else { + threadData = new QThreadData; + // This needs to be called prior to new AdoptedThread() to + // avoid recursion. + TlsSetValue(qt_current_thread_data_tls_index, threadData); + threadData->thread = new QAdoptedThread(threadData); + threadData->deref(); + } + + if (!QCoreApplicationPrivate::theMainThread) { + QCoreApplicationPrivate::theMainThread = threadData->thread; + } else { + HANDLE realHandle = INVALID_HANDLE_VALUE; +#if !defined(Q_OS_WINCE) || (defined(_WIN32_WCE) && (_WIN32_WCE>=0x600)) + DuplicateHandle(GetCurrentProcess(), + GetCurrentThread(), + GetCurrentProcess(), + &realHandle, + 0, + FALSE, + DUPLICATE_SAME_ACCESS); +#else + realHandle = (HANDLE)GetCurrentThreadId(); +#endif + qt_watch_adopted_thread(realHandle, threadData->thread); + } + } + return threadData; +} + +void QAdoptedThread::init() +{ + d_func()->handle = GetCurrentThread(); + d_func()->id = GetCurrentThreadId(); +} + +static QVector<HANDLE> qt_adopted_thread_handles; +static QVector<QThread *> qt_adopted_qthreads; +static QMutex qt_adopted_thread_watcher_mutex; +static HANDLE qt_adopted_thread_watcher_handle = 0; +static HANDLE qt_adopted_thread_wakeup = 0; + +/*! \internal + Adds an adopted thread to the list of threads that Qt watches to make sure + the thread data is properly cleaned up. This function starts the watcher + thread if necessary. +*/ +void qt_watch_adopted_thread(const HANDLE adoptedThreadHandle, QThread *qthread) +{ + QMutexLocker lock(&qt_adopted_thread_watcher_mutex); + qt_adopted_thread_handles.append(adoptedThreadHandle); + qt_adopted_qthreads.append(qthread); + + // Start watcher thread if it is not already running. + if (qt_adopted_thread_watcher_handle == 0) { + if (qt_adopted_thread_wakeup == 0) { + qt_adopted_thread_wakeup = QT_WA_INLINE(CreateEventW(0, false, false, 0), + CreateEventA(0, false, false, 0)); + qt_adopted_thread_handles.prepend(qt_adopted_thread_wakeup); + } + + qt_adopted_thread_watcher_handle = + (HANDLE)_beginthread(qt_adopted_thread_watcher_function, 0, NULL); + } else { + SetEvent(qt_adopted_thread_wakeup); + } +} + +/*! \internal + This function loops and waits for native adopted threads to finish. + When this happens it derefs the QThreadData for the adopted thread + to make sure it gets cleaned up properly. +*/ +void qt_adopted_thread_watcher_function(void *) +{ + forever { + qt_adopted_thread_watcher_mutex.lock(); + + if (qt_adopted_thread_handles.count() == 1) { + qt_adopted_thread_watcher_handle = 0; + qt_adopted_thread_watcher_mutex.unlock(); + break; + } + + QVector<HANDLE> handlesCopy = qt_adopted_thread_handles; + qt_adopted_thread_watcher_mutex.unlock(); + + DWORD ret = WAIT_TIMEOUT; + int loops = (handlesCopy.count() / MAXIMUM_WAIT_OBJECTS) + 1, offset, count; + if (loops == 1) { + // no need to loop, no timeout + offset = 0; + count = handlesCopy.count(); + ret = WaitForMultipleObjects(handlesCopy.count(), handlesCopy.constData(), false, INFINITE); + } else { + int loop = 0; + do { + offset = loop * MAXIMUM_WAIT_OBJECTS; + count = qMin(handlesCopy.count() - offset, MAXIMUM_WAIT_OBJECTS); + ret = WaitForMultipleObjects(count, handlesCopy.constData() + offset, false, 100); + loop = (loop + 1) % loops; + } while (ret == WAIT_TIMEOUT); + } + + if (ret == WAIT_FAILED || !(ret >= WAIT_OBJECT_0 && ret < WAIT_OBJECT_0 + uint(count))) { + qWarning("QThread internal error while waiting for adopted threads: %d", int(GetLastError())); + continue; + } + + const int handleIndex = offset + ret - WAIT_OBJECT_0; + if (handleIndex == 0){ + // New handle to watch was added. + continue; + } else { +// printf("(qt) - qt_adopted_thread_watcher_function... called\n"); + const int qthreadIndex = handleIndex - 1; + QThreadData::get2(qt_adopted_qthreads.at(qthreadIndex))->deref(); +#if !defined(Q_OS_WINCE) || (defined(_WIN32_WCE) && (_WIN32_WCE>=0x600)) + CloseHandle(qt_adopted_thread_handles.at(handleIndex)); +#endif + QMutexLocker lock(&qt_adopted_thread_watcher_mutex); + qt_adopted_thread_handles.remove(handleIndex); + qt_adopted_qthreads.remove(qthreadIndex); + } + } +} + +#if !defined(QT_NO_DEBUG) && defined(Q_CC_MSVC) && !defined(Q_OS_WINCE) + +#ifndef Q_OS_WIN64 +# define ULONG_PTR DWORD +#endif + +typedef struct tagTHREADNAME_INFO +{ + DWORD dwType; // must be 0x1000 + LPCSTR szName; // pointer to name (in user addr space) + HANDLE dwThreadID; // thread ID (-1=caller thread) + DWORD dwFlags; // reserved for future use, must be zero +} THREADNAME_INFO; + +void qt_set_thread_name(HANDLE threadId, LPCSTR threadName) +{ + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = threadName; + info.dwThreadID = threadId; + info.dwFlags = 0; + + __try + { + RaiseException(0x406D1388, 0, sizeof(info)/sizeof(DWORD), (const ULONG_PTR*)&info); + } + __except (EXCEPTION_CONTINUE_EXECUTION) + { + } +} +#endif // !QT_NO_DEBUG && Q_CC_MSVC && !Q_OS_WINCE + +/************************************************************************** + ** QThreadPrivate + *************************************************************************/ + +#endif // QT_NO_THREAD + +void QThreadPrivate::createEventDispatcher(QThreadData *data) +{ + data->eventDispatcher = new QEventDispatcherWin32; + data->eventDispatcher->startingUp(); +} + +#ifndef QT_NO_THREAD + +unsigned int __stdcall QThreadPrivate::start(void *arg) +{ + QThread *thr = reinterpret_cast<QThread *>(arg); + QThreadData *data = QThreadData::get2(thr); + + qt_create_tls(); + TlsSetValue(qt_current_thread_data_tls_index, data); + + QThread::setTerminationEnabled(false); + + data->quitNow = false; + // ### TODO: allow the user to create a custom event dispatcher + if (QCoreApplication::instance()) + createEventDispatcher(data); + +#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, + objectName.isEmpty() ? + thr->metaObject()->className() : objectName.constData()); +#endif + + emit thr->started(); + QThread::setTerminationEnabled(true); + thr->run(); + + finish(arg); + return 0; +} + +void QThreadPrivate::finish(void *arg, bool lockAnyway) +{ + QThread *thr = reinterpret_cast<QThread *>(arg); + QThreadPrivate *d = thr->d_func(); + + if (lockAnyway) + d->mutex.lock(); + d->priority = QThread::InheritPriority; + d->running = false; + d->finished = true; + if (d->terminated) + emit thr->terminated(); + d->terminated = false; + emit thr->finished(); + + if (d->data->eventDispatcher) { + d->data->eventDispatcher->closingDown(); + QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher; + d->data->eventDispatcher = 0; + delete eventDispatcher; + } + + QThreadStorageData::finish(reinterpret_cast<void **>(&d->data->tls)); + + if (!d->waiters) { + CloseHandle(d->handle); + d->handle = 0; + } + + d->id = 0; + + if (lockAnyway) + d->mutex.unlock(); +} + +/************************************************************************** + ** QThread + *************************************************************************/ + +Qt::HANDLE QThread::currentThreadId() +{ + return (Qt::HANDLE)GetCurrentThreadId(); +} + +int QThread::idealThreadCount() +{ + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + return sysinfo.dwNumberOfProcessors; +} + +void QThread::yieldCurrentThread() +{ +#ifndef Q_OS_WINCE + if (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based) + SwitchToThread(); + else +#endif + ::Sleep(0); +} + +void QThread::sleep(unsigned long secs) +{ + ::Sleep(secs * 1000); +} + +void QThread::msleep(unsigned long msecs) +{ + ::Sleep(msecs); +} + +void QThread::usleep(unsigned long usecs) +{ + ::Sleep((usecs / 1000) + 1); +} + + +void QThread::start(Priority priority) +{ + Q_D(QThread); + QMutexLocker locker(&d->mutex); + + if (d->running) + return; + + d->running = true; + d->finished = false; + d->terminated = false; + + /* + NOTE: we create the thread in the suspended state, set the + priority and then resume the thread. + + since threads are created with normal priority by default, we + could get into a case where a thread (with priority less than + NormalPriority) tries to create a new thread (also with priority + less than NormalPriority), but the newly created thread preempts + its 'parent' and runs at normal priority. + */ + d->handle = (Qt::HANDLE) _beginthreadex(NULL, d->stackSize, QThreadPrivate::start, + this, CREATE_SUSPENDED, &(d->id)); + + if (!d->handle) { + qErrnoWarning(errno, "QThread::start: Failed to create thread"); + d->running = false; + d->finished = true; + return; + } + + // Since Win 9x will have problems if the priority is idle or time critical + // we have to use the closest one instead + int prio; + d->priority = priority; + switch (d->priority) { + case IdlePriority: + if (QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based) { + prio = THREAD_PRIORITY_LOWEST; + } else { + prio = THREAD_PRIORITY_IDLE; + } + break; + + case LowestPriority: + prio = THREAD_PRIORITY_LOWEST; + break; + + case LowPriority: + prio = THREAD_PRIORITY_BELOW_NORMAL; + break; + + case NormalPriority: + prio = THREAD_PRIORITY_NORMAL; + break; + + case HighPriority: + prio = THREAD_PRIORITY_ABOVE_NORMAL; + break; + + case HighestPriority: + prio = THREAD_PRIORITY_HIGHEST; + break; + + case TimeCriticalPriority: + if (QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based) { + prio = THREAD_PRIORITY_HIGHEST; + } else { + prio = THREAD_PRIORITY_TIME_CRITICAL; + } + break; + + case InheritPriority: + default: + prio = GetThreadPriority(GetCurrentThread()); + break; + } + + if (!SetThreadPriority(d->handle, prio)) { + qErrnoWarning("QThread::start: Failed to set thread priority"); + } + + if (ResumeThread(d->handle) == (DWORD) -1) { + qErrnoWarning("QThread::start: Failed to resume new thread"); + } +} + +void QThread::terminate() +{ + Q_D(QThread); + QMutexLocker locker(&d->mutex); + if (!d->running) + return; + if (!d->terminationEnabled) { + d->terminatePending = true; + return; + } + TerminateThread(d->handle, 0); + d->terminated = true; + QThreadPrivate::finish(this, 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(); + + bool ret = false; + switch (WaitForSingleObject(d->handle, time)) { + case WAIT_OBJECT_0: + ret = true; + break; + case WAIT_FAILED: + qErrnoWarning("QThread::wait: Thread wait failure"); + break; + case WAIT_ABANDONED: + case WAIT_TIMEOUT: + default: + break; + } + + locker.mutex()->lock(); + --d->waiters; + + if (ret && !d->finished) { + // thread was terminated by someone else + d->terminated = true; + QThreadPrivate::finish(this, false); + } + + if (d->finished && !d->waiters) { + CloseHandle(d->handle); + d->handle = 0; + } + + 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->terminated = true; + QThreadPrivate::finish(thr, false); + locker.unlock(); // don't leave the mutex locked! + _endthreadex(0); + } +} + +void QThread::setPriority(Priority priority) +{ + Q_D(QThread); + QMutexLocker locker(&d->mutex); + if (!d->running) { + qWarning("QThread::setPriority: Cannot set priority, thread is not running"); + return; + } + + // copied from start() with a few modifications: + + // Since Win 9x will have problems if the priority is idle or time critical + // we have to use the closest one instead + int prio; + d->priority = priority; + switch (d->priority) { + case IdlePriority: + if (QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based) { + prio = THREAD_PRIORITY_LOWEST; + } else { + prio = THREAD_PRIORITY_IDLE; + } + break; + + case LowestPriority: + prio = THREAD_PRIORITY_LOWEST; + break; + + case LowPriority: + prio = THREAD_PRIORITY_BELOW_NORMAL; + break; + + case NormalPriority: + prio = THREAD_PRIORITY_NORMAL; + break; + + case HighPriority: + prio = THREAD_PRIORITY_ABOVE_NORMAL; + break; + + case HighestPriority: + prio = THREAD_PRIORITY_HIGHEST; + break; + + case TimeCriticalPriority: + if (QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based) { + prio = THREAD_PRIORITY_HIGHEST; + } else { + prio = THREAD_PRIORITY_TIME_CRITICAL; + } + break; + + case InheritPriority: + default: + qWarning("QThread::setPriority: Argument cannot be InheritPriority"); + return; + } + + if (!SetThreadPriority(d->handle, prio)) { + qErrnoWarning("QThread::setPriority: Failed to set thread priority"); + } +} + +QT_END_NAMESPACE +#endif // QT_NO_THREAD |