summaryrefslogtreecommitdiff
path: root/src/corelib/thread/qthread_unix.cpp
diff options
context:
space:
mode:
authoraxis <qt-info@nokia.com>2009-04-24 13:34:15 +0200
committeraxis <qt-info@nokia.com>2009-04-24 13:34:15 +0200
commit8f427b2b914d5b575a4a7c0ed65d2fb8f45acc76 (patch)
treea17e1a767a89542ab59907462206d7dcf2e504b2 /src/corelib/thread/qthread_unix.cpp
downloadqt4-tools-8f427b2b914d5b575a4a7c0ed65d2fb8f45acc76.tar.gz
Long live Qt for S60!
Diffstat (limited to 'src/corelib/thread/qthread_unix.cpp')
-rw-r--r--src/corelib/thread/qthread_unix.cpp647
1 files changed, 647 insertions, 0 deletions
diff --git a/src/corelib/thread/qthread_unix.cpp b/src/corelib/thread/qthread_unix.cpp
new file mode 100644
index 0000000000..0e1769aa6c
--- /dev/null
+++ b/src/corelib/thread/qthread_unix.cpp
@@ -0,0 +1,647 @@
+/****************************************************************************
+**
+** 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
+
+#ifdef Q_OS_SYMBIAN
+#include <private/qeventdispatcher_symbian_p.h>
+#else
+#include <private/qeventdispatcher_unix_p.h>
+#endif
+
+#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(&current_thread_data_key, destroy_current_thread_data);
+}
+
+QThreadData *QThreadData::current()
+{
+ pthread_once(&current_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()
+{
+ Q_D(QThread);
+ d->thread_id = pthread_self();
+#ifdef Q_OS_SYMBIAN
+ d->data->symbian_thread_handle = RThread();
+ TThreadId threadId = d->data->symbian_thread_handle.Id();
+ d->data->symbian_thread_handle.Open(threadId);
+#endif
+}
+
+/*
+ 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
+#ifdef Q_OS_SYMBIAN
+ data->eventDispatcher = new QEventDispatcherSymbian;
+#else
+ data->eventDispatcher = new QEventDispatcherUNIX;
+#endif
+ data->eventDispatcher->startingUp();
+}
+
+#ifndef QT_NO_THREAD
+
+void *QThreadPrivate::start(void *arg)
+{
+#ifndef Q_OS_SYMBIAN
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+ pthread_cleanup_push(QThreadPrivate::finish, arg);
+#endif
+
+ QThread *thr = reinterpret_cast<QThread *>(arg);
+ QThreadData *data = QThreadData::get2(thr);
+
+#ifdef Q_OS_SYMBIAN
+ // Because Symbian Open C does not provide a way to convert between
+ // RThread and pthread_t, we must delay initialization of the RThread
+ // handle when creating a thread, until we are running in the new thread.
+ // Here, we pick up the current thread and assign that to the handle.
+ data->symbian_thread_handle = RThread();
+ TThreadId threadId = data->symbian_thread_handle.Id();
+ data->symbian_thread_handle.Open(threadId);
+#endif
+
+ pthread_once(&current_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();
+#ifndef Q_OS_SYMBIAN
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+ pthread_testcancel();
+#endif
+ thr->run();
+
+#ifndef Q_OS_SYMBIAN
+ pthread_cleanup_pop(1);
+#else
+ QThreadPrivate::finish(arg);
+#endif
+
+ return 0;
+}
+
+#ifdef Q_OS_SYMBIAN
+void QThreadPrivate::finish(void *arg, bool lockAnyway)
+#else
+void QThreadPrivate::finish(void *arg)
+#endif
+{
+ QThread *thr = reinterpret_cast<QThread *>(arg);
+ QThreadPrivate *d = thr->d_func();
+#ifdef Q_OS_SYMBIAN
+ if (lockAnyway)
+#endif
+ 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;
+ }
+
+ void *data = &d->data->tls;
+ QThreadStorageData::finish((void **)data);
+
+ d->thread_id = 0;
+#ifdef Q_OS_SYMBIAN
+ d->data->symbian_thread_handle.Close();
+#endif
+ d->thread_done.wakeAll();
+#ifdef Q_OS_SYMBIAN
+ if (lockAnyway)
+#endif
+ d->mutex.unlock();
+}
+
+
+
+
+/**************************************************************************
+ ** 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?
+#elif defined(Q_OS_SYMBIAN)
+ // ### TODO - Get the number of cores from HAL? when multicore architectures (SMP) are supported
+ cores = 1;
+#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(Q_OS_SYMBIAN) && defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && (_POSIX_THREAD_PRIORITY_SCHEDULING-0 >= 0)
+// ### Need to implement thread sheduling and priorities for symbian os. Implementation removed for now
+ 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
+#ifndef Q_OS_SYMBIAN
+ pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED);
+#endif
+ 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;
+#ifdef Q_OS_SYMBIAN
+ d->data->symbian_thread_handle.Close();
+#endif
+ }
+}
+
+void QThread::terminate()
+{
+ Q_D(QThread);
+ QMutexLocker locker(&d->mutex);
+
+ if (!d->thread_id)
+ return;
+
+#ifndef Q_OS_SYMBIAN
+ 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;
+ }
+#else
+ if (!d->running)
+ return;
+ if (!d->terminationEnabled) {
+ d->terminatePending = true;
+ return;
+ }
+
+ d->data->symbian_thread_handle.Terminate(KErrNone);
+
+ d->terminated = true;
+#endif
+
+
+}
+
+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)
+{
+ QThread *thr = currentThread();
+ Q_ASSERT_X(thr != 0, "QThread::setTerminationEnabled()",
+ "Current thread was not started with QThread.");
+#ifndef Q_OS_SYMBIAN
+ pthread_setcancelstate(enabled ? PTHREAD_CANCEL_ENABLE : PTHREAD_CANCEL_DISABLE, NULL);
+ if (enabled)
+ pthread_testcancel();
+#else
+ 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!
+ pthread_exit(NULL);
+ }
+#endif
+}
+
+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, &param) != 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, &param);
+#endif
+}
+
+#endif // QT_NO_THREAD
+
+QT_END_NAMESPACE
+