1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "qthread.h"
43 #include "qplatformdefs.h"
44 #include <private/qcoreapplication_p.h>
45 #include <private/qeventdispatcher_symbian_p.h>
46 #include "qthreadstorage.h"
47 #include "qthread_p.h"
48 #include <private/qsystemerror_p.h>
49 #include <private/qcore_symbian_p.h>
50 
51 #include <sched.h>
52 #include <hal.h>
53 #include <hal_data.h>
54 #include <e32math.h>
55 
56 #include <QRegExp>
57 // This can be manually enabled if debugging thread problems
58 #ifdef QT_USE_RTTI_IN_THREAD_CLASSNAME
59 #include <typeinfo>
60 #endif
61 
62 // You only find these enumerations on Symbian^3 onwards, so we need to provide our own
63 // to remain compatible with older releases. They won't be called by pre-Sym^3 SDKs.
64 
65 // HALData::ENumCpus
66 #define QT_HALData_ENumCpus 119
67 
68 QT_BEGIN_NAMESPACE
69 
70 #ifndef QT_NO_THREAD
71 
72 enum { ThreadPriorityResetFlag = 0x80000000 };
73 
74 // Utility functions for getting, setting and clearing thread specific data.
get_thread_data()75 static QThreadData *get_thread_data()
76 {
77     return reinterpret_cast<QThreadData *>(Dll::Tls());
78 }
79 
set_thread_data(QThreadData * data)80 static void set_thread_data(QThreadData *data)
81 {
82     qt_symbian_throwIfError(Dll::SetTls(data));
83 }
84 
clear_thread_data()85 static void clear_thread_data()
86 {
87     Dll::FreeTls();
88 }
89 
90 
init_symbian_thread_handle(RThread & thread)91 static void init_symbian_thread_handle(RThread &thread)
92 {
93     thread = RThread();
94     TThreadId threadId = thread.Id();
95     qt_symbian_throwIfError(thread.Open(threadId, EOwnerProcess));
96 }
97 
current()98 QThreadData *QThreadData::current()
99 {
100     QThreadData *data = get_thread_data();
101     if (!data) {
102         void *a;
103         if (QInternal::activateCallbacks(QInternal::AdoptCurrentThread, &a)) {
104             QThread *adopted = static_cast<QThread*>(a);
105             Q_ASSERT(adopted);
106             data = QThreadData::get2(adopted);
107             set_thread_data(data);
108             adopted->d_func()->running = true;
109             adopted->d_func()->finished = false;
110             static_cast<QAdoptedThread *>(adopted)->init();
111         } else {
112             data = new QThreadData;
113             QT_TRY {
114                 set_thread_data(data);
115                 data->thread = new QAdoptedThread(data);
116             } QT_CATCH(...) {
117                 clear_thread_data();
118                 data->deref();
119                 data = 0;
120                 QT_RETHROW;
121             }
122             data->deref();
123         }
124         data->isAdopted = true;
125         data->threadId = QThread::currentThreadId();
126         if (!QCoreApplicationPrivate::theMainThread)
127             QCoreApplicationPrivate::theMainThread = data->thread;
128     }
129     return data;
130 }
131 
132 
133 class QCAdoptedThreadMonitor : public CActive
134 {
135 public:
QCAdoptedThreadMonitor(QThread * thread)136     QCAdoptedThreadMonitor(QThread *thread)
137     : CActive(EPriorityStandard), data(QThreadData::get2(thread))
138     {
139         CActiveScheduler::Add(this);
140         data->symbian_thread_handle.Logon(iStatus);
141         SetActive();
142     }
~QCAdoptedThreadMonitor()143     ~QCAdoptedThreadMonitor()
144     {
145         Cancel();
146     }
DoCancel()147     void DoCancel()
148     {
149         data->symbian_thread_handle.LogonCancel(iStatus);
150     }
151     void RunL();
152 private:
153     QThreadData* data;
154 };
155 
156 class QCAddAdoptedThread : public CActive
157 {
158 public:
QCAddAdoptedThread()159     QCAddAdoptedThread()
160     : CActive(EPriorityStandard)
161     {
162         CActiveScheduler::Add(this);
163     }
ConstructL()164     void ConstructL()
165     {
166         User::LeaveIfError(monitorThread.Open(RThread().Id()));
167         start();
168     }
~QCAddAdoptedThread()169     ~QCAddAdoptedThread()
170     {
171         Cancel();
172         monitorThread.Close();
173     }
DoCancel()174     void DoCancel()
175     {
176         User::RequestComplete(stat, KErrCancel);
177     }
start()178     void start()
179     {
180         iStatus = KRequestPending;
181         SetActive();
182         stat = &iStatus;
183     }
RunL()184     void RunL()
185     {
186         if (iStatus.Int() != KErrNone)
187             return;
188 
189         QMutexLocker adoptedThreadMonitorMutexlock(&adoptedThreadMonitorMutex);
190         for (int i=threadsToAdd.size()-1; i>=0; i--) {
191             // Create an active object to monitor the thread
192             new (ELeave) QCAdoptedThreadMonitor(threadsToAdd[i]);
193             count++;
194             threadsToAdd.pop_back();
195         }
196         start();
197     }
add(QThread * thread)198     static void add(QThread *thread)
199     {
200         QMutexLocker adoptedThreadMonitorMutexlock(&adoptedThreadMonitorMutex);
201         if (!adoptedThreadAdder) {
202             RThread monitorThread;
203             qt_symbian_throwIfError(monitorThread.Create(KNullDesC(), &monitorThreadFunc, 1024, &User::Allocator(), 0));
204             TRequestStatus started;
205             monitorThread.Rendezvous(started);
206             monitorThread.Resume();
207             User::WaitForRequest(started);
208             monitorThread.Close();
209         }
210         if (RThread().Id() == adoptedThreadAdder->monitorThread.Id())
211             return;
212         adoptedThreadAdder->threadsToAdd.push_back(thread);
213         if (adoptedThreadAdder->stat) {
214             adoptedThreadAdder->monitorThread.RequestComplete(adoptedThreadAdder->stat, KErrNone);
215         }
216     }
monitorThreadFuncL()217     static void monitorThreadFuncL()
218     {
219         CActiveScheduler* scheduler = new (ELeave) CActiveScheduler();
220         CleanupStack::PushL(scheduler);
221         CActiveScheduler::Install(scheduler);
222 
223         adoptedThreadAdder = new(ELeave) QCAddAdoptedThread();
224         CleanupStack::PushL(adoptedThreadAdder);
225         adoptedThreadAdder->ConstructL();
226         QCAddAdoptedThread *adder = adoptedThreadAdder;
227 
228         RThread::Rendezvous(KErrNone);
229         CActiveScheduler::Start();
230 
231         CleanupStack::PopAndDestroy(adder);
232         CleanupStack::PopAndDestroy(scheduler);
233     }
monitorThreadFunc(void *)234     static int monitorThreadFunc(void *)
235     {
236         _LIT(KMonitorThreadName, "adoptedMonitorThread");
237         RThread::RenameMe(KMonitorThreadName());
238         CTrapCleanup* cleanup = CTrapCleanup::New();
239         TRAPD(ret, monitorThreadFuncL());
240         delete cleanup;
241         return ret;
242     }
threadDied()243     static void threadDied()
244     {
245         QMutexLocker adoptedThreadMonitorMutexlock(&adoptedThreadMonitorMutex);
246         if (adoptedThreadAdder) {
247             adoptedThreadAdder->count--;
248             if (adoptedThreadAdder->count <= 0 && adoptedThreadAdder->threadsToAdd.size() == 0) {
249                 CActiveScheduler::Stop();
250                 adoptedThreadAdder = 0;
251             }
252         }
253     }
254 
255 private:
256     QVector<QThread*> threadsToAdd;
257     RThread monitorThread;
258     static QMutex adoptedThreadMonitorMutex;
259     static QCAddAdoptedThread *adoptedThreadAdder;
260     int count;
261     TRequestStatus *stat;
262 };
263 
264 QMutex QCAddAdoptedThread::adoptedThreadMonitorMutex;
265 QCAddAdoptedThread* QCAddAdoptedThread::adoptedThreadAdder = 0;
266 
finishAdoptedThread(QThreadData * data,bool closeNativeHandle)267 static void finishAdoptedThread(QThreadData* data, bool closeNativeHandle)
268 {
269     if (data->isAdopted) {
270         QThread *thread = data->thread;
271         Q_ASSERT(thread);
272         QThreadPrivate *thread_p = static_cast<QThreadPrivate *>(QObjectPrivate::get(thread));
273         if (!thread_p->finished)
274             thread_p->finish(thread, true, closeNativeHandle);
275         else if (closeNativeHandle && data->symbian_thread_handle.Handle())
276             data->symbian_thread_handle.Close();
277     }
278 }
279 
RunL()280 void QCAdoptedThreadMonitor::RunL()
281 {
282     // clean up the thread, or close the handle if that's all that's left
283     finishAdoptedThread(data, true);
284     data->deref();
285     QCAddAdoptedThread::threadDied();
286     delete this;
287 }
288 
289 static pthread_once_t current_thread_data_once = PTHREAD_ONCE_INIT;
290 static pthread_key_t current_thread_data_key;
291 
pthread_in_thread_cleanup(void * p)292 static void pthread_in_thread_cleanup(void *p)
293 {
294     QThreadData *data = static_cast<QThreadData *>(p);
295     // clean up the thread, but leave the handle for adoptedThreadMonitor
296     finishAdoptedThread(data, false);
297 }
298 
create_current_thread_data_key()299 static void create_current_thread_data_key()
300 {
301     pthread_key_create(&current_thread_data_key, pthread_in_thread_cleanup);
302 }
303 
destroy_current_thread_data_key()304 static void destroy_current_thread_data_key()
305 {
306     pthread_once(&current_thread_data_once, create_current_thread_data_key);
307     pthread_key_delete(current_thread_data_key);
308 }
Q_DESTRUCTOR_FUNCTION(destroy_current_thread_data_key)309 Q_DESTRUCTOR_FUNCTION(destroy_current_thread_data_key)
310 
311 void QAdoptedThread::init()
312 {
313     Q_D(QThread);
314     d->thread_id = RThread().Id();  // type operator to TUint
315     init_symbian_thread_handle(d->data->symbian_thread_handle);
316     QCAddAdoptedThread::add(this);
317     pthread_once(&current_thread_data_once, create_current_thread_data_key);
318     pthread_setspecific(current_thread_data_key, get_thread_data());
319 }
320 
321 /*
322    QThreadPrivate
323 */
324 
325 #if defined(Q_C_CALLBACKS)
326 extern "C" {
327 #endif
328 
329 typedef void*(*QtThreadCallback)(void*);
330 
331 #if defined(Q_C_CALLBACKS)
332 }
333 #endif
334 
335 #endif // QT_NO_THREAD
336 
createEventDispatcher(QThreadData * data)337 void QThreadPrivate::createEventDispatcher(QThreadData *data)
338 {
339     QMutexLocker l(&data->postEventList.mutex);
340     data->eventDispatcher = new QEventDispatcherSymbian;
341     l.unlock();
342     data->eventDispatcher->startingUp();
343 }
344 
345 #ifndef QT_NO_THREAD
346 
start(void * arg)347 void *QThreadPrivate::start(void *arg)
348 {
349     QThread *thr = reinterpret_cast<QThread *>(arg);
350     QThreadData *data = QThreadData::get2(thr);
351 
352     // do we need to reset the thread priority?
353     if (int(thr->d_func()->priority) & ThreadPriorityResetFlag) {
354         thr->setPriority(QThread::Priority(thr->d_func()->priority & ~ThreadPriorityResetFlag));
355     }
356 
357     // On symbian, threads other than the main thread are non critical by default
358     // This means a worker thread can crash without crashing the application - to
359     // use this feature, we would need to use RThread::Logon in the main thread
360     // to catch abnormal thread exit and emit the finished signal.
361     // For the sake of cross platform consistency, we set the thread as process critical
362     // - advanced users who want the symbian behaviour can change the critical
363     // attribute of the thread again once the app gains control in run()
364     User::SetCritical(User::EProcessCritical);
365 
366     data->threadId = QThread::currentThreadId();
367     set_thread_data(data);
368 
369     CTrapCleanup *cleanup = CTrapCleanup::New();
370     q_check_ptr(cleanup);
371 
372     {
373         QMutexLocker locker(&thr->d_func()->mutex);
374         data->quitNow = thr->d_func()->exited;
375     }
376 
377     // ### TODO: allow the user to create a custom event dispatcher
378     createEventDispatcher(data);
379 
380     TRAPD(err, {
381         try {
382             emit thr->started();
383             thr->run();
384         } catch (const std::exception& ex) {
385             qWarning("QThreadPrivate::start: Thread exited on exception %s", ex.what());
386             User::Leave(KErrGeneral);   // leave to force cleanup stack cleanup
387         }
388     });
389     if (err)
390         qWarning("QThreadPrivate::start: Thread exited on leave %d", err);
391 
392     // finish emits signals which should be wrapped in a trap for Symbian code, but otherwise ignore leaves and exceptions.
393     TRAP(err, {
394         try {
395             QThreadPrivate::finish(arg);
396         } catch (const std::exception& ex) {
397             User::Leave(KErrGeneral);   // leave to force cleanup stack cleanup
398         }
399     });
400 
401     delete cleanup;
402 
403     return 0;
404 }
405 
finish(void * arg,bool lockAnyway,bool closeNativeHandle)406 void QThreadPrivate::finish(void *arg, bool lockAnyway, bool closeNativeHandle)
407 {
408     QThread *thr = reinterpret_cast<QThread *>(arg);
409     QThreadPrivate *d = thr->d_func();
410 
411     QMutexLocker locker(lockAnyway ? &d->mutex : 0);
412 
413     d->isInFinish = true;
414     d->priority = QThread::InheritPriority;
415     bool terminated = d->terminated;
416     void *data = &d->data->tls;
417     locker.unlock();
418     if (terminated)
419         emit thr->terminated();
420     emit thr->finished();
421     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
422     QThreadStorageData::finish((void **)data);
423     locker.relock();
424     d->terminated = false;
425 
426     QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher;
427     if (eventDispatcher) {
428         d->data->eventDispatcher = 0;
429         locker.unlock();
430         eventDispatcher->closingDown();
431         delete eventDispatcher;
432         locker.relock();
433     }
434 
435     d->thread_id = 0;
436     if (closeNativeHandle)
437         d->data->symbian_thread_handle.Close();
438     d->running = false;
439     d->finished = true;
440 
441     d->isInFinish = false;
442     d->thread_done.wakeAll();
443 }
444 
445 
446 
447 
448 /**************************************************************************
449  ** QThread
450  *************************************************************************/
451 
currentThreadId()452 Qt::HANDLE QThread::currentThreadId()
453 {
454     return (Qt::HANDLE) (TUint) RThread().Id();
455 }
456 
idealThreadCount()457 int QThread::idealThreadCount()
458 {
459     int cores = 1;
460 
461     if (QSysInfo::symbianVersion() >= QSysInfo::SV_SF_3) {
462         TInt inumcpus;
463         TInt err;
464         err = HAL::Get((HALData::TAttribute)QT_HALData_ENumCpus, inumcpus);
465         if (err == KErrNone) {
466             cores = qMax(inumcpus, 1);
467         }
468     }
469 
470     return cores;
471 }
472 
yieldCurrentThread()473 void QThread::yieldCurrentThread()
474 {
475     sched_yield();
476 }
477 
478 /*  \internal
479     helper function to do thread sleeps
480 */
thread_sleep(unsigned long remaining,unsigned long scale)481 static void thread_sleep(unsigned long remaining, unsigned long scale)
482 {
483     // maximum Symbian wait is 2^31 microseconds
484     unsigned long maxWait = KMaxTInt / scale;
485     do {
486         unsigned long waitTime = qMin(maxWait, remaining);
487         remaining -= waitTime;
488         User::AfterHighRes(waitTime * scale);
489     } while (remaining);
490 }
491 
sleep(unsigned long secs)492 void QThread::sleep(unsigned long secs)
493 {
494     thread_sleep(secs, 1000000ul);
495 }
496 
msleep(unsigned long msecs)497 void QThread::msleep(unsigned long msecs)
498 {
499     thread_sleep(msecs, 1000ul);
500 }
501 
usleep(unsigned long usecs)502 void QThread::usleep(unsigned long usecs)
503 {
504     thread_sleep(usecs, 1ul);
505 }
506 
calculateSymbianPriority(QThread::Priority priority)507 TThreadPriority calculateSymbianPriority(QThread::Priority priority)
508     {
509     // Both Qt & Symbian use limited enums; this matches the mapping previously done through conversion to Posix granularity
510     TThreadPriority symPriority;
511     switch (priority)
512     {
513         case QThread::IdlePriority:
514             symPriority = EPriorityMuchLess;
515             break;
516         case QThread::LowestPriority:
517         case QThread::LowPriority:
518             symPriority = EPriorityLess;
519             break;
520         case QThread::NormalPriority:
521             symPriority = EPriorityNormal;
522             break;
523         case QThread::HighPriority:
524             symPriority = EPriorityMore;
525             break;
526         case QThread::HighestPriority:
527         case QThread::TimeCriticalPriority:
528             symPriority = EPriorityMuchMore;
529             break;
530         case QThread::InheritPriority:
531         default:
532             symPriority = RThread().Priority();
533             break;
534     }
535     return symPriority;
536     }
537 
start(Priority priority)538 void QThread::start(Priority priority)
539 {
540     Q_D(QThread);
541     QMutexLocker locker(&d->mutex);
542 
543     if (d->isInFinish)
544         d->thread_done.wait(locker.mutex());
545 
546     if (d->running)
547         return;
548 
549     d->running = true;
550     d->finished = false;
551     d->terminated = false;
552     d->returnCode = 0;
553     d->exited = false;
554 
555     d->priority = priority;
556 
557     if (d->stackSize == 0)
558         // The default stack size on Symbian is very small, making even basic
559         // operations like file I/O fail, so we increase it by default.
560         d->stackSize = 0x14000; // Maximum stack size on Symbian.
561 
562     int code = KErrAlreadyExists;
563     QString className(QLatin1String(metaObject()->className()));
564 #ifdef QT_USE_RTTI_IN_THREAD_CLASSNAME
565     // use RTTI, if enabled, to get a more accurate className. This must be manually enabled.
566     const char* rttiName = typeid(*this).name();
567     if (rttiName)
568         className = QLatin1String(rttiName);
569 #endif
570     QString threadNameBase = QString(QLatin1String("%1_%2_v=0x%3_")).arg(objectName()).arg(className).arg(*(uint*)this,8,16,QLatin1Char('0'));
571     // Thread name can contain only characters allowed by User::ValidateName() otherwise RThread::Create fails.
572     // Not allowed characters are:
573     // - any character outside range 0x20 - 0x7e
574     // - or asterisk, question mark or colon
575     const QRegExp notAllowedChars(QLatin1String("[^\\x20-\\x7e]|\\*|\\?|\\:"));
576     threadNameBase.replace(notAllowedChars, QLatin1String("_"));
577 
578     TPtrC threadNameBasePtr(qt_QString2TPtrC(threadNameBase));
579     // max thread name length is KMaxKernelName
580     TBuf<KMaxKernelName> name;
581     threadNameBasePtr.Set(threadNameBasePtr.Left(qMin(threadNameBasePtr.Length(), name.MaxLength() - 8)));
582     const int MaxRetries = 10;
583     for (int i=0; i<MaxRetries && code == KErrAlreadyExists; i++) {
584         // generate a thread name with a random component to avoid and resolve name collisions
585         // a named thread can be opened from another process
586         name.Zero();
587         name.Append(threadNameBasePtr);
588         name.AppendNumFixedWidth(Math::Random(), EHex, 8);
589         code = d->data->symbian_thread_handle.Create(name, (TThreadFunction) QThreadPrivate::start, d->stackSize, NULL, this);
590     }
591     if (code == KErrNone) {
592         d->thread_id = d->data->symbian_thread_handle.Id();
593         TThreadPriority symPriority = calculateSymbianPriority(priority);
594         d->data->symbian_thread_handle.SetPriority(symPriority);
595         d->data->symbian_thread_handle.Resume();
596     } else {
597         qWarning("QThread::start: Thread creation error: %s", qPrintable(QSystemError(code, QSystemError::NativeError).toString()));
598 
599         d->running = false;
600         d->finished = false;
601         d->thread_id = 0;
602         d->data->symbian_thread_handle.Close();
603     }
604 }
605 
terminate()606 void QThread::terminate()
607 {
608     Q_D(QThread);
609     QMutexLocker locker(&d->mutex);
610 
611     if (!d->thread_id)
612         return;
613 
614     if (!d->running)
615         return;
616     if (!d->terminationEnabled) {
617         d->terminatePending = true;
618         return;
619     }
620 
621     d->terminated = true;
622     // "false, false" meaning:
623     // 1. lockAnyway = false. Don't lock the mutex because it's already locked
624     //    (see above).
625     // 2. closeNativeSymbianHandle = false. We don't want to close the thread handle,
626     //    because we need it here to terminate the thread.
627     QThreadPrivate::finish(this, false, false);
628     d->data->symbian_thread_handle.Terminate(KErrNone);
629     d->data->symbian_thread_handle.Close();
630 }
631 
wait(unsigned long time)632 bool QThread::wait(unsigned long time)
633 {
634     Q_D(QThread);
635     QMutexLocker locker(&d->mutex);
636 
637     if (d->thread_id == (TUint) RThread().Id()) {
638         qWarning("QThread::wait: Thread tried to wait on itself");
639         return false;
640     }
641 
642     if (d->finished || !d->running)
643         return true;
644 
645     while (d->running) {
646         // Check if thread still exists. Needed because kernel will kill it without notification
647         // before global statics are deleted at application exit.
648         if (d->data->symbian_thread_handle.Handle()
649             && d->data->symbian_thread_handle.ExitType() != EExitPending) {
650             // Cannot call finish here as wait is typically called from another thread.
651             // It won't be necessary anyway, as we should never get here under normal operations;
652             // all QThreads are EProcessCritical and therefore cannot normally exit
653             // undetected (i.e. panic) as long as all thread control is via QThread.
654             return true;
655         }
656         if (!d->thread_done.wait(locker.mutex(), time))
657             return false;
658     }
659     return true;
660 }
661 
setTerminationEnabled(bool enabled)662 void QThread::setTerminationEnabled(bool enabled)
663 {
664     QThread *thr = currentThread();
665     Q_ASSERT_X(thr != 0, "QThread::setTerminationEnabled()",
666                "Current thread was not started with QThread.");
667     QThreadPrivate *d = thr->d_func();
668     QMutexLocker locker(&d->mutex);
669     d->terminationEnabled = enabled;
670     if (enabled && d->terminatePending) {
671         d->terminated = true;
672         // "false" meaning:
673         // -  lockAnyway = false. Don't lock the mutex because it's already locked
674         //    (see above).
675         QThreadPrivate::finish(thr, false);
676         locker.unlock(); // don't leave the mutex locked!
677         User::Exit(0);  // may be some other cleanup required? what if AS or cleanup stack?
678     }
679 }
680 
setPriority(Priority priority)681 void QThread::setPriority(Priority priority)
682 {
683     Q_D(QThread);
684     QMutexLocker locker(&d->mutex);
685     if (!d->running) {
686         qWarning("QThread::setPriority: Cannot set priority, thread is not running");
687         return;
688     }
689 
690     d->priority = priority;
691 
692     // copied from start() with a few modifications:
693     TThreadPriority symPriority = calculateSymbianPriority(priority);
694     d->data->symbian_thread_handle.SetPriority(symPriority);
695 }
696 
697 #endif // QT_NO_THREAD
698 
699 QT_END_NAMESPACE
700 
701