KASync

future.h
1/*
2 SPDX-FileCopyrightText: 2014 Daniel Vrátil <dvratil@redhat.com>
3 SPDX-FileCopyrightText: 2016 Daniel Vrátil <dvratil@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#ifndef FUTURE_H
9#define FUTURE_H
10
11#include "kasync_export.h"
12
13class QEventLoop;
14
15#include <type_traits>
16
17#include <QSharedDataPointer>
18#include <QPointer>
19#include <QVector>
20#include <QEventLoop>
21
22namespace KAsync {
23
24//@cond PRIVATE
25
26class FutureWatcherBase;
27template<typename T>
28class FutureWatcher;
29
30namespace Private {
31struct Execution;
32class ExecutorBase;
33
34typedef QSharedPointer<Execution> ExecutionPtr;
35} // namespace Private
36
37struct KASYNC_EXPORT Error
38{
39 Error() : errorCode(0) {};
40 explicit Error(const char *message) : errorCode(1), errorMessage(QString::fromLatin1(message)) {}
41 Error(int code, const char *message) : errorCode(code), errorMessage(QString::fromLatin1(message)) {}
42 Error(int code, const QString &message) : errorCode(code), errorMessage(message) {}
43
44 bool operator ==(const Error &other) const {
45 return (errorCode == other.errorCode) && (errorMessage == other.errorMessage);
46 }
47
48 bool operator !=(const Error &other) const {
49 return !(*this == other);
50 }
51
52 operator bool() const {
53 return (errorCode != 0);
54 }
55
56 int errorCode;
58private:
59 //Disable all implicit conversions except to bool, to avoid accidentally implicitly casting an error to a continuation argument.
60 //This becomes an issue if you forget to specify all template arguments, as the template argument deduction may employ a nonsensical implicit conversion from i.e. error to int. So as long as the Error object is used in the Job::then overload resolution no implicit conversions here.
61 //Of course this "solution" still breaks if you forget the template argument with a boolean parameter....
62 template <typename T>
63 operator T() const;
64};
65
66class KASYNC_EXPORT FutureBase
67{
68 friend struct KAsync::Private::Execution;
69 friend class FutureWatcherBase;
70
71public:
72 virtual ~FutureBase();
73
74
75 void setFinished();
76 bool isFinished() const;
77
78 void setError(int code = 1, const QString &message = QString());
79 void setError(const Error &error);
80 void addError(const Error &error);
81 void clearErrors();
82 bool hasError() const;
83 int errorCode() const;
84 QString errorMessage() const;
85 QVector<Error> errors() const;
86
87 void setProgress(qreal progress);
88 void setProgress(int processed, int total);
89
90protected:
91 class KASYNC_EXPORT PrivateBase : public QSharedData
92 {
93 public:
94 explicit PrivateBase(const KAsync::Private::ExecutionPtr &execution);
95 virtual ~PrivateBase();
96
97 void releaseExecution();
98
99 bool finished;
100 QVector<Error> errors;
101
103 private:
105 };
106
107 explicit FutureBase();
108 explicit FutureBase(FutureBase::PrivateBase *dd);
109 FutureBase(const FutureBase &other);
110 FutureBase &operator=(const FutureBase &other) = default;
111
112 void addWatcher(KAsync::FutureWatcherBase *watcher);
113 void releaseExecution();
114
115protected:
117};
118
119template<typename T>
120class FutureWatcher;
121
122template<typename T>
123class Future;
124
125template<typename T>
126class FutureGeneric : public FutureBase
127{
128 friend class FutureWatcher<T>;
129
130public:
131
132 void waitForFinished() const
133 {
134 if (isFinished()) {
135 return;
136 }
137 FutureWatcher<T> watcher;
138 QEventLoop eventLoop;
139 QObject::connect(&watcher, &KAsync::FutureWatcher<T>::futureReady,
140 &eventLoop, &QEventLoop::quit);
141 watcher.setFuture(*static_cast<const KAsync::Future<T>*>(this));
142 eventLoop.exec();
143 }
144
145protected:
146 //@cond PRIVATE
147 explicit FutureGeneric(const KAsync::Private::ExecutionPtr &execution)
148 : FutureBase(new Private(execution))
149 {}
150
151 FutureGeneric(const FutureGeneric &) = default;
152 FutureGeneric &operator=(const FutureGeneric &) = default;
153
154protected:
155 class Private : public FutureBase::PrivateBase
156 {
157 public:
158 explicit Private(const KAsync::Private::ExecutionPtr &execution)
159 : FutureBase::PrivateBase(execution)
160 {}
161
162 std::conditional_t<std::is_void<T>::value, int /* dummy */, T> value;
163 };
164};
165//@endcond
166
167
168
169/**
170 * @ingroup Future
171 *
172 * @brief Future is a promise that is used by Job to deliver result
173 * of an asynchronous execution.
174 *
175 * The Future is passed internally to each executed task, and the task can use
176 * it to report its progress, result and notify when it is finished.
177 *
178 * Users use Future they receive from calling Job::exec() to get access
179 * to the overall result of the execution. FutureWatcher&lt;T&gt; can be used
180 * to wait for the Future to finish in non-blocking manner.
181 *
182 * @see Future<void>
183 */
184template<typename T>
185class Future : public FutureGeneric<T>
186{
187 //@cond PRIVATE
188 friend class KAsync::Private::ExecutorBase;
189
190 template<typename T_>
191 friend class KAsync::FutureWatcher;
192 //@endcond
193public:
194 /**
195 * @brief Constructor
196 */
197 explicit Future()
198 : FutureGeneric<T>(KAsync::Private::ExecutionPtr())
199 {}
200
201 /**
202 * @brief Copy constructor
203 */
204 Future(const Future<T> &other)
205 : FutureGeneric<T>(other)
206 {}
207
208 /**
209 * Set the result of the Future. This method is called by the task upon
210 * calculating the result. After setting the value, the caller must also
211 * call setFinished() to notify users that the result
212 * is available.
213 *
214 * @warning This method must only be called by the tasks inside Job,
215 * never by outside users.
216 *
217 * @param value The result value
218 */
219 void setValue(const T &value)
220 {
221 dataImpl()->value = value;
222 }
223
224 /**
225 * Retrieve the result of the Future. Calling this method when the future has
226 * not yet finished (i.e. isFinished() returns false)
227 * returns undefined result.
228 */
229 T value() const
230 {
231 return dataImpl()->value;
232 }
233
234 T *operator->()
235 {
236 return &(dataImpl()->value);
237 }
238
239 const T *operator->() const
240 {
241 return &(dataImpl()->value);
242 }
243
244 T &operator*()
245 {
246 return dataImpl()->value;
247 }
248
249 const T &operator*() const
250 {
251 return dataImpl()->value;
252 }
253
254#ifdef ONLY_DOXYGEN
255 /**
256 * Will block until the Future has finished.
257 *
258 * @note Internally this method is using a nested QEventLoop, which can
259 * in some situation cause problems and deadlocks. It is recommended to use
260 * FutureWatcher.
261 *
262 * @see isFinished()
263 */
264 void waitForFinished() const;
265
266 /**
267 * Marks the future as finished. This will cause all FutureWatcher&lt;T&gt;
268 * objects watching this particular instance to emit FutureWatcher::futureReady()
269 * signal, and will cause all callers currently blocked in Future::waitForFinished()
270 * method of this particular instance to resume.
271 *
272 * @warning This method must only be called by the tasks inside Job,
273 * never by outside users.
274 *
275 * @see isFinished()
276 */
277 void setFinished();
278
279 /**
280 * Query whether the Future has already finished.
281 *
282 * @see setFinished()
283 */
284 bool isFinished() const;
285
286 /**
287 * Used by tasks to report an error that happened during execution. If an
288 * error handler was provided to the task, it will be executed with the
289 * given arguments. Otherwise the error will be propagated to next task
290 * that has an error handler, or all the way up to user.
291 *
292 * This method also internally calls setFinished()
293 *
294 * @warning This method must only be called by the tasks inside Job,
295 * never by outside users.
296 *
297 * @param code Optional error code
298 * @param message Optional error message
299 *
300 * @see errorCode(), errorMessage()
301 */
302 void setError(int code = 1, const QString &message = QString());
303
304 /**
305 * Returns error code set via setError() or 0 if no
306 * error has occurred.
307 *
308 * @see setError(), errorMessage()
309 */
310 int errorCode() const;
311
312 /**
313 * Returns error message set via setError() or empty
314 * string if no error occurred.
315 *
316 * @see setError(), errorCode()
317 */
318 QString errorMessage() const;
319
320 /**
321 * Sets progress of the task. All FutureWatcher instances watching
322 * this particular future will then emit FutureWatcher::futureProgress()
323 * signal.
324 *
325 * @param processed Already processed amount
326 * @param total Total amount to process
327 */
328 void setProgress(int processed, int total);
329
330 /**
331 * Sets progress of the task.
332 *
333 * @param progress Progress
334 */
335 void setProgress(qreal progress);
336
337#endif // ONLY_DOXYGEN
338 void setResult(const T &value)
339 {
340 dataImpl()->value = value;
341 FutureBase::setFinished();
342 }
343
344protected:
345 //@cond PRIVATE
346 Future(const KAsync::Private::ExecutionPtr &execution)
347 : FutureGeneric<T>(execution)
348 {}
349 //@endcond
350
351private:
352 inline auto dataImpl()
353 {
354 return static_cast<typename FutureGeneric<T>::Private*>(this->d.data());
355 }
356
357 inline auto dataImpl() const
358 {
359 return static_cast<typename FutureGeneric<T>::Private*>(this->d.data());
360 }
361};
362
363/**
364 * @ingroup Future
365 *
366 * @brief A specialization of Future&lt;T&gt; for tasks that have no (void)
367 * result.
368 *
369 * Unlike the generic Future&lt;T&gt; this specialization does not have
370 * setValue() and value() methods to set/retrieve result.
371 *
372 * @see Future
373 */
374template<>
375class Future<void> : public FutureGeneric<void>
376{
377 friend class KAsync::Private::ExecutorBase;
378
379public:
380 /**
381 * @brief Constructor
382 */
383 Future()
384 : FutureGeneric<void>(KAsync::Private::ExecutionPtr())
385 {}
386
387protected:
388 //@cond PRIVATE
389 Future(const KAsync::Private::ExecutionPtr &execution)
390 : FutureGeneric<void>(execution)
391 {}
392 //@endcond
393};
394
395
396
397
398//@cond PRIVATE
399class KASYNC_EXPORT FutureWatcherBase : public QObject
400{
401 Q_OBJECT
402
403 friend class FutureBase;
404
405Q_SIGNALS:
406 void futureReady();
407 void futureProgress(qreal progress);
408
409protected:
410 FutureWatcherBase(QObject *parent = nullptr);
411
412 virtual ~FutureWatcherBase();
413
414 void futureReadyCallback();
415 void futureProgressCallback(qreal progress);
416
417 void setFutureImpl(const KAsync::FutureBase &future);
418
419protected:
420 class Private {
421 public:
422 KAsync::FutureBase future;
423 };
424
425 Private * const d;
426
427private:
428 Q_DISABLE_COPY(FutureWatcherBase)
429};
430//@endcond
431
432
433/**
434 * @ingroup Future
435 *
436 * @brief The FutureWatcher allows monitoring of Job results using
437 * signals and slots.
438 *
439 * FutureWatcher is returned by Job upon execution. User can then
440 * connect to its futureReady() and futureProgress() signals to be notified
441 * about progress of the asynchronous job. When futureReady() signal is emitted,
442 * the result of the job is available in Future::value().
443 */
444template<typename T>
445class FutureWatcher : public FutureWatcherBase
446{
447 //@cond PRIVATE
448 friend class KAsync::FutureGeneric<T>;
449 //@endcond
450
451public:
452 /**
453 * Constructs a new FutureWatcher that can watch for status of Future&lt;T&gt;
454 */
455 FutureWatcher(QObject *parent = nullptr)
456 : FutureWatcherBase(parent)
457 {}
458
459 ~FutureWatcher()
460 {}
461
462 /**
463 * Set future to watch.
464 *
465 * @param future Future object to watch
466 */
467 void setFuture(const KAsync::Future<T> &future)
468 {
469 setFutureImpl(*static_cast<const KAsync::FutureBase*>(&future));
470 }
471
472 /**
473 * Returns currently watched future.
474 */
475 KAsync::Future<T> future() const
476 {
477 return *static_cast<KAsync::Future<T>*>(&d->future);
478 }
479
480#ifdef ONLY_DOXYGEN
481Q_SIGNALS:
482 /**
483 * The signal is emitted when the execution has finished and the result
484 * can be collected.
485 *
486 * @see Future::setFinished(), Future::setError()
487 */
488 void futureReady();
489
490 /**
491 * The signal is emitted when progress of the execution changes. This has
492 * to be explicitly supported by the job being executed, otherwise the
493 * signal is not emitted.
494 *
495 * @see Future::setProgress()
496 */
497 void futureProgress(qreal progress);
498#endif
499
500private:
501 FutureWatcher(const FutureWatcher<T> &) = delete;
502 FutureWatcher &operator=(const FutureWatcher<T> &) = delete;
503};
504
505} // namespace Async
506
507KASYNC_EXPORT QDebug& operator<<(QDebug &dbg, const KAsync::Error &error);
508
509#endif // FUTURE_H
KCALUTILS_EXPORT QString errorMessage(const KCalendarCore::Exception &exception)
QDebug operator<<(QDebug dbg, const PerceptualColor::MultiSpinBoxSection &value)
int exec(ProcessEventsFlags flags)
void quit()
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:10:09 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.