#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
#if defined(Q_OS_LINUX) && !defined(SCHED_IDLE)
// from linux/sched.h
# define SCHED_IDLE 5
#endif
QT_BEGIN_NAMESPACE
#ifndef QT_NO_THREAD
enum { ThreadPriorityResetFlag = 0x80000000 };
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);
}
static void destroy_current_thread_data_key()
{
pthread_key_delete(current_thread_data_key);
}
Q_DESTRUCTOR_FUNCTION(destroy_current_thread_data_key)
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);
// do we need to reset the thread priority?
if (int(thr->d_func()->priority) & ThreadPriorityResetFlag) {
thr->setPriority(QThread::Priority(thr->d_func()->priority & ~ThreadPriorityResetFlag));
}
#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