swift
worker.h
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: Copyright (C) 2014 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
5 
6 #ifndef SWIFT_MISC_WORKER_H
7 #define SWIFT_MISC_WORKER_H
8 
9 #include <algorithm>
10 #include <atomic>
11 #include <functional>
12 #include <future>
13 #include <memory>
14 #include <type_traits>
15 
16 #include <QMetaObject>
17 #include <QMetaType>
18 #include <QMutex>
19 #include <QMutexLocker>
20 #include <QObject>
21 #include <QPointer>
22 #include <QString>
23 #include <QThread>
24 #include <QTimer>
25 #include <QVariant>
26 #include <QtGlobal>
27 
28 #include "misc/invoke.h"
29 #include "misc/promise.h"
30 #include "misc/swiftmiscexport.h"
31 
32 namespace swift::misc
33 {
38  {
39  Q_OBJECT
40 
41  public:
43  explicit CRegularThread(QObject *parent = nullptr) : QThread(parent) {}
44 
46  ~CRegularThread() override;
47 
49  CRegularThread(const CRegularThread &) = delete;
50 
53 
56 
59 
60  protected:
62  void run() override;
63 
64  private:
65  std::atomic<void *> m_handle { nullptr };
66  };
67 
72  {
73  Q_OBJECT
74 
75  public:
77  ~CWorkerBase() override;
78 
80  CWorkerBase(const CWorkerBase &) = delete;
81 
83  CWorkerBase &operator=(const CWorkerBase &) = delete;
84 
86  CWorkerBase(CWorkerBase &&) = delete;
87 
90 
92  static const QStringList &getLogCategories();
93 
96  template <typename T, typename F>
97  void then(T *context, F functor)
98  {
99  Q_ASSERT(context->thread() == QThread::currentThread());
100  QMutexLocker lock(&m_finishedMutex);
101  connect(this, &CWorkerBase::finished, context, functor);
102  if (m_finished) { private_ns::invokeSlot(functor, context); }
103  }
104 
107  template <typename F>
108  void then(F functor)
109  {
110  QMutexLocker lock(&m_finishedMutex);
111  connect(this, &CWorkerBase::finished, functor);
112  if (m_finished) { functor(); }
113  }
114 
117  bool isFinished() const
118  {
119  QMutexLocker lock(&m_finishedMutex);
120  return m_finished;
121  }
122 
125  template <typename F>
126  void doIfFinished(F functor) const
127  {
128  QMutexLocker lock(&m_finishedMutex);
129  if (m_finished) { functor(); }
130  }
131 
134  template <typename F>
135  void doIfNotFinished(F functor) const
136  {
137  QMutexLocker lock(&m_finishedMutex);
138  if (!m_finished) { functor(); }
139  }
140 
143  template <typename F1, typename F2>
144  void doIfFinishedElse(F1 ifFunctor, F2 elseFunctor) const
145  {
146  QMutexLocker lock(&m_finishedMutex);
147  if (m_finished) { ifFunctor(); }
148  else { elseFunctor(); }
149  }
150 
153  void waitForFinished() noexcept;
154 
157  void abandon() noexcept;
158 
160  void abandonAndWait() noexcept;
161 
163  static const QSet<CWorkerBase *> &allWorkers() { return s_allWorkers; }
164 
165  signals:
167  void aboutToStart();
168 
171  void finished();
172 
173  protected:
175  CWorkerBase();
176 
179  bool isAbandoned() const;
180 
182  bool hasStarted() const { return m_started; }
183 
185  void setStarted() { m_started = true; }
186 
188  void setFinished()
189  {
190  QMutexLocker lock(&m_finishedMutex);
191  m_finished = true;
192  emit finished();
193  }
194 
195  private:
196  virtual void quit() noexcept {}
197  virtual void quitAndWait() noexcept { waitForFinished(); }
198 
199  bool m_started = false;
200  bool m_finished = false;
201  mutable QRecursiveMutex m_finishedMutex;
202  static QSet<CWorkerBase *> s_allWorkers;
203  };
204 
211  class SWIFT_MISC_EXPORT CWorker final : public CWorkerBase
212  {
213  Q_OBJECT
214 
215  public:
224  template <typename F>
225  static CWorker *fromTask(QObject *owner, const QString &name, F &&task)
226  {
227  // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
228  int typeId = qMetaTypeId<std::decay_t<decltype(std::forward<F>(task)())>>();
229  return fromTaskImpl(owner, name, typeId, [task = std::forward<F>(task)]() mutable {
230  if constexpr (std::is_void_v<decltype(task())>)
231  {
232  std::move(task)();
233  return QVariant();
234  }
235  else { return QVariant::fromValue(std::move(task)()); }
236  });
237  }
238 
242  template <typename R, typename F>
243  void thenWithResult(F functor)
244  {
245  Q_ASSERT_X(m_result.canConvert<R>(), Q_FUNC_INFO, "Type in thenWithResult must match return type of task");
246  then([this, functor]() { functor(this->resultNoWait<R>()); });
247  }
248 
252  template <typename R, typename T, typename F>
253  void thenWithResult(T *context, F functor)
254  {
255  Q_ASSERT_X(m_result.canConvert<R>(), Q_FUNC_INFO, "Type in thenWithResult must match return type of task");
256 
257  // MS 2018-10 It is possible that a queued finished() signal will be delivered after the worker was
258  // destroyed, so we can't refer to the this pointer inside the contextual then() lambda.
259  // Therefore we use a promise to extract the result from a non-contextual then(). See T414.
260  auto promise = std::make_shared<std::promise<R>>();
261  auto future = promise->get_future().share();
262  then([this, promise]() { promise->set_value(this->resultNoWait<R>()); });
263  then(context, [context, functor, future]() { private_ns::invokeSlot(functor, context, future.get()); });
264  }
265 
269  template <typename R>
270  R result()
271  {
272  waitForFinished();
273  return this->resultNoWait<R>();
274  }
275 
276  private slots:
278  void ps_runTask();
279 
280  private:
281  CWorker(const std::function<QVariant()> &task) : m_task(task) {}
282  static CWorker *fromTaskImpl(QObject *owner, const QString &name, int typeId,
283  const std::function<QVariant()> &task);
284 
285  template <typename R>
286  R resultNoWait()
287  {
288  Q_ASSERT(m_result.canConvert<R>());
289  return m_result.value<R>();
290  }
291 
292  std::function<QVariant()> m_task;
293  QVariant m_result;
294  };
295 
300  {
301  Q_OBJECT
302 
303  public:
309  CContinuousWorker(QObject *owner, const QString &name);
310 
312  void start(QThread::Priority priority = QThread::InheritPriority);
313 
317  void quit() noexcept final;
318 
321  void quitAndWait() noexcept final;
322 
325  bool isEnabled() const { return m_enabled; }
326 
328  const QString &getName() { return m_name; }
329 
331  const QObject *owner() const { return m_owner; }
332 
333  protected:
335  virtual void initialize() {}
336 
338  virtual void cleanup() {}
339 
342  virtual void beforeQuit() noexcept {}
343 
345  virtual unsigned long waitTimeoutMs() const { return 15 * 1000; }
346 
347  private:
349  void finish();
350 
353  void setEnabled(bool enabled) { m_enabled = enabled; }
354 
357 
358  QObject *m_owner = nullptr;
359  QString m_name;
360  std::atomic<bool> m_enabled { true };
361  };
362 } // namespace swift::misc
363 
364 #endif // SWIFT_MISC_WORKER_H
Base class for a long-lived worker object which lives in its own thread.
Definition: worker.h:300
virtual void beforeQuit() noexcept
Called before quit is called.
Definition: worker.h:342
bool isEnabled() const
Enabled (running)?
Definition: worker.h:325
const QObject * owner() const
Owner of the worker.
Definition: worker.h:331
virtual void cleanup()
Called when the thread is finished.
Definition: worker.h:338
virtual void initialize()
Called when the thread is started.
Definition: worker.h:335
virtual unsigned long waitTimeoutMs() const
Wait time for quitAndWait, 0 means not waiting.
Definition: worker.h:345
const QString & getName()
Name of the worker.
Definition: worker.h:328
Just a subclass of QThread whose destructor waits for the thread to finish.
Definition: worker.h:38
CRegularThread(QObject *parent=nullptr)
Constructor.
Definition: worker.h:43
CRegularThread(CRegularThread &&)=delete
Move constructor.
CRegularThread(const CRegularThread &)=delete
Copy constructor.
CRegularThread & operator=(CRegularThread &&)=delete
Move assignment.
CRegularThread & operator=(const CRegularThread &)=delete
Copy assignment.
Base class for CWorker and CContinuousWorker.
Definition: worker.h:72
CWorkerBase & operator=(CWorkerBase &&)=delete
Move assignment.
void doIfFinished(F functor) const
Executes some code (in the caller's thread) if the task has finished.
Definition: worker.h:126
void aboutToStart()
Emitted when the task is about to start.
void then(T *context, F functor)
Connects to a functor or method which will be called when the task is finished.
Definition: worker.h:97
void setStarted()
Mark the task as started.
Definition: worker.h:185
CWorkerBase(const CWorkerBase &)=delete
Copy constructor.
CWorkerBase(CWorkerBase &&)=delete
Move constructor.
CWorkerBase & operator=(const CWorkerBase &)=delete
Copy assignment.
void finished()
Emitted when the task is finished.
void then(F functor)
Connects to a functor which will be called when the task is finished.
Definition: worker.h:108
bool isFinished() const
Returns true if the task has finished.
Definition: worker.h:117
void doIfNotFinished(F functor) const
Executes some code (in the caller's thread) if the task has not finished.
Definition: worker.h:135
bool hasStarted() const
True if the worker has started.
Definition: worker.h:182
void doIfFinishedElse(F1 ifFunctor, F2 elseFunctor) const
Executes some code (in the caller's thread) if the task has finished and some different code if it ha...
Definition: worker.h:144
void setFinished()
Mark the task as finished.
Definition: worker.h:188
Class for doing some arbitrary parcel of work in its own thread.
Definition: worker.h:212
static CWorker * fromTask(QObject *owner, const QString &name, F &&task)
Returns a new worker object which lives in a new thread.
Definition: worker.h:225
R result()
Returns the result of the task, waiting for it to finish if necessary.
Definition: worker.h:270
void thenWithResult(T *context, F functor)
Connects to a functor or method to which will be passed the result when the task is finished.
Definition: worker.h:253
void thenWithResult(F functor)
Connects to a functor to which will be passed the result when the task is finished.
Definition: worker.h:243
Free functions in swift::misc.
QThread * currentThread()
QVariant fromValue(T &&value)
#define SWIFT_MISC_EXPORT
Export a class or function from the library.