diff options
author | mread <qt-info@nokia.com> | 2011-02-09 08:18:36 (GMT) |
---|---|---|
committer | mread <qt-info@nokia.com> | 2011-03-09 12:45:07 (GMT) |
commit | 834ad29849ebef99d7e5c516cde26d550abff7cd (patch) | |
tree | c3da2435c2bb23948eaff22c93f7f70fcd2c9aea /src/corelib/thread | |
parent | f351340d328a896829779794e8810f0d913161fb (diff) | |
download | Qt-834ad29849ebef99d7e5c516cde26d550abff7cd.zip Qt-834ad29849ebef99d7e5c516cde26d550abff7cd.tar.gz Qt-834ad29849ebef99d7e5c516cde26d550abff7cd.tar.bz2 |
Symbian native implementation of corelib/thread
QThread, QWaitCondition and QMutex have new Symbian native
implementations. These new implementations are split out into
new _symbian files.
Task-number: QTBUG-13990
Reviewed-by: Shane Kearns
Diffstat (limited to 'src/corelib/thread')
-rw-r--r-- | src/corelib/thread/qmutex_p.h | 13 | ||||
-rw-r--r-- | src/corelib/thread/qmutex_symbian.cpp | 104 | ||||
-rw-r--r-- | src/corelib/thread/qthread_symbian.cpp | 545 | ||||
-rw-r--r-- | src/corelib/thread/qthread_unix.cpp | 145 | ||||
-rw-r--r-- | src/corelib/thread/qwaitcondition_symbian.cpp | 210 | ||||
-rw-r--r-- | src/corelib/thread/qwaitcondition_unix.cpp | 2 | ||||
-rw-r--r-- | src/corelib/thread/thread.pri | 30 |
7 files changed, 891 insertions, 158 deletions
diff --git a/src/corelib/thread/qmutex_p.h b/src/corelib/thread/qmutex_p.h index 1a31c87..789d1f0 100644 --- a/src/corelib/thread/qmutex_p.h +++ b/src/corelib/thread/qmutex_p.h @@ -62,6 +62,11 @@ # include <mach/semaphore.h> #endif +#if defined(Q_OS_SYMBIAN) +# include <e32std.h> +#undef QT_SYMBIAN_USE_RFASTLOCK +#endif + QT_BEGIN_NAMESPACE class QMutexPrivate : public QMutexData { @@ -81,12 +86,18 @@ public: #if defined(Q_OS_MAC) semaphore_t mach_semaphore; -#elif defined(Q_OS_UNIX) && !defined(Q_OS_LINUX) +#elif defined(Q_OS_UNIX) && !defined(Q_OS_LINUX) && !defined(Q_OS_SYMBIAN) volatile bool wakeup; pthread_mutex_t mutex; pthread_cond_t cond; #elif defined(Q_OS_WIN32) || defined(Q_OS_WINCE) HANDLE event; +#elif defined(Q_OS_SYMBIAN) +# ifdef QT_SYMBIAN_USE_RFASTLOCK + RFastLock lock; +# else + RSemaphore lock; +# endif #endif }; diff --git a/src/corelib/thread/qmutex_symbian.cpp b/src/corelib/thread/qmutex_symbian.cpp new file mode 100644 index 0000000..e7487cd --- /dev/null +++ b/src/corelib/thread/qmutex_symbian.cpp @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qplatformdefs.h" +#include "qmutex.h" +#include <qdebug.h> + +#ifndef QT_NO_THREAD +#include "qatomic.h" +#include "qelapsedtimer.h" +#include "qthread.h" +#include "qmutex_p.h" + +QT_BEGIN_NAMESPACE + + +QMutexPrivate::QMutexPrivate(QMutex::RecursionMode mode) + : QMutexData(mode), maximumSpinTime(MaximumSpinTimeThreshold), averageWaitTime(0), owner(0), count(0) +{ +#ifdef QT_SYMBIAN_USE_RFASTLOCK + int r = lock.CreateLocal(); +#else + int r = lock.CreateLocal(0); +#endif + if (r != KErrNone) + qWarning("QMutex: failed to create lock, error %d", r); +} + +QMutexPrivate::~QMutexPrivate() +{ + lock.Close(); +} + +bool QMutexPrivate::wait(int timeout) +{ + if (contenders.fetchAndAddAcquire(1) == 0) { + // lock acquired without waiting + return true; + } + int r = KErrTimedOut; + if (timeout < 0) { + lock.Wait(); + r = KErrNone; + } else { + // Symbian lock waits are specified in microseconds. + // The wait is therefore chunked. + // KErrNone indicates success, KErrGeneral and KErrArgument are real fails, anything else is a timeout + do { + int waitTime = qMin(KMaxTInt / 1000, timeout); + timeout -= waitTime; + r = lock.Wait(waitTime * 1000); + } while (r != KErrNone && r != KErrGeneral && r != KErrArgument && timeout > 0); + } + bool returnValue = (r == KErrNone); + contenders.deref(); + return returnValue; +} + +void QMutexPrivate::wakeUp() +{ + lock.Signal(); +} + +QT_END_NAMESPACE + +#endif // QT_NO_THREAD diff --git a/src/corelib/thread/qthread_symbian.cpp b/src/corelib/thread/qthread_symbian.cpp new file mode 100644 index 0000000..4a7a204 --- /dev/null +++ b/src/corelib/thread/qthread_symbian.cpp @@ -0,0 +1,545 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 + +#include <private/qeventdispatcher_symbian_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 +#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 +#ifndef QT_NO_CORESERVICES +# include <CoreServices/CoreServices.h> +#endif //QT_NO_CORESERVICES + +# 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 + +#if defined(Q_OS_DARWIN) || !defined(Q_OS_OPENBSD) && defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && (_POSIX_THREAD_PRIORITY_SCHEDULING-0 >= 0) +#define QT_HAS_THREAD_PRIORITY_SCHEDULING +#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) +{ + // 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); +} + +static void destroy_current_thread_data_key() +{ + pthread_once(¤t_thread_data_once, create_current_thread_data_key); + pthread_key_delete(current_thread_data_key); +} +Q_DESTRUCTOR_FUNCTION(destroy_current_thread_data_key) + + +// Utility functions for getting, setting and clearing thread specific data. +// In Symbian, TLS access is significantly faster than pthread_getspecific. +// However Symbian does not have the thread destruction cleanup functionality +// that pthread has, so pthread_setspecific is also used. +static QThreadData *get_thread_data() +{ + return reinterpret_cast<QThreadData *>(Dll::Tls()); +} + +static void set_thread_data(QThreadData *data) +{ + qt_symbian_throwIfError(Dll::SetTls(data)); +// pthread_once(¤t_thread_data_once, create_current_thread_data_key); +// pthread_setspecific(current_thread_data_key, data); +} + +static void clear_thread_data() +{ + Dll::FreeTls(); +// pthread_setspecific(current_thread_data_key, 0); +} + + +static void init_symbian_thread_handle(RThread &thread) +{ + thread = RThread(); + TThreadId threadId = thread.Id(); + thread.Open(threadId); + + // Make thread handle accessible process wide + RThread originalCloser = thread; + thread.Duplicate(thread, EOwnerProcess); + originalCloser.Close(); +} + +QThreadData *QThreadData::current() +{ + QThreadData *data = get_thread_data(); + if (!data) { + void *a; + if (QInternal::activateCallbacks(QInternal::AdoptCurrentThread, &a)) { + QThread *adopted = static_cast<QThread*>(a); + Q_ASSERT(adopted); + data = QThreadData::get2(adopted); + set_thread_data(data); + adopted->d_func()->running = true; + adopted->d_func()->finished = false; + static_cast<QAdoptedThread *>(adopted)->init(); + } else { + data = new QThreadData; + QT_TRY { + set_thread_data(data); + data->thread = new QAdoptedThread(data); + } QT_CATCH(...) { + clear_thread_data(); + 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 = RThread().Id(); // type operator to TUint + init_symbian_thread_handle(d->data->symbian_thread_handle); +} + +/* + 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) +{ + data->eventDispatcher = new QEventDispatcherSymbian; + data->eventDispatcher->startingUp(); +} + +#ifndef QT_NO_THREAD + +void *QThreadPrivate::start(void *arg) +{ + 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)); + } + + // 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. + init_symbian_thread_handle(data->symbian_thread_handle); + + // On symbian, threads other than the main thread are non critical by default + // This means a worker thread can crash without crashing the application - to + // use this feature, we would need to use RThread::Logon in the main thread + // to catch abnormal thread exit and emit the finished signal. + // For the sake of cross platform consistency, we set the thread as process critical + // - advanced users who want the symbian behaviour can change the critical + // attribute of the thread again once the app gains control in run() + User::SetCritical(User::EProcessCritical); + + set_thread_data(data); + + data->ref(); + data->quitNow = false; + + // ### TODO: allow the user to create a custom event dispatcher + createEventDispatcher(data); + + emit thr->started(); + thr->run(); + + QThreadPrivate::finish(arg); + + return 0; +} + +void QThreadPrivate::finish(void *arg, bool lockAnyway, bool closeNativeHandle) +{ + QThread *thr = reinterpret_cast<QThread *>(arg); + QThreadPrivate *d = thr->d_func(); + + QMutexLocker locker(lockAnyway ? &d->mutex : 0); + + d->isInFinish = true; + d->priority = QThread::InheritPriority; + bool terminated = d->terminated; + void *data = &d->data->tls; + locker.unlock(); + if (terminated) + emit thr->terminated(); + emit thr->finished(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QThreadStorageData::finish((void **)data); + locker.relock(); + d->terminated = false; + + QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher; + if (eventDispatcher) { + d->data->eventDispatcher = 0; + locker.unlock(); + eventDispatcher->closingDown(); + delete eventDispatcher; + locker.relock(); + } + + d->thread_id = 0; + if (closeNativeHandle) + d->data->symbian_thread_handle.Close(); + d->running = false; + d->finished = true; + + d->isInFinish = false; + d->thread_done.wakeAll(); +} + + + + +/************************************************************************** + ** QThread + *************************************************************************/ + +Qt::HANDLE QThread::currentThreadId() +{ + return (Qt::HANDLE) (TUint) RThread().Id(); +} + +#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; + + // ### TODO - Get the number of cores from HAL? when multicore architectures (SMP) are supported + cores = 1; + + return cores; +} + +void QThread::yieldCurrentThread() +{ + sched_yield(); +} + +/* \internal + helper function to do thread sleeps +*/ +static void thread_sleep(unsigned long remaining, unsigned long scale) +{ + // maximum Symbian wait is 2^31 microseconds + unsigned long maxWait = KMaxTInt / scale; + do { + unsigned long waitTime = qMin(maxWait, remaining); + remaining -= waitTime; + User::AfterHighRes(waitTime * scale); + } while (remaining); +} + +void QThread::sleep(unsigned long secs) +{ + thread_sleep(secs, 1000000ul); +} + +void QThread::msleep(unsigned long msecs) +{ + thread_sleep(msecs, 1000ul); +} + +void QThread::usleep(unsigned long usecs) +{ + thread_sleep(usecs, 1ul); +} + +TThreadPriority calculateSymbianPriority(QThread::Priority priority) + { + // Both Qt & Symbian use limited enums; this matches the mapping previously done through conversion to Posix granularity + TThreadPriority symPriority; + switch (priority) + { + case QThread::IdlePriority: + symPriority = EPriorityMuchLess; + break; + case QThread::LowestPriority: + case QThread::LowPriority: + symPriority = EPriorityLess; + break; + case QThread::NormalPriority: + symPriority = EPriorityNormal; + break; + case QThread::HighPriority: + symPriority = EPriorityMore; + break; + case QThread::HighestPriority: + case QThread::TimeCriticalPriority: + default: + symPriority = EPriorityMuchMore; + break; + } + return symPriority; + } + +void QThread::start(Priority priority) +{ + Q_D(QThread); + QMutexLocker locker(&d->mutex); + + if (d->isInFinish) + d->thread_done.wait(locker.mutex()); + + if (d->running) + return; + + d->running = true; + d->finished = false; + d->terminated = false; + d->returnCode = 0; + d->exited = false; + + d->priority = priority; + + 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. + + int code = 0; + if (d->data->symbian_thread_handle.Create(KNullDesC, (TThreadFunction) QThreadPrivate::start, d->stackSize, NULL, this) == KErrNone) + { + d->thread_id = d->data->symbian_thread_handle.Id(); + TThreadPriority symPriority = calculateSymbianPriority(priority); + d->data->symbian_thread_handle.SetPriority(symPriority); + d->data->symbian_thread_handle.Resume(); + } + else + code = ENOMEM; // probably the problem + + if (code) { + qWarning("QThread::start: Thread creation error: %s", qPrintable(qt_error_string(code))); + + d->running = false; + d->finished = false; + d->thread_id = 0; + d->data->symbian_thread_handle.Close(); + } +} + +void QThread::terminate() +{ + Q_D(QThread); + QMutexLocker locker(&d->mutex); + + if (!d->thread_id) + return; + + 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(); +} + +bool QThread::wait(unsigned long time) +{ + Q_D(QThread); + QMutexLocker locker(&d->mutex); + + if (d->thread_id == (TUint) RThread().Id()) { + qWarning("QThread::wait: Thread tried to wait on itself"); + return false; + } + + if (d->finished || !d->running) + return true; + + while (d->running) { + // Check if thread still exists. Needed because kernel will kill it without notification + // before global statics are deleted at application exit. + if (d->data->symbian_thread_handle.Handle() + && d->data->symbian_thread_handle.ExitType() != EExitPending) { + // Cannot call finish here as wait is typically called from another thread. + // It won't be necessary anyway, as we should never get here under normal operations; + // all QThreads are EProcessCritical and therefore cannot normally exit + // undetected (i.e. panic) as long as all thread control is via QThread. + return true; + } + 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."); + 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! + User::Exit(0); // may be some other cleanup required? what if AS or cleanup stack? + } +} + +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: + TThreadPriority symPriority = calculateSymbianPriority(priority); + d->data->symbian_thread_handle.SetPriority(symPriority); +} + +#endif // QT_NO_THREAD + +QT_END_NAMESPACE + diff --git a/src/corelib/thread/qthread_unix.cpp b/src/corelib/thread/qthread_unix.cpp index 0766447..2f13315 100644 --- a/src/corelib/thread/qthread_unix.cpp +++ b/src/corelib/thread/qthread_unix.cpp @@ -48,11 +48,7 @@ # 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" @@ -152,51 +148,23 @@ Q_DESTRUCTOR_FUNCTION(destroy_current_thread_data_key) // Utility functions for getting, setting and clearing thread specific data. -// In Symbian, TLS access is significantly faster than pthread_getspecific. -// However Symbian does not have the thread destruction cleanup functionality -// that pthread has, so pthread_setspecific is also used. static QThreadData *get_thread_data() { -#ifdef Q_OS_SYMBIAN - return reinterpret_cast<QThreadData *>(Dll::Tls()); -#else pthread_once(¤t_thread_data_once, create_current_thread_data_key); return reinterpret_cast<QThreadData *>(pthread_getspecific(current_thread_data_key)); -#endif } static void set_thread_data(QThreadData *data) { -#ifdef Q_OS_SYMBIAN - qt_symbian_throwIfError(Dll::SetTls(data)); -#endif pthread_once(¤t_thread_data_once, create_current_thread_data_key); pthread_setspecific(current_thread_data_key, data); } static void clear_thread_data() { -#ifdef Q_OS_SYMBIAN - Dll::FreeTls(); -#endif pthread_setspecific(current_thread_data_key, 0); } - -#ifdef Q_OS_SYMBIAN -static void init_symbian_thread_handle(RThread &thread) -{ - thread = RThread(); - TThreadId threadId = thread.Id(); - thread.Open(threadId); - - // Make thread handle accessible process wide - RThread originalCloser = thread; - thread.Duplicate(thread, EOwnerProcess); - originalCloser.Close(); -} -#endif - QThreadData *QThreadData::current() { QThreadData *data = get_thread_data(); @@ -234,9 +202,6 @@ void QAdoptedThread::init() { Q_D(QThread); d->thread_id = pthread_self(); -#ifdef Q_OS_SYMBIAN - init_symbian_thread_handle(d->data->symbian_thread_handle); -#endif } /* @@ -264,11 +229,7 @@ void QThreadPrivate::createEventDispatcher(QThreadData *data) data->eventDispatcher = new QEventDispatcherGlib; else #endif -#ifdef Q_OS_SYMBIAN - data->eventDispatcher = new QEventDispatcherSymbian; -#else - data->eventDispatcher = new QEventDispatcherUNIX; -#endif + data->eventDispatcher = new QEventDispatcherUNIX; data->eventDispatcher->startingUp(); } @@ -276,11 +237,8 @@ void QThreadPrivate::createEventDispatcher(QThreadData *data) 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); @@ -290,23 +248,6 @@ void *QThreadPrivate::start(void *arg) 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. - init_symbian_thread_handle(data->symbian_thread_handle); - - // On symbian, threads other than the main thread are non critical by default - // This means a worker thread can crash without crashing the application - to - // use this feature, we would need to use RThread::Logon in the main thread - // to catch abnormal thread exit and emit the finished signal. - // For the sake of cross platform consistency, we set the thread as process critical - // - advanced users who want the symbian behaviour can change the critical - // attribute of the thread again once the app gains control in run() - User::SetCritical(User::EProcessCritical); -#endif - set_thread_data(data); data->ref(); @@ -316,35 +257,21 @@ void *QThreadPrivate::start(void *arg) 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 - QMutexLocker locker(lockAnyway ? &d->mutex : 0); -#else QMutexLocker locker(&d->mutex); -#endif d->isInFinish = true; d->priority = QThread::InheritPriority; @@ -369,10 +296,6 @@ void QThreadPrivate::finish(void *arg) } d->thread_id = 0; -#ifdef Q_OS_SYMBIAN - if (closeNativeHandle) - d->data->symbian_thread_handle.Close(); -#endif d->running = false; d->finished = true; @@ -430,9 +353,6 @@ int QThread::idealThreadCount() #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) @@ -573,8 +493,7 @@ void QThread::start(Priority priority) d->priority = priority; -#if defined(QT_HAS_THREAD_PRIORITY_SCHEDULING) && !defined(Q_OS_SYMBIAN) -// ### Need to implement thread sheduling and priorities for symbian os. Implementation removed for now +#if defined(QT_HAS_THREAD_PRIORITY_SCHEDULING) switch (priority) { case InheritPriority: { @@ -616,12 +535,6 @@ void QThread::start(Priority priority) } #endif // QT_HAS_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) @@ -647,9 +560,7 @@ void QThread::start(Priority priority) 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); } @@ -662,9 +573,6 @@ void QThread::start(Priority priority) d->running = false; d->finished = false; d->thread_id = 0; -#ifdef Q_OS_SYMBIAN - d->data->symbian_thread_handle.Close(); -#endif } } @@ -676,7 +584,6 @@ void QThread::terminate() 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", @@ -684,26 +591,6 @@ void QThread::terminate() } 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) @@ -720,18 +607,6 @@ bool QThread::wait(unsigned long time) return true; while (d->running) { -#ifdef Q_OS_SYMBIAN - // Check if thread still exists. Needed because kernel will kill it without notification - // before global statics are deleted at application exit. - if (d->data->symbian_thread_handle.Handle() - && d->data->symbian_thread_handle.ExitType() != EExitPending) { - // Cannot call finish here as wait is typically called from another thread. - // It won't be necessary anyway, as we should never get here under normal operations; - // all QThreads are EProcessCritical and therefore cannot normally exit - // undetected (i.e. panic) as long as all thread control is via QThread. - return true; - } -#endif if (!d->thread_done.wait(locker.mutex(), time)) return false; } @@ -743,25 +618,11 @@ 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) diff --git a/src/corelib/thread/qwaitcondition_symbian.cpp b/src/corelib/thread/qwaitcondition_symbian.cpp new file mode 100644 index 0000000..83fa597 --- /dev/null +++ b/src/corelib/thread/qwaitcondition_symbian.cpp @@ -0,0 +1,210 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qplatformdefs.h" +#include "qwaitcondition.h" +#include "qmutex.h" +#include "qreadwritelock.h" +#include "qatomic.h" +#include "qstring.h" +#include "qelapsedtimer" +#include "qdebug.h" + +#include "qmutex_p.h" +#include "qreadwritelock_p.h" + +#include <errno.h> + +#ifndef QT_NO_THREAD + +QT_BEGIN_NAMESPACE + +static void report_error(int err, const char *where, const char *what) +{ + if (err != KErrNone) + qWarning("%s: %s failure: %d", where, what, err); +} + +class QWaitConditionPrivate { +public: + RMutex mutex; + RCondVar cond; + int waiters; + int wakeups; + int count; + + QWaitConditionPrivate() + : waiters(0), wakeups(0), count(0) + { + qt_symbian_throwIfError(mutex.CreateLocal()); + int err = cond.CreateLocal(); + if (err != KErrNone) { + mutex.Close(); + qt_symbian_throwIfError(err); + } + } + + ~QWaitConditionPrivate() + { + cond.Close(); + mutex.Close(); + } + + bool wait(unsigned long time) + { + TInt err = KErrNone; + if (time == ULONG_MAX) { + // untimed wait, loop because RCondVar::Wait may return before the condition is triggered + do { + // qDebug() << "about to wait forever"; + err = cond.Wait(mutex); + // qDebug() << "cond.Wait(mutex) returned"; + } while (err == KErrNone && wakeups == 0); + } else { + unsigned long maxWait = KMaxTInt / 1000; + QElapsedTimer waitTimer; + do { + waitTimer.start(); + unsigned long waitTime = qMin(maxWait, time); + // wait at least 1ms, as 0 means no wait + // qDebug() << "about to wait " << qMax(1ul, waitTime) * 1000; + err = cond.TimedWait(mutex, qMax(1ul, waitTime) * 1000); + // RCondVar::TimedWait may return before the condition is triggered, update the timeout with actual wait time + time -= qMin((unsigned long)waitTimer.elapsed(), waitTime); + // qDebug() << "err=" << err << " time=" << time << " wakeups=" << wakeups; + } while ((err == KErrNone && wakeups == 0) || (err == KErrTimedOut && time > 0)); + } + + Q_ASSERT_X(waiters > 0, "QWaitCondition::wait", "internal error (waiters)"); + --waiters; + if (err == KErrNone) { + Q_ASSERT_X(wakeups > 0, "QWaitCondition::wait", "internal error (wakeups)"); + --wakeups; + } + + mutex.Signal(); + + if (err && err != KErrTimedOut) + report_error(err, "QWaitCondition::wait()", "cv wait"); + return err == KErrNone; + } +}; + +QWaitCondition::QWaitCondition() +{ + d = new QWaitConditionPrivate; +} + +QWaitCondition::~QWaitCondition() +{ + delete d; +} + +void QWaitCondition::wakeOne() +{ + d->mutex.Wait(); + d->wakeups = qMin(d->wakeups + 1, d->waiters); + d->cond.Signal(); + d->count++; + d->mutex.Signal(); + if ((d->count%1000) == 999) + User::After(1); +} + +void QWaitCondition::wakeAll() +{ + d->mutex.Wait(); + d->wakeups = d->waiters; + d->cond.Broadcast(); + d->count++; + d->mutex.Signal(); + if ((d->count%1000) == 999) + User::After(1); +} + +bool QWaitCondition::wait(QMutex *mutex, unsigned long time) +{ + if (! mutex) + return false; + if (mutex->d->recursive) { + qWarning("QWaitCondition: cannot wait on recursive mutexes"); + return false; + } + + d->mutex.Wait(); + ++d->waiters; + mutex->unlock(); + + bool returnValue = d->wait(time); + + mutex->lock(); + + return returnValue; +} + +bool QWaitCondition::wait(QReadWriteLock *readWriteLock, unsigned long time) +{ + if (!readWriteLock || readWriteLock->d->accessCount == 0) + return false; + if (readWriteLock->d->accessCount < -1) { + qWarning("QWaitCondition: cannot wait on QReadWriteLocks with recursive lockForWrite()"); + return false; + } + + d->mutex.Wait(); + ++d->waiters; + + int previousAccessCount = readWriteLock->d->accessCount; + readWriteLock->unlock(); + + bool returnValue = d->wait(time); + + if (previousAccessCount < 0) + readWriteLock->lockForWrite(); + else + readWriteLock->lockForRead(); + + return returnValue; +} + +QT_END_NAMESPACE + +#endif // QT_NO_THREAD diff --git a/src/corelib/thread/qwaitcondition_unix.cpp b/src/corelib/thread/qwaitcondition_unix.cpp index f06a158..d0f23b5 100644 --- a/src/corelib/thread/qwaitcondition_unix.cpp +++ b/src/corelib/thread/qwaitcondition_unix.cpp @@ -61,8 +61,6 @@ static void report_error(int code, const char *where, const char *what) qWarning("%s: %s failure: %s", where, what, qPrintable(qt_error_string(code))); } - - class QWaitConditionPrivate { public: pthread_mutex_t mutex; diff --git a/src/corelib/thread/thread.pri b/src/corelib/thread/thread.pri index 03f661d..787d709 100644 --- a/src/corelib/thread/thread.pri +++ b/src/corelib/thread/thread.pri @@ -4,11 +4,11 @@ HEADERS += thread/qmutex.h \ thread/qreadwritelock.h \ thread/qsemaphore.h \ - thread/qthread.h \ - thread/qthreadstorage.h \ - thread/qwaitcondition.h \ - thread/qatomic.h - + thread/qthread.h \ + thread/qthreadstorage.h \ + thread/qwaitcondition.h \ + thread/qatomic.h + # private headers HEADERS += thread/qmutex_p.h \ thread/qmutexpool_p.h \ @@ -19,15 +19,19 @@ HEADERS += thread/qmutex_p.h \ SOURCES += thread/qatomic.cpp \ thread/qmutex.cpp \ thread/qreadwritelock.cpp \ - thread/qmutexpool.cpp \ - thread/qsemaphore.cpp \ - thread/qthread.cpp \ - thread/qthreadstorage.cpp + thread/qmutexpool.cpp \ + thread/qsemaphore.cpp \ + thread/qthread.cpp \ + thread/qthreadstorage.cpp + +unix:!symbian:SOURCES += thread/qmutex_unix.cpp \ + thread/qthread_unix.cpp \ + thread/qwaitcondition_unix.cpp -unix:SOURCES += thread/qmutex_unix.cpp \ - thread/qthread_unix.cpp \ - thread/qwaitcondition_unix.cpp +symbian:SOURCES += thread/qmutex_symbian.cpp \ + thread/qthread_symbian.cpp \ + thread/qwaitcondition_symbian.cpp win32:SOURCES += thread/qmutex_win.cpp \ thread/qthread_win.cpp \ - thread/qwaitcondition_win.cpp + thread/qwaitcondition_win.cpp |