/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (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 Technology Preview License Agreement accompanying
** this package.
**
** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@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
#ifdef Q_OS_VXWORKS
# if (_WRS_VXWORKS_MAJOR > 6) || ((_WRS_VXWORKS_MAJOR == 6) && (_WRS_VXWORKS_MINOR >= 6))
# include <vxCpuLib.h>
# include <cpuset.h>
# define QT_VXWORKS_HAS_CPUSET
# endif
#endif
#ifdef Q_OS_HPUX
#include <sys/pstat.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)
{
#if defined(Q_OS_VXWORKS)
// Calling setspecific(..., 0) sets the value to 0 for ALL threads.
// The 'set to 1' workaround adds a bit of an overhead though,
// since this function is called twice now.
if (p == (void *)1)
return;
#endif
// 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,
#if defined(Q_OS_VXWORKS)
(void *)1);
#else
0);
#endif
}
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);
QT_TRY {
data->thread = new QAdoptedThread(data);
} QT_CATCH(...) {
pthread_setspecific(current_thread_data_key, 0);
data->deref();
data = 0;
QT_RETHROW;
}
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)
{
// Symbian Open C supports neither thread cancellation nor cleanup_push.
#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(¤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
createEventDispatcher(data);
emit thr->started();
#ifndef Q_OS_SYMBIAN
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
pthread_testcancel();
#endif
thr->run();
#ifdef Q_OS_SYMBIAN
QThreadPrivate::finish(arg);
#else
pthread_cleanup_pop(1);
#endif
return 0;
}
#ifdef Q_OS_SYMBIAN
void QThreadPrivate::finish(void *arg, bool lockAnyway, bool closeNativeHandle)
#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
if (closeNativeHandle)
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)
// as of aug 2008 Integrity only supports one single core CPU
cores = 1;
#elif defined(Q_OS_SYMBIAN)
// ### TODO - Get the number of cores from HAL? when multicore architectures (SMP) are supported
cores = 1;
#elif defined(Q_OS_VXWORKS)
// VxWorks
# if defined(QT_VXWORKS_HAS_CPUSET)
cpuset_t cpus = vxCpuEnabledGet();
cores = 0;
// 128 cores should be enough for everyone ;)
for (int i = 0; i < 128 && !CPUSET_ISZERO(cpus); ++i) {
if (CPUSET_ISSET(cpus, i)) {
CPUSET_CLR(cpus, i);
cores++;
}
}
# else
// as of aug 2008 VxWorks < 6.6 only supports one single core CPU
cores = 1;
# endif
#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
#ifdef Q_OS_SYMBIAN
if (d->stackSize == 0)
// The default stack size on Symbian is very small, making even basic
// operations like file I/O fail, so we increase it by default.
d->stackSize = 0x14000; // Maximum stack size on Symbian.
#endif
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->terminated = true;
// "false, false" meaning:
// 1. lockAnyway = false. Don't lock the mutex because it's already locked
// (see above).
// 2. closeNativeSymbianHandle = false. We don't want to close the thread handle,
// because we need it here to terminate the thread.
QThreadPrivate::finish(this, false, false);
d->data->symbian_thread_handle.Terminate(KErrNone);
d->data->symbian_thread_handle.Close();
#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
Q_UNUSED(thr)
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;
// "false" meaning:
// - lockAnyway = false. Don't lock the mutex because it's already locked
// (see above).
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, ¶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