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 {
37  class SWIFT_MISC_EXPORT CRegularThread final : public QThread
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 
71  class SWIFT_MISC_EXPORT CWorkerBase : public QObject
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  int typeId = qMetaTypeId<std::decay_t<decltype(std::forward<F>(task)())>>();
228  return fromTaskImpl(owner, name, typeId, [task = std::forward<F>(task)]() mutable {
229  if constexpr (std::is_void_v<decltype(task())>)
230  {
231  std::move(task)();
232  return QVariant();
233  }
234  else { return QVariant::fromValue(std::move(task)()); }
235  });
236  }
237 
241  template <typename R, typename F>
242  void thenWithResult(F functor)
243  {
244  Q_ASSERT_X(m_result.canConvert<R>(), Q_FUNC_INFO, "Type in thenWithResult must match return type of task");
245  then([this, functor]() { functor(this->resultNoWait<R>()); });
246  }
247 
251  template <typename R, typename T, typename F>
252  void thenWithResult(T *context, F functor)
253  {
254  Q_ASSERT_X(m_result.canConvert<R>(), Q_FUNC_INFO, "Type in thenWithResult must match return type of task");
255 
256  // MS 2018-10 It is possible that a queued finished() signal will be delivered after the worker was
257  // destroyed, so we can't refer to the this pointer inside the contextual then() lambda.
258  // Therefore we use a promise to extract the result from a non-contextual then(). See T414.
259  auto promise = std::make_shared<std::promise<R>>();
260  auto future = promise->get_future().share();
261  then([this, promise]() { promise->set_value(this->resultNoWait<R>()); });
262  then(context, [context, functor, future]() { private_ns::invokeSlot(functor, context, future.get()); });
263  }
264 
268  template <typename R>
269  R result()
270  {
271  waitForFinished();
272  return this->resultNoWait<R>();
273  }
274 
275  private slots:
277  void ps_runTask();
278 
279  private:
280  CWorker(const std::function<QVariant()> &task) : m_task(task) {}
281  static CWorker *fromTaskImpl(QObject *owner, const QString &name, int typeId,
282  const std::function<QVariant()> &task);
283 
284  template <typename R>
285  R resultNoWait()
286  {
287  Q_ASSERT(m_result.canConvert<R>());
288  return m_result.value<R>();
289  }
290 
291  std::function<QVariant()> m_task;
292  QVariant m_result;
293  };
294 
299  {
300  Q_OBJECT
301 
302  public:
308  CContinuousWorker(QObject *owner, const QString &name);
309 
311  void start(QThread::Priority priority = QThread::InheritPriority);
312 
316  void quit() noexcept final;
317 
320  void quitAndWait() noexcept final;
321 
324  bool isEnabled() const { return m_enabled; }
325 
327  const QString &getName() { return m_name; }
328 
330  const QObject *owner() const { return m_owner; }
331 
332  protected:
334  virtual void initialize() {}
335 
337  virtual void cleanup() {}
338 
341  virtual void beforeQuit() noexcept {}
342 
344  virtual unsigned long waitTimeoutMs() const { return 15 * 1000; }
345 
346  private:
348  void finish();
349 
352  void setEnabled(bool enabled) { m_enabled = enabled; }
353 
356 
357  QObject *m_owner = nullptr;
358  QString m_name;
359  std::atomic<bool> m_enabled { true };
360  };
361 } // namespace swift::misc
362 
363 #endif // SWIFT_MISC_WORKER_H
Base class for a long-lived worker object which lives in its own thread.
Definition: worker.h:299
virtual void beforeQuit() noexcept
Called before quit is called.
Definition: worker.h:341
bool isEnabled() const
Enabled (running)?
Definition: worker.h:324
const QObject * owner() const
Owner of the worker.
Definition: worker.h:330
virtual void cleanup()
Called when the thread is finished.
Definition: worker.h:337
virtual void initialize()
Called when the thread is started.
Definition: worker.h:334
virtual unsigned long waitTimeoutMs() const
Wait time for quitAndWait, 0 means not waiting.
Definition: worker.h:344
const QString & getName()
Name of the worker.
Definition: worker.h:327
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:269
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:252
void thenWithResult(F functor)
Connects to a functor to which will be passed the result when the task is finished.
Definition: worker.h:242
Free functions in swift::misc.
#define SWIFT_MISC_EXPORT
Export a class or function from the library.