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 : public QThread
38  {
39  Q_OBJECT
40 
41  public:
43  CRegularThread(QObject *parent = nullptr) : QThread(parent) {}
44 
46  ~CRegularThread() override;
47 
48  protected:
50  void run() override;
51 
52  private:
53  std::atomic<void *> m_handle { nullptr };
54  };
55 
59  class SWIFT_MISC_EXPORT CWorkerBase : public QObject
60  {
61  Q_OBJECT
62 
63  public:
65  static const QStringList &getLogCategories();
66 
69  template <typename T, typename F>
70  void then(T *context, F functor)
71  {
72  Q_ASSERT(context->thread() == QThread::currentThread());
73  QMutexLocker lock(&m_finishedMutex);
74  connect(this, &CWorkerBase::finished, context, functor);
75  if (m_finished) { private_ns::invokeSlot(functor, context); }
76  }
77 
80  template <typename F>
81  void then(F functor)
82  {
83  QMutexLocker lock(&m_finishedMutex);
84  connect(this, &CWorkerBase::finished, functor);
85  if (m_finished) { functor(); }
86  }
87 
90  bool isFinished() const
91  {
92  QMutexLocker lock(&m_finishedMutex);
93  return m_finished;
94  }
95 
98  template <typename F>
99  void doIfFinished(F functor) const
100  {
101  QMutexLocker lock(&m_finishedMutex);
102  if (m_finished) { functor(); }
103  }
104 
107  template <typename F>
108  void doIfNotFinished(F functor) const
109  {
110  QMutexLocker lock(&m_finishedMutex);
111  if (!m_finished) { functor(); }
112  }
113 
116  template <typename F1, typename F2>
117  void doIfFinishedElse(F1 ifFunctor, F2 elseFunctor) const
118  {
119  QMutexLocker lock(&m_finishedMutex);
120  if (m_finished) { ifFunctor(); }
121  else { elseFunctor(); }
122  }
123 
126  void waitForFinished() noexcept;
127 
130  void abandon() noexcept;
131 
133  void abandonAndWait() noexcept;
134 
136  static const QSet<CWorkerBase *> &allWorkers() { return s_allWorkers; }
137 
138  signals:
140  void aboutToStart();
141 
144  void finished();
145 
146  protected:
148  CWorkerBase();
149 
151  ~CWorkerBase() override;
152 
155  bool isAbandoned() const;
156 
158  bool hasStarted() const { return m_started; }
159 
161  void setStarted() { m_started = true; }
162 
164  void setFinished()
165  {
166  QMutexLocker lock(&m_finishedMutex);
167  m_finished = true;
168  emit finished();
169  }
170 
171  private:
172  virtual void quit() noexcept {}
173  virtual void quitAndWait() noexcept { waitForFinished(); }
174 
175  bool m_started = false;
176  bool m_finished = false;
177  mutable QRecursiveMutex m_finishedMutex;
178  static QSet<CWorkerBase *> s_allWorkers;
179  };
180 
187  class SWIFT_MISC_EXPORT CWorker final : public CWorkerBase
188  {
189  Q_OBJECT
190 
191  public:
200  template <typename F>
201  static CWorker *fromTask(QObject *owner, const QString &name, F &&task)
202  {
203  int typeId = qMetaTypeId<std::decay_t<decltype(std::forward<F>(task)())>>();
204  return fromTaskImpl(owner, name, typeId, [task = std::forward<F>(task)]() mutable {
205  if constexpr (std::is_void_v<decltype(task())>)
206  {
207  std::move(task)();
208  return QVariant();
209  }
210  else { return QVariant::fromValue(std::move(task)()); }
211  });
212  }
213 
217  template <typename R, typename F>
218  void thenWithResult(F functor)
219  {
220  Q_ASSERT_X(m_result.canConvert<R>(), Q_FUNC_INFO, "Type in thenWithResult must match return type of task");
221  then([this, functor]() { functor(this->resultNoWait<R>()); });
222  }
223 
227  template <typename R, typename T, typename F>
228  void thenWithResult(T *context, F functor)
229  {
230  Q_ASSERT_X(m_result.canConvert<R>(), Q_FUNC_INFO, "Type in thenWithResult must match return type of task");
231 
232  // MS 2018-10 It is possible that a queued finished() signal will be delivered after the worker was
233  // destroyed, so we can't refer to the this pointer inside the contextual then() lambda.
234  // Therefore we use a promise to extract the result from a non-contextual then(). See T414.
235  auto promise = std::make_shared<std::promise<R>>();
236  auto future = promise->get_future().share();
237  then([this, promise]() { promise->set_value(this->resultNoWait<R>()); });
238  then(context, [context, functor, future]() { private_ns::invokeSlot(functor, context, future.get()); });
239  }
240 
244  template <typename R>
245  R result()
246  {
247  waitForFinished();
248  return this->resultNoWait<R>();
249  }
250 
251  private slots:
253  void ps_runTask();
254 
255  private:
256  CWorker(const std::function<QVariant()> &task) : m_task(task) {}
257  static CWorker *fromTaskImpl(QObject *owner, const QString &name, int typeId,
258  const std::function<QVariant()> &task);
259 
260  template <typename R>
261  R resultNoWait()
262  {
263  Q_ASSERT(m_result.canConvert<R>());
264  return m_result.value<R>();
265  }
266 
267  std::function<QVariant()> m_task;
268  QVariant m_result;
269  };
270 
275  {
276  Q_OBJECT
277 
278  public:
284  CContinuousWorker(QObject *owner, const QString &name);
285 
287  void start(QThread::Priority priority = QThread::InheritPriority);
288 
292  void quit() noexcept final;
293 
296  void quitAndWait() noexcept final;
297 
300  bool isEnabled() const { return m_enabled; }
301 
304  void setEnabled(bool enabled) { m_enabled = enabled; }
305 
308  void startUpdating(int updateTimeSecs);
309 
311  const QString &getName() { return m_name; }
312 
314  const QObject *owner() const { return m_owner; }
315 
316  protected:
318  virtual void initialize() {}
319 
321  virtual void cleanup() {}
322 
325  virtual void beforeQuit() noexcept {}
326 
328  virtual unsigned long waitTimeoutMs() const { return 15 * 1000; }
329 
331  void stopUpdateTimer();
332 
333  QTimer m_updateTimer { this };
334 
335  private:
337  void finish();
338 
342 
343  QObject *m_owner = nullptr;
344  QString m_name;
345  std::atomic<bool> m_enabled { true };
346  };
347 } // namespace swift::misc
348 
349 #endif // SWIFT_MISC_WORKER_H
Base class for a long-lived worker object which lives in its own thread.
Definition: worker.h:275
virtual void beforeQuit() noexcept
Called before quit is called.
Definition: worker.h:325
bool isEnabled() const
Enabled (running)?
Definition: worker.h:300
const QObject * owner() const
Owner of the worker.
Definition: worker.h:314
void setEnabled(bool enabled)
Enabled (running)?
Definition: worker.h:304
virtual void cleanup()
Called when the thread is finished.
Definition: worker.h:321
virtual void initialize()
Called when the thread is started.
Definition: worker.h:318
virtual unsigned long waitTimeoutMs() const
Wait time for quitAndWait, 0 means not waiting.
Definition: worker.h:328
const QString & getName()
Name of the worker.
Definition: worker.h:311
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
Base class for CWorker and CContinuousWorker.
Definition: worker.h:60
void doIfFinished(F functor) const
Executes some code (in the caller's thread) if the task has finished.
Definition: worker.h:99
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:70
void setStarted()
Mark the task as started.
Definition: worker.h:161
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:81
bool isFinished() const
Returns true if the task has finished.
Definition: worker.h:90
void doIfNotFinished(F functor) const
Executes some code (in the caller's thread) if the task has not finished.
Definition: worker.h:108
bool hasStarted() const
True if the worker has started.
Definition: worker.h:158
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:117
void setFinished()
Mark the task as finished.
Definition: worker.h:164
Class for doing some arbitrary parcel of work in its own thread.
Definition: worker.h:188
static CWorker * fromTask(QObject *owner, const QString &name, F &&task)
Returns a new worker object which lives in a new thread.
Definition: worker.h:201
R result()
Returns the result of the task, waiting for it to finish if necessary.
Definition: worker.h:245
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:228
void thenWithResult(F functor)
Connects to a functor to which will be passed the result when the task is finished.
Definition: worker.h:218
Free functions in swift::misc.
#define SWIFT_MISC_EXPORT
Export a class or function from the library.