diff options
Diffstat (limited to 'src/corelib/thread/qthread_unix.cpp')
-rw-r--r-- | src/corelib/thread/qthread_unix.cpp | 562 |
1 files changed, 562 insertions, 0 deletions
diff --git a/src/corelib/thread/qthread_unix.cpp b/src/corelib/thread/qthread_unix.cpp new file mode 100644 index 0000000..f602821 --- /dev/null +++ b/src/corelib/thread/qthread_unix.cpp @@ -0,0 +1,562 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#include "qthread.h" + +#include "qplatformdefs.h" + +#include <private/qcoreapplication_p.h> +#if !defined(QT_NO_GLIB) +# include "../kernel/qeventdispatcher_glib_p.h" +#endif +#include <private/qeventdispatcher_unix_p.h> + +#include "qthreadstorage.h" + +#include "qthread_p.h" + +#include "qdebug.h" + +#include <sched.h> +#include <errno.h> + +#ifdef Q_OS_BSD4 +#include <sys/sysctl.h> +#endif + +#if defined(Q_OS_MAC) +# ifdef qDebug +# define old_qDebug qDebug +# undef qDebug +# endif +# include <CoreServices/CoreServices.h> + +# ifdef old_qDebug +# undef qDebug +# define qDebug QT_NO_QDEBUG_MACRO +# undef old_qDebug +# endif +#endif + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_THREAD + +static pthread_once_t current_thread_data_once = PTHREAD_ONCE_INIT; +static pthread_key_t current_thread_data_key; + +static void destroy_current_thread_data(void *p) +{ + // POSIX says the value in our key is set to zero before calling + // this destructor function, so we need to set it back to the + // right value... + pthread_setspecific(current_thread_data_key, p); + reinterpret_cast<QThreadData *>(p)->deref(); + // ... but we must reset it to zero before returning so we aren't + // called again (POSIX allows implementations to call destructor + // functions repeatedly until all values are zero) + pthread_setspecific(current_thread_data_key, 0); +} + +static void create_current_thread_data_key() +{ + pthread_key_create(¤t_thread_data_key, destroy_current_thread_data); +} + +QThreadData *QThreadData::current() +{ + pthread_once(¤t_thread_data_once, create_current_thread_data_key); + + QThreadData *data = reinterpret_cast<QThreadData *>(pthread_getspecific(current_thread_data_key)); + if (!data) { + void *a; + if (QInternal::activateCallbacks(QInternal::AdoptCurrentThread, &a)) { + QThread *adopted = static_cast<QThread*>(a); + Q_ASSERT(adopted); + data = QThreadData::get2(adopted); + pthread_setspecific(current_thread_data_key, data); + adopted->d_func()->running = true; + adopted->d_func()->finished = false; + static_cast<QAdoptedThread *>(adopted)->init(); + } else { + data = new QThreadData; + pthread_setspecific(current_thread_data_key, data); + data->thread = new QAdoptedThread(data); + data->deref(); + } + if (!QCoreApplicationPrivate::theMainThread) + QCoreApplicationPrivate::theMainThread = data->thread; + } + return data; +} + + +void QAdoptedThread::init() +{ + d_func()->thread_id = pthread_self(); +} + +/* + QThreadPrivate +*/ + +#if defined(Q_C_CALLBACKS) +extern "C" { +#endif + +typedef void*(*QtThreadCallback)(void*); + +#if defined(Q_C_CALLBACKS) +} +#endif + +#endif // QT_NO_THREAD + +void QThreadPrivate::createEventDispatcher(QThreadData *data) +{ +#if !defined(QT_NO_GLIB) + if (qgetenv("QT_NO_GLIB").isEmpty() + && qgetenv("QT_NO_THREADED_GLIB").isEmpty() + && QEventDispatcherGlib::versionSupported()) + data->eventDispatcher = new QEventDispatcherGlib; + else +#endif + data->eventDispatcher = new QEventDispatcherUNIX; + data->eventDispatcher->startingUp(); +} + +#ifndef QT_NO_THREAD + +void *QThreadPrivate::start(void *arg) +{ + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + pthread_cleanup_push(QThreadPrivate::finish, arg); + + QThread *thr = reinterpret_cast<QThread *>(arg); + QThreadData *data = QThreadData::get2(thr); + + pthread_once(¤t_thread_data_once, create_current_thread_data_key); + pthread_setspecific(current_thread_data_key, data); + + data->ref(); + data->quitNow = false; + + // ### TODO: allow the user to create a custom event dispatcher + if (QCoreApplication::instance()) + createEventDispatcher(data); + + emit thr->started(); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_testcancel(); + thr->run(); + + pthread_cleanup_pop(1); + return 0; +} + +void QThreadPrivate::finish(void *arg) +{ + QThread *thr = reinterpret_cast<QThread *>(arg); + QThreadPrivate *d = thr->d_func(); + QMutexLocker locker(&d->mutex); + + 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; + } + + void *data = &d->data->tls; + QThreadStorageData::finish((void **)data); + + d->thread_id = 0; + d->thread_done.wakeAll(); +} + + + + +/************************************************************************** + ** QThread + *************************************************************************/ + +Qt::HANDLE QThread::currentThreadId() +{ + // requires a C cast here otherwise we run into trouble on AIX + return (Qt::HANDLE)pthread_self(); +} + +#if defined(QT_LINUXBASE) && !defined(_SC_NPROCESSORS_ONLN) +// LSB doesn't define _SC_NPROCESSORS_ONLN. +# define _SC_NPROCESSORS_ONLN 84 +#endif + +int QThread::idealThreadCount() +{ + int cores = -1; + +#if defined(Q_OS_MAC) + // Mac OS X + cores = MPProcessorsScheduled(); +#elif defined(Q_OS_HPUX) + // HP-UX + struct pst_dynamic psd; + if (pstat_getdynamic(&psd, sizeof(psd), 1, 0) == -1) { + perror("pstat_getdynamic"); + cores = -1; + } else { + cores = (int)psd.psd_proc_cnt; + } +#elif defined(Q_OS_BSD4) + // FreeBSD, OpenBSD, NetBSD, BSD/OS + size_t len = sizeof(cores); + int mib[2]; + mib[0] = CTL_HW; + mib[1] = HW_NCPU; + if (sysctl(mib, 2, &cores, &len, NULL, 0) != 0) { + perror("sysctl"); + cores = -1; + } +#elif defined(Q_OS_IRIX) + // IRIX + cores = (int)sysconf(_SC_NPROC_ONLN); +#elif defined(Q_OS_INTEGRITY) + // ### TODO - how to get the amound of CPUs on INTEGRITY? +#else + // the rest: Linux, Solaris, AIX, Tru64 + cores = (int)sysconf(_SC_NPROCESSORS_ONLN); +#endif + + return cores; +} + +void QThread::yieldCurrentThread() +{ + sched_yield(); +} + +/* \internal + helper function to do thread sleeps, since usleep()/nanosleep() + aren't reliable enough (in terms of behavior and availability) +*/ +static void thread_sleep(struct timespec *ti) +{ + pthread_mutex_t mtx; + pthread_cond_t cnd; + + pthread_mutex_init(&mtx, 0); + pthread_cond_init(&cnd, 0); + + pthread_mutex_lock(&mtx); + (void) pthread_cond_timedwait(&cnd, &mtx, ti); + pthread_mutex_unlock(&mtx); + + pthread_cond_destroy(&cnd); + pthread_mutex_destroy(&mtx); +} + +void QThread::sleep(unsigned long secs) +{ + struct timeval tv; + gettimeofday(&tv, 0); + struct timespec ti; + ti.tv_sec = tv.tv_sec + secs; + ti.tv_nsec = (tv.tv_usec * 1000); + thread_sleep(&ti); +} + +void QThread::msleep(unsigned long msecs) +{ + struct timeval tv; + gettimeofday(&tv, 0); + struct timespec ti; + + ti.tv_nsec = (tv.tv_usec + (msecs % 1000) * 1000) * 1000; + ti.tv_sec = tv.tv_sec + (msecs / 1000) + (ti.tv_nsec / 1000000000); + ti.tv_nsec %= 1000000000; + thread_sleep(&ti); +} + +void QThread::usleep(unsigned long usecs) +{ + struct timeval tv; + gettimeofday(&tv, 0); + struct timespec ti; + + ti.tv_nsec = (tv.tv_usec + (usecs % 1000000)) * 1000; + ti.tv_sec = tv.tv_sec + (usecs / 1000000) + (ti.tv_nsec / 1000000000); + ti.tv_nsec %= 1000000000; + thread_sleep(&ti); +} + +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; + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + d->priority = priority; + +#if defined(Q_OS_DARWIN) || !defined(Q_OS_OPENBSD) && defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && (_POSIX_THREAD_PRIORITY_SCHEDULING-0 >= 0) + switch (priority) { + case InheritPriority: + { + pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED); + break; + } + + default: + { + int sched_policy; + if (pthread_attr_getschedpolicy(&attr, &sched_policy) != 0) { + // failed to get the scheduling policy, don't bother + // setting the priority + qWarning("QThread::start: Cannot determine default scheduler policy"); + break; + } + + int prio_min = sched_get_priority_min(sched_policy); + int prio_max = sched_get_priority_max(sched_policy); + if (prio_min == -1 || prio_max == -1) { + // failed to get the scheduling parameters, don't + // bother setting the priority + qWarning("QThread::start: Cannot determine scheduler priority range"); + break; + } + + int prio; + switch (priority) { + case IdlePriority: + prio = prio_min; + break; + + case TimeCriticalPriority: + prio = prio_max; + break; + + default: + // crudely scale our priority enum values to the prio_min/prio_max + prio = (priority * (prio_max - prio_min) / TimeCriticalPriority) + prio_min; + prio = qMax(prio_min, qMin(prio_max, prio)); + break; + } + + sched_param sp; + sp.sched_priority = prio; + + if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED) != 0 + || pthread_attr_setschedpolicy(&attr, sched_policy) != 0 + || pthread_attr_setschedparam(&attr, &sp) != 0) { + // could not set scheduling hints, fallback to inheriting them + pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED); + } + break; + } + } +#endif // _POSIX_THREAD_PRIORITY_SCHEDULING + + if (d->stackSize > 0) { +#if defined(_POSIX_THREAD_ATTR_STACKSIZE) && (_POSIX_THREAD_ATTR_STACKSIZE-0 > 0) + int code = pthread_attr_setstacksize(&attr, d->stackSize); +#else + int code = ENOSYS; // stack size not supported, automatically fail +#endif // _POSIX_THREAD_ATTR_STACKSIZE + + if (code) { + qWarning("QThread::start: Thread stack size error: %s", + qPrintable(qt_error_string(code))); + + // we failed to set the stacksize, and as the documentation states, + // the thread will fail to run... + d->running = false; + d->finished = false; + return; + } + } + + int code = + pthread_create(&d->thread_id, &attr, QThreadPrivate::start, this); + if (code == EPERM) { + // caller does not have permission to set the scheduling + // parameters/policy + pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED); + code = + pthread_create(&d->thread_id, &attr, QThreadPrivate::start, this); + } + + pthread_attr_destroy(&attr); + + if (code) { + qWarning("QThread::start: Thread creation error: %s", qPrintable(qt_error_string(code))); + + d->running = false; + d->finished = false; + d->thread_id = 0; + } +} + +void QThread::terminate() +{ + Q_D(QThread); + QMutexLocker locker(&d->mutex); + + if (!d->thread_id) + return; + + int code = pthread_cancel(d->thread_id); + if (code) { + qWarning("QThread::start: Thread termination error: %s", + qPrintable(qt_error_string((code)))); + } else { + d->terminated = true; + } +} + +bool QThread::wait(unsigned long time) +{ + Q_D(QThread); + QMutexLocker locker(&d->mutex); + + if (d->thread_id == pthread_self()) { + qWarning("QThread::wait: Thread tried to wait on itself"); + return false; + } + + if (d->finished || !d->running) + return true; + + while (d->running) { + if (!d->thread_done.wait(locker.mutex(), time)) + return false; + } + return true; +} + +void QThread::setTerminationEnabled(bool enabled) +{ + Q_ASSERT_X(currentThread() != 0, "QThread::setTerminationEnabled()", + "Current thread was not started with QThread."); + pthread_setcancelstate(enabled ? PTHREAD_CANCEL_ENABLE : PTHREAD_CANCEL_DISABLE, NULL); + if (enabled) + pthread_testcancel(); +} + +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; + } + + d->priority = priority; + + // copied from start() with a few modifications: + +#if defined(Q_OS_DARWIN) || !defined(Q_OS_OPENBSD) && defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && (_POSIX_THREAD_PRIORITY_SCHEDULING-0 >= 0) + int sched_policy; + sched_param param; + + if (pthread_getschedparam(d->thread_id, &sched_policy, ¶m) != 0) { + // failed to get the scheduling policy, don't bother setting + // the priority + qWarning("QThread::setPriority: Cannot get scheduler parameters"); + return; + } + + int prio_min = sched_get_priority_min(sched_policy); + int prio_max = sched_get_priority_max(sched_policy); + if (prio_min == -1 || prio_max == -1) { + // failed to get the scheduling parameters, don't + // bother setting the priority + qWarning("QThread::setPriority: Cannot determine scheduler priority range"); + return; + } + + int prio; + switch (priority) { + case InheritPriority: + qWarning("QThread::setPriority: Argument cannot be InheritPriority"); + return; + + case IdlePriority: + prio = prio_min; + break; + + case TimeCriticalPriority: + prio = prio_max; + break; + + default: + // crudely scale our priority enum values to the prio_min/prio_max + prio = (priority * (prio_max - prio_min) / TimeCriticalPriority) + prio_min; + prio = qMax(prio_min, qMin(prio_max, prio)); + break; + } + + param.sched_priority = prio; + pthread_setschedparam(d->thread_id, sched_policy, ¶m); +#endif +} + +#endif // QT_NO_THREAD + +QT_END_NAMESPACE + |