diff options
author | Sami Lempinen <sami.lempinen@nokia.com> | 2011-03-09 14:00:50 (GMT) |
---|---|---|
committer | Sami Lempinen <sami.lempinen@nokia.com> | 2011-03-09 14:00:50 (GMT) |
commit | cf94df01d2d2364653f2ab602688394450e92d31 (patch) | |
tree | 3e5c4842069406ef6031d0f7bcfaf2683cd8786a /src/corelib/thread | |
parent | c8aad7b59ede3e1c1655059ee9db2436c627aae2 (diff) | |
parent | ad9213b694902d0698911ed1212efca984ee8ab3 (diff) | |
download | Qt-cf94df01d2d2364653f2ab602688394450e92d31.zip Qt-cf94df01d2d2364653f2ab602688394450e92d31.tar.gz Qt-cf94df01d2d2364653f2ab602688394450e92d31.tar.bz2 |
Merging SymbianLite branch to master.
Diffstat (limited to 'src/corelib/thread')
-rw-r--r-- | src/corelib/thread/qmutex_p.h | 8 | ||||
-rw-r--r-- | src/corelib/thread/qmutex_symbian.cpp | 101 | ||||
-rw-r--r-- | src/corelib/thread/qthread_symbian.cpp | 565 | ||||
-rw-r--r-- | src/corelib/thread/qthread_unix.cpp | 151 | ||||
-rw-r--r-- | src/corelib/thread/qwaitcondition_symbian.cpp | 196 | ||||
-rw-r--r-- | src/corelib/thread/qwaitcondition_unix.cpp | 2 | ||||
-rw-r--r-- | src/corelib/thread/thread.pri | 30 |
7 files changed, 889 insertions, 164 deletions
diff --git a/src/corelib/thread/qmutex_p.h b/src/corelib/thread/qmutex_p.h index 1a31c87..70860b1 100644 --- a/src/corelib/thread/qmutex_p.h +++ b/src/corelib/thread/qmutex_p.h @@ -62,6 +62,10 @@ # include <mach/semaphore.h> #endif +#if defined(Q_OS_SYMBIAN) +# include <e32std.h> +#endif + QT_BEGIN_NAMESPACE class QMutexPrivate : public QMutexData { @@ -81,12 +85,14 @@ 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) + RSemaphore lock; #endif }; diff --git a/src/corelib/thread/qmutex_symbian.cpp b/src/corelib/thread/qmutex_symbian.cpp new file mode 100644 index 0000000..288c576 --- /dev/null +++ b/src/corelib/thread/qmutex_symbian.cpp @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** 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" + +#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) +{ + int r = lock.CreateLocal(0); + if (r != KErrNone) + qWarning("QMutex: failed to create lock, error %d", r); + qt_symbian_throwIfError(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; + // Symbian undocumented feature - 0us means no timeout! Use a minimum of 1 + r = lock.Wait(qMax(1, 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..02d671b --- /dev/null +++ b/src/corelib/thread/qthread_symbian.cpp @@ -0,0 +1,565 @@ +/**************************************************************************** +** +** 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> +#include <private/qeventdispatcher_symbian_p.h> +#include "qthreadstorage.h" +#include "qthread_p.h" +#include "qsystemerror_p.h" + +#include <sched.h> + + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_THREAD + +enum { ThreadPriorityResetFlag = 0x80000000 }; + +// Utility functions for getting, setting and clearing thread specific data. +static QThreadData *get_thread_data() +{ + return reinterpret_cast<QThreadData *>(Dll::Tls()); +} + +static void set_thread_data(QThreadData *data) +{ + qt_symbian_throwIfError(Dll::SetTls(data)); +} + +static void clear_thread_data() +{ + Dll::FreeTls(); +} + + +static void init_symbian_thread_handle(RThread &thread) +{ + thread = RThread(); + TThreadId threadId = thread.Id(); + qt_symbian_throwIfError(thread.Open(threadId, EOwnerProcess)); +} + +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; +} + + +class QCAdoptedThreadMonitor : public CActive +{ +public: + QCAdoptedThreadMonitor(QThread *thread) + : CActive(EPriorityStandard), data(QThreadData::get2(thread)) + { + CActiveScheduler::Add(this); + data->symbian_thread_handle.Logon(iStatus); + SetActive(); + } + ~QCAdoptedThreadMonitor() + { + Cancel(); + } + void DoCancel() + { + data->symbian_thread_handle.LogonCancel(iStatus); + } + void RunL() + { + data->deref(); + delete this; + } +private: + QThreadData* data; +}; + +class QCAddAdoptedThread : public CActive +{ +public: + QCAddAdoptedThread() + : CActive(EPriorityStandard) + { + CActiveScheduler::Add(this); + } + void ConstructL() + { + User::LeaveIfError(monitorThread.Open(RThread().Id())); + start(); + } + ~QCAddAdoptedThread() + { + Cancel(); + monitorThread.Close(); + } + void DoCancel() + { + User::RequestComplete(stat, KErrCancel); + } + void start() + { + iStatus = KRequestPending; + SetActive(); + stat = &iStatus; + } + void RunL() + { + if (iStatus.Int() != KErrNone) + return; + + QMutexLocker adoptedThreadMonitorMutexlock(&adoptedThreadMonitorMutex); + for (int i=threadsToAdd.size()-1; i>=0; i--) { + // Create an active object to monitor the thread + new (ELeave) QCAdoptedThreadMonitor(threadsToAdd[i]); + threadsToAdd.pop_back(); + } + start(); + } + static void add(QThread *thread) + { + QMutexLocker adoptedThreadMonitorMutexlock(&adoptedThreadMonitorMutex); + if (!adoptedThreadAdder) { + RThread monitorThread; + qt_symbian_throwIfError(monitorThread.Create(KNullDesC(), &monitorThreadFunc, 1024, &User::Allocator(), 0)); + TRequestStatus started; + monitorThread.Rendezvous(started); + monitorThread.Resume(); + User::WaitForRequest(started); + monitorThread.Close(); + } + adoptedThreadAdder->threadsToAdd.push_back(thread); + if (adoptedThreadAdder->stat) { + adoptedThreadAdder->monitorThread.RequestComplete(adoptedThreadAdder->stat, KErrNone); + } + } + static void monitorThreadFuncL() + { + CActiveScheduler* scheduler = new (ELeave) CActiveScheduler(); + CleanupStack::PushL(scheduler); + CActiveScheduler::Install(scheduler); + + adoptedThreadAdder = new(ELeave) QCAddAdoptedThread(); + CleanupStack::PushL(adoptedThreadAdder); + adoptedThreadAdder->ConstructL(); + + RThread::Rendezvous(KErrNone); + CActiveScheduler::Start(); + + CleanupStack::PopAndDestroy(adoptedThreadAdder); + adoptedThreadAdder = 0; + CleanupStack::PopAndDestroy(scheduler); + } + static int monitorThreadFunc(void *) + { + _LIT(KMonitorThreadName, "adoptedMonitorThread"); + RThread::RenameMe(KMonitorThreadName()); + CTrapCleanup* cleanup = CTrapCleanup::New(); + TRAPD(ret, monitorThreadFuncL()); + delete cleanup; + return ret; + } + +private: + QVector<QThread*> threadsToAdd; + RThread monitorThread; + static QMutex adoptedThreadMonitorMutex; + static QCAddAdoptedThread* adoptedThreadAdder; + TRequestStatus *stat; +}; + +QMutex QCAddAdoptedThread::adoptedThreadMonitorMutex; +QCAddAdoptedThread* QCAddAdoptedThread::adoptedThreadAdder = 0; + +void QAdoptedThread::init() +{ + Q_D(QThread); + d->thread_id = RThread().Id(); // type operator to TUint + init_symbian_thread_handle(d->data->symbian_thread_handle); + QCAddAdoptedThread::add(this); +} + +/* + 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)); + } + + // 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->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(); +} + +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: + symPriority = EPriorityMuchMore; + break; + case QThread::InheritPriority: + default: + symPriority = RThread().Priority(); + 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 = d->data->symbian_thread_handle.Create(KNullDesC, (TThreadFunction) QThreadPrivate::start, d->stackSize, NULL, this); + if (code == 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 { + qWarning("QThread::start: Thread creation error: %s", QSystemError(code, QSystemError::NativeError).toString()); + + 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 5e0d2a2..835378a 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" @@ -163,57 +159,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 HAVE_TLS - return currentThreadData; -#elif defined 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 HAVE_TLS - currentThreadData = data; -#elif defined 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 HAVE_TLS - currentThreadData = 0; -#elif defined 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(); @@ -251,9 +213,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 } /* @@ -281,11 +240,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(); } @@ -293,11 +248,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); @@ -307,23 +259,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(); @@ -336,35 +271,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; @@ -389,10 +310,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; @@ -450,9 +367,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) @@ -593,8 +507,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: { @@ -636,12 +549,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) @@ -667,9 +574,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); } @@ -682,9 +587,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 } } @@ -696,7 +598,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", @@ -704,26 +605,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) @@ -740,18 +621,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; } @@ -763,25 +632,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..1e7172a --- /dev/null +++ b/src/corelib/thread/qwaitcondition_symbian.cpp @@ -0,0 +1,196 @@ +/**************************************************************************** +** +** 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 "qmutex_p.h" +#include "qreadwritelock_p.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; + + QWaitConditionPrivate() + : waiters(0), wakeups(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 { + err = cond.Wait(mutex); + } 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 + 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); + } 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->mutex.Signal(); +} + +void QWaitCondition::wakeAll() +{ + d->mutex.Wait(); + d->wakeups = d->waiters; + d->cond.Broadcast(); + d->mutex.Signal(); +} + +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 |