| 1 | /* -*- C++ -*- |
| 2 | This file is part of ThreadWeaver. It implements the Thread class. |
| 3 | |
| 4 | SPDX-FileCopyrightText: 2004-2013 Mirko Boehm <mirko@kde.org> |
| 5 | |
| 6 | SPDX-License-Identifier: LGPL-2.0-or-later |
| 7 | |
| 8 | $Id: Thread.cpp 25 2005-08-14 12:41:38Z mirko $ |
| 9 | */ |
| 10 | |
| 11 | #include "thread.h" |
| 12 | |
| 13 | #include <QCoreApplication> |
| 14 | #include <QDebug> |
| 15 | #include <QPointer> |
| 16 | |
| 17 | #include "debuggingaids.h" |
| 18 | #include "exception.h" |
| 19 | #include "job.h" |
| 20 | #include "threadweaver.h" |
| 21 | #include "weaver.h" |
| 22 | |
| 23 | using namespace ThreadWeaver; |
| 24 | |
| 25 | class Q_DECL_HIDDEN Thread::Private |
| 26 | { |
| 27 | public: |
| 28 | explicit Private(Weaver *theParent) |
| 29 | : parent(theParent) |
| 30 | , id(makeId()) |
| 31 | , job(nullptr) |
| 32 | { |
| 33 | Q_ASSERT(parent); |
| 34 | } |
| 35 | |
| 36 | Weaver *parent; |
| 37 | const unsigned int id; |
| 38 | JobPointer job; |
| 39 | QMutex mutex; |
| 40 | |
| 41 | static unsigned int makeId() |
| 42 | { |
| 43 | static QAtomicInt s_id(1); |
| 44 | return s_id.fetchAndAddRelease(valueToAdd: 1); |
| 45 | } |
| 46 | }; |
| 47 | |
| 48 | Thread::Thread(Weaver *parent) |
| 49 | : QThread() // no parent, because the QObject hierarchy of this thread |
| 50 | // does not have a parent (see QObject::pushToThread) |
| 51 | , d(new Private(parent)) |
| 52 | { |
| 53 | const QString queueName = |
| 54 | parent->objectName().isEmpty() ? QString::fromLatin1(ba: "Queue(0x%1)" ).arg(a: quintptr(parent), fieldwidth: 0, base: 16, fillChar: QChar::fromLatin1(c: '0')) : parent->objectName(); |
| 55 | setObjectName(QString::fromLatin1(ba: "%1[%2]" ).arg(a: queueName).arg(a: QString::number(id()), fieldWidth: 2, fillChar: QChar::fromLatin1(c: '0'))); |
| 56 | } |
| 57 | |
| 58 | Thread::~Thread() |
| 59 | { |
| 60 | delete d; |
| 61 | } |
| 62 | |
| 63 | unsigned int Thread::id() const |
| 64 | { |
| 65 | return d->id; // id is const |
| 66 | } |
| 67 | |
| 68 | void Thread::run() |
| 69 | { |
| 70 | Q_ASSERT(d->parent); |
| 71 | Q_ASSERT(QCoreApplication::instance() != nullptr); |
| 72 | d->parent->threadEnteredRun(thread: this); |
| 73 | |
| 74 | TWDEBUG(3, "Thread::run [%u]: running.\n" , id()); |
| 75 | |
| 76 | bool wasBusy = false; |
| 77 | while (true) { |
| 78 | TWDEBUG(3, "Thread::run [%u]: trying to execute the next job.\n" , id()); |
| 79 | |
| 80 | try { |
| 81 | // the assignment is intentional: newJob needs to go out of scope at the end of the if statement |
| 82 | if (JobPointer newJob = d->parent->applyForWork(thread: this, wasBusy)) { |
| 83 | QMutexLocker l(&d->mutex); |
| 84 | Q_UNUSED(l); |
| 85 | d->job = newJob; |
| 86 | } else { |
| 87 | break; |
| 88 | } |
| 89 | } catch (AbortThread &) { |
| 90 | break; |
| 91 | } |
| 92 | |
| 93 | wasBusy = true; |
| 94 | d->job->execute(job: d->job, this); |
| 95 | JobPointer oldJob; |
| 96 | { // When finally destroying the last reference to d->job, do not hold the mutex. |
| 97 | // It may trigger destruction of the job, which can execute arbitrary code. |
| 98 | QMutexLocker l(&d->mutex); |
| 99 | Q_UNUSED(l); |
| 100 | oldJob = d->job; |
| 101 | d->job.clear(); |
| 102 | } |
| 103 | oldJob.clear(); |
| 104 | } |
| 105 | TWDEBUG(3, "Thread::run [%u]: exiting.\n" , id()); |
| 106 | } |
| 107 | |
| 108 | void Thread::requestAbort() |
| 109 | { |
| 110 | QMutexLocker l(&d->mutex); |
| 111 | Q_UNUSED(l); |
| 112 | if (d->job) { |
| 113 | d->job->requestAbort(); |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | #include "moc_thread.cpp" |
| 118 | |