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 | |