summaryrefslogtreecommitdiffstats
path: root/async/src
diff options
context:
space:
mode:
Diffstat (limited to 'async/src')
-rw-r--r--async/src/CMakeLists.txt1
-rw-r--r--async/src/async.cpp52
-rw-r--r--async/src/async.h224
-rw-r--r--async/src/debug.cpp73
-rw-r--r--async/src/debug.h80
-rw-r--r--async/src/future.cpp112
-rw-r--r--async/src/future.h145
7 files changed, 497 insertions, 190 deletions
diff --git a/async/src/CMakeLists.txt b/async/src/CMakeLists.txt
index 6f8ab63..becc8ee 100644
--- a/async/src/CMakeLists.txt
+++ b/async/src/CMakeLists.txt
@@ -5,6 +5,7 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR})
5set(async_SRCS 5set(async_SRCS
6 async.cpp 6 async.cpp
7 future.cpp 7 future.cpp
8 debug.cpp
8) 9)
9 10
10add_library(${PROJECT_NAME} SHARED ${async_SRCS}) 11add_library(${PROJECT_NAME} SHARED ${async_SRCS})
diff --git a/async/src/async.cpp b/async/src/async.cpp
index 5e26bd8..e92d333 100644
--- a/async/src/async.cpp
+++ b/async/src/async.cpp
@@ -21,9 +21,57 @@
21#include <QDebug> 21#include <QDebug>
22#include <QEventLoop> 22#include <QEventLoop>
23 23
24
25using namespace Async; 24using namespace Async;
26 25
26Private::Execution::Execution(const Private::ExecutorBasePtr &executor)
27 : executor(executor)
28 , resultBase(nullptr)
29 , isRunning(false)
30 , isFinished(false)
31{
32}
33
34Private::Execution::~Execution()
35{
36 if (resultBase) {
37 resultBase->releaseExecution();
38 delete resultBase;
39 }
40 prevExecution.reset();
41}
42
43void Private::Execution::setFinished()
44{
45 isFinished = true;
46 //executor.clear();
47#ifndef QT_NO_DEBUG
48 if (tracer) {
49 delete tracer;
50 }
51#endif
52}
53
54void Private::Execution::releaseFuture()
55{
56 resultBase = 0;
57}
58
59bool Private::Execution::errorWasHandled() const
60{
61 Execution *exec = const_cast<Execution*>(this);
62 while (exec) {
63 if (exec->executor->hasErrorFunc()) {
64 return true;
65 }
66 exec = exec->prevExecution.data();
67 }
68 return false;
69}
70
71
72
73
74
27Private::ExecutorBase::ExecutorBase(const ExecutorBasePtr &parent) 75Private::ExecutorBase::ExecutorBase(const ExecutorBasePtr &parent)
28 : mPrev(parent) 76 : mPrev(parent)
29{ 77{
@@ -34,6 +82,8 @@ Private::ExecutorBase::~ExecutorBase()
34} 82}
35 83
36 84
85
86
37JobBase::JobBase(const Private::ExecutorBasePtr &executor) 87JobBase::JobBase(const Private::ExecutorBasePtr &executor)
38 : mExecutor(executor) 88 : mExecutor(executor)
39{ 89{
diff --git a/async/src/async.h b/async/src/async.h
index c6ca9e7..73eeaa0 100644
--- a/async/src/async.h
+++ b/async/src/async.h
@@ -25,6 +25,7 @@
25#include <iterator> 25#include <iterator>
26 26
27#include "future.h" 27#include "future.h"
28#include "debug.h"
28#include "async_impl.h" 29#include "async_impl.h"
29 30
30#include <QVector> 31#include <QVector>
@@ -46,20 +47,22 @@
46 * that can be stored and executed later on. Jobs can be composed, similarly to functions. 47 * that can be stored and executed later on. Jobs can be composed, similarly to functions.
47 * 48 *
48 * Relations between the components: 49 * Relations between the components:
49 * * Job: description of what should happen 50 * * Job: API wrapper around Executors chain. Can be destroyed while still running,
50 * * Executor: Running execution of a job, the process that calculates the result. 51 * because the actual execution happens in the background
52 * * Executor: Describes task to execute. Executors form a linked list matching the
53 * order in which they will be executed. The Executor chain is destroyed when
54 * the parent Job is destroyed. However if the Job is still running it is
55 * guaranteed that the Executor chain will not be destroyed until the execution
56 * is finished.
57 * * Execution: The running execution of the task stored in Executor. Each call to Job::exec()
58 * instantiates new Execution chain, which makes it possible for the Job to be
59 * executed multiple times (even in parallel).
51 * * Future: Representation of the result that is being calculated 60 * * Future: Representation of the result that is being calculated
52 * 61 *
53 * Lifetime:
54 * * Before a job is executed is treated like a normal value on the stack.
55 * * As soon as the job is executed, a heap allocated executor keeps the task running until complete. The associated future handle remains
56 * valid until the task is complete. To abort a job it has to be killed through the future handle.
57 * TODO: Can we tie the lifetime of the executor to the last available future handle?
58 * 62 *
59 * TODO: Progress reporting through future 63 * TODO: Composed progress reporting
60 * TODO: Possibility to abort a job through future (perhaps optional?) 64 * TODO: Possibility to abort a job through future (perhaps optional?)
61 * TODO: Support for timeout, specified during exec call, after which the error handler gets called with a defined errorCode. 65 * TODO: Support for timeout, specified during exec call, after which the error handler gets called with a defined errorCode.
62 * TODO: Repeated execution of a job to facilitate i.e. an async while loop of a job?
63 */ 66 */
64namespace Async { 67namespace Async {
65 68
@@ -94,38 +97,18 @@ class ExecutorBase;
94typedef QSharedPointer<ExecutorBase> ExecutorBasePtr; 97typedef QSharedPointer<ExecutorBase> ExecutorBasePtr;
95 98
96struct Execution { 99struct Execution {
97 Execution(const ExecutorBasePtr &executor) 100 Execution(const ExecutorBasePtr &executor);
98 : executor(executor) 101 ~Execution();
99 , resultBase(nullptr) 102 void setFinished();
100 , isRunning(false)
101 , isFinished(false)
102 {}
103
104 ~Execution()
105 {
106 if (resultBase) {
107 resultBase->releaseExecution();
108 delete resultBase;
109 }
110 prevExecution.reset();
111 }
112
113 void setFinished()
114 {
115 isFinished = true;
116 executor.clear();
117 }
118 103
119 template<typename T> 104 template<typename T>
120 Async::Future<T>* result() 105 Async::Future<T>* result() const
121 { 106 {
122 return static_cast<Async::Future<T>*>(resultBase); 107 return static_cast<Async::Future<T>*>(resultBase);
123 } 108 }
124 109
125 void releaseFuture() 110 void releaseFuture();
126 { 111 bool errorWasHandled() const;
127 resultBase = 0;
128 }
129 112
130 ExecutorBasePtr executor; 113 ExecutorBasePtr executor;
131 FutureBase *resultBase; 114 FutureBase *resultBase;
@@ -133,8 +116,13 @@ struct Execution {
133 bool isFinished; 116 bool isFinished;
134 117
135 ExecutionPtr prevExecution; 118 ExecutionPtr prevExecution;
119
120#ifndef QT_NO_DEBUG
121 Tracer *tracer;
122#endif
136}; 123};
137 124
125
138typedef QSharedPointer<Execution> ExecutionPtr; 126typedef QSharedPointer<Execution> ExecutionPtr;
139 127
140class ExecutorBase 128class ExecutorBase
@@ -145,6 +133,9 @@ class ExecutorBase
145 template<typename Out, typename ... In> 133 template<typename Out, typename ... In>
146 friend class Async::Job; 134 friend class Async::Job;
147 135
136 friend class Execution;
137 friend class Async::Tracer;
138
148public: 139public:
149 virtual ~ExecutorBase(); 140 virtual ~ExecutorBase();
150 virtual ExecutionPtr exec(const ExecutorBasePtr &self) = 0; 141 virtual ExecutionPtr exec(const ExecutorBasePtr &self) = 0;
@@ -155,21 +146,31 @@ protected:
155 template<typename T> 146 template<typename T>
156 Async::Future<T>* createFuture(const ExecutionPtr &execution) const; 147 Async::Future<T>* createFuture(const ExecutionPtr &execution) const;
157 148
149 virtual bool hasErrorFunc() const = 0;
150 virtual bool handleError(const ExecutionPtr &execution) = 0;
151
158 ExecutorBasePtr mPrev; 152 ExecutorBasePtr mPrev;
153
154#ifndef QT_NO_DEBUG
155 QString mExecutorName;
156#endif
159}; 157};
160 158
161template<typename PrevOut, typename Out, typename ... In> 159template<typename PrevOut, typename Out, typename ... In>
162class Executor : public ExecutorBase 160class Executor : public ExecutorBase
163{ 161{
164protected: 162protected:
165 Executor(ErrorHandler errorHandler, const Private::ExecutorBasePtr &parent) 163 Executor(ErrorHandler errorFunc, const Private::ExecutorBasePtr &parent)
166 : ExecutorBase(parent) 164 : ExecutorBase(parent)
167 , mErrorFunc(errorHandler) 165 , mErrorFunc(errorFunc)
168 {} 166 {}
167
169 virtual ~Executor() {} 168 virtual ~Executor() {}
170 virtual void run(const ExecutionPtr &execution) = 0; 169 virtual void run(const ExecutionPtr &execution) = 0;
171 170
172 ExecutionPtr exec(const ExecutorBasePtr &self); 171 ExecutionPtr exec(const ExecutorBasePtr &self);
172 bool hasErrorFunc() const { return (bool) mErrorFunc; }
173 bool handleError(const ExecutionPtr &execution);
173 174
174 std::function<void(int, const QString &)> mErrorFunc; 175 std::function<void(int, const QString &)> mErrorFunc;
175}; 176};
@@ -178,7 +179,7 @@ template<typename Out, typename ... In>
178class ThenExecutor: public Executor<typename detail::prevOut<In ...>::type, Out, In ...> 179class ThenExecutor: public Executor<typename detail::prevOut<In ...>::type, Out, In ...>
179{ 180{
180public: 181public:
181 ThenExecutor(ThenTask<Out, In ...> then, ErrorHandler errorHandler, const ExecutorBasePtr &parent); 182 ThenExecutor(ThenTask<Out, In ...> then, ErrorHandler errorFunc, const ExecutorBasePtr &parent);
182 void run(const ExecutionPtr &execution); 183 void run(const ExecutionPtr &execution);
183private: 184private:
184 ThenTask<Out, In ...> mFunc; 185 ThenTask<Out, In ...> mFunc;
@@ -188,7 +189,7 @@ template<typename PrevOut, typename Out, typename In>
188class EachExecutor : public Executor<PrevOut, Out, In> 189class EachExecutor : public Executor<PrevOut, Out, In>
189{ 190{
190public: 191public:
191 EachExecutor(EachTask<Out, In> each, ErrorHandler errorHandler, const ExecutorBasePtr &parent); 192 EachExecutor(EachTask<Out, In> each, ErrorHandler errorFunc, const ExecutorBasePtr &parent);
192 void run(const ExecutionPtr &execution); 193 void run(const ExecutionPtr &execution);
193private: 194private:
194 EachTask<Out, In> mFunc; 195 EachTask<Out, In> mFunc;
@@ -199,7 +200,7 @@ template<typename Out, typename In>
199class ReduceExecutor : public ThenExecutor<Out, In> 200class ReduceExecutor : public ThenExecutor<Out, In>
200{ 201{
201public: 202public:
202 ReduceExecutor(ReduceTask<Out, In> reduce, ErrorHandler errorHandler, const ExecutorBasePtr &parent); 203 ReduceExecutor(ReduceTask<Out, In> reduce, ErrorHandler errorFunc, const ExecutorBasePtr &parent);
203private: 204private:
204 ReduceTask<Out, In> mFunc; 205 ReduceTask<Out, In> mFunc;
205}; 206};
@@ -208,7 +209,7 @@ template<typename Out, typename ... In>
208class SyncThenExecutor : public Executor<typename detail::prevOut<In ...>::type, Out, In ...> 209class SyncThenExecutor : public Executor<typename detail::prevOut<In ...>::type, Out, In ...>
209{ 210{
210public: 211public:
211 SyncThenExecutor(SyncThenTask<Out, In ...> then, ErrorHandler errorHandler, const ExecutorBasePtr &parent); 212 SyncThenExecutor(SyncThenTask<Out, In ...> then, ErrorHandler errorFunc, const ExecutorBasePtr &parent);
212 void run(const ExecutionPtr &execution); 213 void run(const ExecutionPtr &execution);
213 214
214private: 215private:
@@ -221,7 +222,7 @@ template<typename Out, typename In>
221class SyncReduceExecutor : public SyncThenExecutor<Out, In> 222class SyncReduceExecutor : public SyncThenExecutor<Out, In>
222{ 223{
223public: 224public:
224 SyncReduceExecutor(SyncReduceTask<Out, In> reduce, ErrorHandler errorHandler, const ExecutorBasePtr &parent); 225 SyncReduceExecutor(SyncReduceTask<Out, In> reduce, ErrorHandler errorFunc, const ExecutorBasePtr &parent);
225private: 226private:
226 SyncReduceTask<Out, In> mFunc; 227 SyncReduceTask<Out, In> mFunc;
227}; 228};
@@ -230,7 +231,7 @@ template<typename PrevOut, typename Out, typename In>
230class SyncEachExecutor : public Executor<PrevOut, Out, In> 231class SyncEachExecutor : public Executor<PrevOut, Out, In>
231{ 232{
232public: 233public:
233 SyncEachExecutor(SyncEachTask<Out, In> each, ErrorHandler errorHandler, const ExecutorBasePtr &parent); 234 SyncEachExecutor(SyncEachTask<Out, In> each, ErrorHandler errorFunc, const ExecutorBasePtr &parent);
234 void run(const ExecutionPtr &execution); 235 void run(const ExecutionPtr &execution);
235private: 236private:
236 void run(Async::Future<Out> *future, const typename PrevOut::value_type &arg, std::false_type); // !std::is_void<Out> 237 void run(Async::Future<Out> *future, const typename PrevOut::value_type &arg, std::false_type); // !std::is_void<Out>
@@ -251,10 +252,10 @@ private:
251 * where @p In is type of the result. 252 * where @p In is type of the result.
252 */ 253 */
253template<typename Out, typename ... In> 254template<typename Out, typename ... In>
254Job<Out, In ...> start(ThenTask<Out, In ...> func); 255Job<Out, In ...> start(ThenTask<Out, In ...> func, ErrorHandler errorFunc = ErrorHandler());
255 256
256template<typename Out, typename ... In> 257template<typename Out, typename ... In>
257Job<Out, In ...> start(SyncThenTask<Out, In ...> func); 258Job<Out, In ...> start(SyncThenTask<Out, In ...> func, ErrorHandler errorFunc = ErrorHandler());
258 259
259#ifdef WITH_KJOB 260#ifdef WITH_KJOB
260template<typename ReturnType, typename KJobType, ReturnType (KJobType::*KJobResultMethod)(), typename ... Args> 261template<typename ReturnType, typename KJobType, ReturnType (KJobType::*KJobResultMethod)(), typename ... Args>
@@ -356,10 +357,10 @@ class Job : public JobBase
356 friend class Job; 357 friend class Job;
357 358
358 template<typename OutOther, typename ... InOther> 359 template<typename OutOther, typename ... InOther>
359 friend Job<OutOther, InOther ...> start(Async::ThenTask<OutOther, InOther ...> func); 360 friend Job<OutOther, InOther ...> start(Async::ThenTask<OutOther, InOther ...> func, ErrorHandler errorFunc);
360 361
361 template<typename OutOther, typename ... InOther> 362 template<typename OutOther, typename ... InOther>
362 friend Job<OutOther, InOther ...> start(Async::SyncThenTask<OutOther, InOther ...> func); 363 friend Job<OutOther, InOther ...> start(Async::SyncThenTask<OutOther, InOther ...> func, ErrorHandler errorFunc);
363 364
364#ifdef WITH_KJOB 365#ifdef WITH_KJOB
365 template<typename ReturnType, typename KJobType, ReturnType (KJobType::*KJobResultMethod)(), typename ... Args> 366 template<typename ReturnType, typename KJobType, ReturnType (KJobType::*KJobResultMethod)(), typename ... Args>
@@ -499,9 +500,14 @@ private:
499 auto job = otherJob; 500 auto job = otherJob;
500 FutureWatcher<OutOther> *watcher = new FutureWatcher<OutOther>(); 501 FutureWatcher<OutOther> *watcher = new FutureWatcher<OutOther>();
501 QObject::connect(watcher, &FutureWatcherBase::futureReady, 502 QObject::connect(watcher, &FutureWatcherBase::futureReady,
502 [watcher, &future]() { 503 [watcher, future]() {
503 Async::detail::copyFutureValue(watcher->future(), future); 504 // FIXME: We pass future by value, because using reference causes the
504 future.setFinished(); 505 // future to get deleted before this lambda is invoked, leading to crash
506 // in copyFutureValue()
507 // copy by value is const
508 auto outFuture = future;
509 Async::detail::copyFutureValue(watcher->future(), outFuture);
510 outFuture.setFinished();
505 delete watcher; 511 delete watcher;
506 }); 512 });
507 watcher->setFuture(job.exec(in ...)); 513 watcher->setFuture(job.exec(in ...));
@@ -517,17 +523,17 @@ private:
517namespace Async { 523namespace Async {
518 524
519template<typename Out, typename ... In> 525template<typename Out, typename ... In>
520Job<Out, In ...> start(ThenTask<Out, In ...> func) 526Job<Out, In ...> start(ThenTask<Out, In ...> func, ErrorHandler error)
521{ 527{
522 return Job<Out, In...>(Private::ExecutorBasePtr( 528 return Job<Out, In...>(Private::ExecutorBasePtr(
523 new Private::ThenExecutor<Out, In ...>(func, ErrorHandler(), Private::ExecutorBasePtr()))); 529 new Private::ThenExecutor<Out, In ...>(func, error, Private::ExecutorBasePtr())));
524} 530}
525 531
526template<typename Out, typename ... In> 532template<typename Out, typename ... In>
527Job<Out, In ...> start(SyncThenTask<Out, In ...> func) 533Job<Out, In ...> start(SyncThenTask<Out, In ...> func, ErrorHandler error)
528{ 534{
529 return Job<Out, In...>(Private::ExecutorBasePtr( 535 return Job<Out, In...>(Private::ExecutorBasePtr(
530 new Private::SyncThenExecutor<Out, In ...>(func, ErrorHandler(), Private::ExecutorBasePtr()))); 536 new Private::SyncThenExecutor<Out, In ...>(func, error, Private::ExecutorBasePtr())));
531} 537}
532 538
533#ifdef WITH_KJOB 539#ifdef WITH_KJOB
@@ -586,20 +592,18 @@ ExecutionPtr Executor<PrevOut, Out, In ...>::exec(const ExecutorBasePtr &self)
586 // Passing 'self' to execution ensures that the Executor chain remains 592 // Passing 'self' to execution ensures that the Executor chain remains
587 // valid until the entire execution is finished 593 // valid until the entire execution is finished
588 ExecutionPtr execution = ExecutionPtr::create(self); 594 ExecutionPtr execution = ExecutionPtr::create(self);
595#ifndef QT_NO_DEBUG
596 execution->tracer = new Tracer(execution.data()); // owned by execution
597#endif
589 598
590 // chainup 599 // chainup
591 execution->prevExecution = mPrev ? mPrev->exec(mPrev) : ExecutionPtr(); 600 execution->prevExecution = mPrev ? mPrev->exec(mPrev) : ExecutionPtr();
592 /*
593 } else if (mPrev && !mPrevFuture) {
594 // If previous job is running or finished, just get it's future
595 mPrevFuture = static_cast<Async::Future<PrevOut>*>(mPrev->result());
596 }
597 */
598 601
599 execution->resultBase = this->createFuture<Out>(execution); 602 execution->resultBase = ExecutorBase::createFuture<Out>(execution);
600 auto fw = new Async::FutureWatcher<Out>(); 603 auto fw = new Async::FutureWatcher<Out>();
601 QObject::connect(fw, &Async::FutureWatcher<Out>::futureReady, 604 QObject::connect(fw, &Async::FutureWatcher<Out>::futureReady,
602 [fw, execution, this]() { 605 [fw, execution, this]() {
606 handleError(execution);
603 execution->setFinished(); 607 execution->setFinished();
604 delete fw; 608 delete fw;
605 }); 609 });
@@ -607,50 +611,77 @@ ExecutionPtr Executor<PrevOut, Out, In ...>::exec(const ExecutorBasePtr &self)
607 611
608 Async::Future<PrevOut> *prevFuture = execution->prevExecution ? execution->prevExecution->result<PrevOut>() : nullptr; 612 Async::Future<PrevOut> *prevFuture = execution->prevExecution ? execution->prevExecution->result<PrevOut>() : nullptr;
609 if (!prevFuture || prevFuture->isFinished()) { 613 if (!prevFuture || prevFuture->isFinished()) {
610 if (prevFuture && prevFuture->errorCode() != 0) { 614 if (prevFuture) { // prevFuture implies execution->prevExecution
611 if (mErrorFunc) { 615 if (prevFuture->errorCode()) {
612 mErrorFunc(prevFuture->errorCode(), prevFuture->errorMessage()); 616 // Propagate the errorCode and message to the outer Future
613 execution->resultBase->setFinished(); 617 execution->resultBase->setError(prevFuture->errorCode(), prevFuture->errorMessage());
614 execution->setFinished(); 618 if (!execution->errorWasHandled()) {
615 return execution; 619 if (handleError(execution)) {
620 return execution;
621 }
622 } else {
623 return execution;
624 }
616 } else { 625 } else {
617 // Propagate the error to next caller 626 // Propagate error (if any)
618 } 627 }
619 } 628 }
629
620 execution->isRunning = true; 630 execution->isRunning = true;
621 run(execution); 631 run(execution);
622 } else { 632 } else {
623 auto futureWatcher = new Async::FutureWatcher<PrevOut>(); 633 auto prevFutureWatcher = new Async::FutureWatcher<PrevOut>();
624 QObject::connect(futureWatcher, &Async::FutureWatcher<PrevOut>::futureReady, 634 QObject::connect(prevFutureWatcher, &Async::FutureWatcher<PrevOut>::futureReady,
625 [futureWatcher, execution, this]() { 635 [prevFutureWatcher, execution, this]() {
626 auto prevFuture = futureWatcher->future(); 636 auto prevFuture = prevFutureWatcher->future();
627 assert(prevFuture.isFinished()); 637 assert(prevFuture.isFinished());
628 delete futureWatcher; 638 delete prevFutureWatcher;
629 if (prevFuture.errorCode() != 0) { 639 auto prevExecutor = execution->executor->mPrev;
630 if (mErrorFunc) { 640 if (prevFuture.errorCode()) {
631 mErrorFunc(prevFuture.errorCode(), prevFuture.errorMessage()); 641 execution->resultBase->setError(prevFuture.errorCode(), prevFuture.errorMessage());
632 execution->resultBase->setFinished(); 642 if (!execution->errorWasHandled()) {
633 return; 643 if (handleError(execution)) {
644 return;
645 }
634 } else { 646 } else {
635 // Propagate the error to next caller 647 return;
636 } 648 }
637 } 649 }
650
651
652 // propagate error (if any)
638 execution->isRunning = true; 653 execution->isRunning = true;
639 run(execution); 654 run(execution);
640 }); 655 });
641 656
642 futureWatcher->setFuture(*static_cast<Async::Future<PrevOut>*>(prevFuture)); 657 prevFutureWatcher->setFuture(*static_cast<Async::Future<PrevOut>*>(prevFuture));
643 } 658 }
644 659
645 return execution; 660 return execution;
646} 661}
647 662
663template<typename PrevOut, typename Out, typename ... In>
664bool Executor<PrevOut, Out, In ...>::handleError(const ExecutionPtr &execution)
665{
666 assert(execution->resultBase->isFinished());
667 if (execution->resultBase->errorCode()) {
668 if (mErrorFunc) {
669 mErrorFunc(execution->resultBase->errorCode(),
670 execution->resultBase->errorMessage());
671 return true;
672 }
673 }
674
675 return false;
676}
677
648 678
649template<typename Out, typename ... In> 679template<typename Out, typename ... In>
650ThenExecutor<Out, In ...>::ThenExecutor(ThenTask<Out, In ...> then, ErrorHandler error, const ExecutorBasePtr &parent) 680ThenExecutor<Out, In ...>::ThenExecutor(ThenTask<Out, In ...> then, ErrorHandler error, const ExecutorBasePtr &parent)
651 : Executor<typename detail::prevOut<In ...>::type, Out, In ...>(error, parent) 681 : Executor<typename detail::prevOut<In ...>::type, Out, In ...>(error, parent)
652 , mFunc(then) 682 , mFunc(then)
653{ 683{
684 STORE_EXECUTOR_NAME("ThenExecutor", Out, In ...);
654} 685}
655 686
656template<typename Out, typename ... In> 687template<typename Out, typename ... In>
@@ -662,7 +693,7 @@ void ThenExecutor<Out, In ...>::run(const ExecutionPtr &execution)
662 assert(prevFuture->isFinished()); 693 assert(prevFuture->isFinished());
663 } 694 }
664 695
665 this->mFunc(prevFuture ? prevFuture->value() : In() ..., *execution->result<Out>()); 696 ThenExecutor<Out, In ...>::mFunc(prevFuture ? prevFuture->value() : In() ..., *execution->result<Out>());
666} 697}
667 698
668template<typename PrevOut, typename Out, typename In> 699template<typename PrevOut, typename Out, typename In>
@@ -670,6 +701,7 @@ EachExecutor<PrevOut, Out, In>::EachExecutor(EachTask<Out, In> each, ErrorHandle
670 : Executor<PrevOut, Out, In>(error, parent) 701 : Executor<PrevOut, Out, In>(error, parent)
671 , mFunc(each) 702 , mFunc(each)
672{ 703{
704 STORE_EXECUTOR_NAME("EachExecutor", PrevOut, Out, In);
673} 705}
674 706
675template<typename PrevOut, typename Out, typename In> 707template<typename PrevOut, typename Out, typename In>
@@ -687,7 +719,7 @@ void EachExecutor<PrevOut, Out, In>::run(const ExecutionPtr &execution)
687 719
688 for (auto arg : prevFuture->value()) { 720 for (auto arg : prevFuture->value()) {
689 Async::Future<Out> future; 721 Async::Future<Out> future;
690 this->mFunc(arg, future); 722 EachExecutor<PrevOut, Out, In>::mFunc(arg, future);
691 auto fw = new Async::FutureWatcher<Out>(); 723 auto fw = new Async::FutureWatcher<Out>();
692 mFutureWatchers.append(fw); 724 mFutureWatchers.append(fw);
693 QObject::connect(fw, &Async::FutureWatcher<Out>::futureReady, 725 QObject::connect(fw, &Async::FutureWatcher<Out>::futureReady,
@@ -708,16 +740,18 @@ void EachExecutor<PrevOut, Out, In>::run(const ExecutionPtr &execution)
708} 740}
709 741
710template<typename Out, typename In> 742template<typename Out, typename In>
711ReduceExecutor<Out, In>::ReduceExecutor(ReduceTask<Out, In> reduce, ErrorHandler error, const ExecutorBasePtr &parent) 743ReduceExecutor<Out, In>::ReduceExecutor(ReduceTask<Out, In> reduce, ErrorHandler errorFunc, const ExecutorBasePtr &parent)
712 : ThenExecutor<Out, In>(reduce, error, parent) 744 : ThenExecutor<Out, In>(reduce, errorFunc, parent)
713{ 745{
746 STORE_EXECUTOR_NAME("ReduceExecutor", Out, In);
714} 747}
715 748
716template<typename Out, typename ... In> 749template<typename Out, typename ... In>
717SyncThenExecutor<Out, In ...>::SyncThenExecutor(SyncThenTask<Out, In ...> then, ErrorHandler errorHandler, const ExecutorBasePtr &parent) 750SyncThenExecutor<Out, In ...>::SyncThenExecutor(SyncThenTask<Out, In ...> then, ErrorHandler errorFunc, const ExecutorBasePtr &parent)
718 : Executor<typename detail::prevOut<In ...>::type, Out, In ...>(errorHandler, parent) 751 : Executor<typename detail::prevOut<In ...>::type, Out, In ...>(errorFunc, parent)
719 , mFunc(then) 752 , mFunc(then)
720{ 753{
754 STORE_EXECUTOR_NAME("SyncThenExecutor", Out, In ...);
721} 755}
722 756
723template<typename Out, typename ... In> 757template<typename Out, typename ... In>
@@ -740,7 +774,7 @@ void SyncThenExecutor<Out, In ...>::run(const ExecutionPtr &execution, std::fals
740 : nullptr; 774 : nullptr;
741 (void) prevFuture; // silence 'set but not used' warning 775 (void) prevFuture; // silence 'set but not used' warning
742 Async::Future<Out> *future = execution->result<Out>(); 776 Async::Future<Out> *future = execution->result<Out>();
743 future->setValue(this->mFunc(prevFuture ? prevFuture->value() : In() ...)); 777 future->setValue(SyncThenExecutor<Out, In...>::mFunc(prevFuture ? prevFuture->value() : In() ...));
744} 778}
745 779
746template<typename Out, typename ... In> 780template<typename Out, typename ... In>
@@ -751,14 +785,15 @@ void SyncThenExecutor<Out, In ...>::run(const ExecutionPtr &execution, std::true
751 ? execution->prevExecution->result<typename detail::prevOut<In ...>::type>() 785 ? execution->prevExecution->result<typename detail::prevOut<In ...>::type>()
752 : nullptr; 786 : nullptr;
753 (void) prevFuture; // silence 'set but not used' warning 787 (void) prevFuture; // silence 'set but not used' warning
754 this->mFunc(prevFuture ? prevFuture->value() : In() ...); 788 SyncThenExecutor<Out, In ...>::mFunc(prevFuture ? prevFuture->value() : In() ...);
755} 789}
756 790
757template<typename PrevOut, typename Out, typename In> 791template<typename PrevOut, typename Out, typename In>
758SyncEachExecutor<PrevOut, Out, In>::SyncEachExecutor(SyncEachTask<Out, In> each, ErrorHandler errorHandler, const ExecutorBasePtr &parent) 792SyncEachExecutor<PrevOut, Out, In>::SyncEachExecutor(SyncEachTask<Out, In> each, ErrorHandler errorFunc, const ExecutorBasePtr &parent)
759 : Executor<PrevOut, Out, In>(errorHandler, parent) 793 : Executor<PrevOut, Out, In>(errorFunc, parent)
760 , mFunc(each) 794 , mFunc(each)
761{ 795{
796 STORE_EXECUTOR_NAME("SyncEachExecutor", PrevOut, Out, In);
762} 797}
763 798
764template<typename PrevOut, typename Out, typename In> 799template<typename PrevOut, typename Out, typename In>
@@ -783,19 +818,20 @@ void SyncEachExecutor<PrevOut, Out, In>::run(const ExecutionPtr &execution)
783template<typename PrevOut, typename Out, typename In> 818template<typename PrevOut, typename Out, typename In>
784void SyncEachExecutor<PrevOut, Out, In>::run(Async::Future<Out> *out, const typename PrevOut::value_type &arg, std::false_type) 819void SyncEachExecutor<PrevOut, Out, In>::run(Async::Future<Out> *out, const typename PrevOut::value_type &arg, std::false_type)
785{ 820{
786 out->setValue(out->value() + this->mFunc(arg)); 821 out->setValue(out->value() + SyncEachExecutor<PrevOut, Out, In>::mFunc(arg));
787} 822}
788 823
789template<typename PrevOut, typename Out, typename In> 824template<typename PrevOut, typename Out, typename In>
790void SyncEachExecutor<PrevOut, Out, In>::run(Async::Future<Out> * /* unused */, const typename PrevOut::value_type &arg, std::true_type) 825void SyncEachExecutor<PrevOut, Out, In>::run(Async::Future<Out> * /* unused */, const typename PrevOut::value_type &arg, std::true_type)
791{ 826{
792 this->mFunc(arg); 827 SyncEachExecutor<PrevOut, Out, In>::mFunc(arg);
793} 828}
794 829
795template<typename Out, typename In> 830template<typename Out, typename In>
796SyncReduceExecutor<Out, In>::SyncReduceExecutor(SyncReduceTask<Out, In> reduce, ErrorHandler errorHandler, const ExecutorBasePtr &parent) 831SyncReduceExecutor<Out, In>::SyncReduceExecutor(SyncReduceTask<Out, In> reduce, ErrorHandler errorFunc, const ExecutorBasePtr &parent)
797 : SyncThenExecutor<Out, In>(reduce, errorHandler, parent) 832 : SyncThenExecutor<Out, In>(reduce, errorFunc, parent)
798{ 833{
834 STORE_EXECUTOR_NAME("SyncReduceExecutor", Out, In);
799} 835}
800 836
801 837
diff --git a/async/src/debug.cpp b/async/src/debug.cpp
new file mode 100644
index 0000000..9dfad1a
--- /dev/null
+++ b/async/src/debug.cpp
@@ -0,0 +1,73 @@
1/*
2 * Copyright 2015 Daniel Vrátil <dvratil@redhat.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public License as
6 * published by the Free Software Foundation; either version 2 of
7 * the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "debug.h"
19#include "async.h"
20
21#include <QStringBuilder>
22
23#ifdef __GNUG__
24#include <cxxabi.h>
25#include <memory>
26#endif
27
28namespace Async
29{
30
31Q_LOGGING_CATEGORY(Debug, "org.kde.async", QtWarningMsg);
32Q_LOGGING_CATEGORY(Trace, "org.kde.async.trace", QtWarningMsg);
33
34QString demangleName(const char *name)
35{
36#ifdef __GNUG__
37 int status = 1; // uses -3 to 0 error codes
38 std::unique_ptr<char, void(*)(void*)> demangled(abi::__cxa_demangle(name, 0, 0, &status), std::free);
39 if (status == 0) {
40 return QString(demangled.get());
41 }
42#endif
43 return QString(name);
44}
45
46}
47
48using namespace Async;
49
50int Tracer::lastId = 0;
51
52Tracer::Tracer(Private::Execution *execution)
53 : mId(lastId++)
54 , mExecution(execution)
55{
56 msg(Async::Tracer::Start);
57}
58
59Tracer::~Tracer()
60{
61 msg(Async::Tracer::End);
62 // FIXME: Does this work on parallel executions?
63 --lastId;
64 --mId;
65}
66
67void Tracer::msg(Tracer::MsgType msgType)
68{
69 qCDebug(Trace).nospace() << (QString().fill(QLatin1Char(' '), mId * 2) %
70 (msgType == Async::Tracer::Start ? " START " : " END ") %
71 QString::number(mId) % " " %
72 mExecution->executor->mExecutorName);
73}
diff --git a/async/src/debug.h b/async/src/debug.h
new file mode 100644
index 0000000..c453eb3
--- /dev/null
+++ b/async/src/debug.h
@@ -0,0 +1,80 @@
1/*
2 * Copyright 2015 Daniel Vrátil <dvratil@redhat.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public License as
6 * published by the Free Software Foundation; either version 2 of
7 * the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#ifndef ASYNC_DEBUG_H
19#define ASYNC_DEBUG_H
20
21#include <QLoggingCategory>
22#include <QStringBuilder>
23
24#ifndef QT_NO_DEBUG
25#include <typeinfo>
26#endif
27
28namespace Async
29{
30
31Q_DECLARE_LOGGING_CATEGORY(Debug)
32Q_DECLARE_LOGGING_CATEGORY(Trace)
33
34QString demangleName(const char *name);
35
36namespace Private
37{
38class Execution;
39}
40
41class Tracer
42{
43public:
44 Tracer(Private::Execution *execution);
45 ~Tracer();
46
47private:
48 enum MsgType {
49 Start,
50 End
51 };
52 void msg(MsgType);
53
54 int mId;
55 Private::Execution *mExecution;
56
57 static int lastId;
58};
59
60}
61
62#ifndef QT_NO_DEBUG
63 template<typename T>
64 QString storeExecutorNameExpanded() {
65 return Async::demangleName(typeid(T).name());
66 }
67
68 template<typename T, typename ... Tail>
69 typename std::enable_if<sizeof ... (Tail) != 0, QString>::type
70 storeExecutorNameExpanded() {
71 return storeExecutorNameExpanded<T>() % QStringLiteral(", ") % storeExecutorNameExpanded<Tail ...>();
72 }
73
74 #define STORE_EXECUTOR_NAME(name, ...) \
75 ExecutorBase::mExecutorName = QStringLiteral(name) % QStringLiteral("<") % storeExecutorNameExpanded<__VA_ARGS__>() % QStringLiteral(">")
76#else
77 #define STORE_EXECUTOR_NAME(...)
78#endif
79
80#endif // ASYNC_DEBUG_H \ No newline at end of file
diff --git a/async/src/future.cpp b/async/src/future.cpp
index 50a326a..4f3cd80 100644
--- a/async/src/future.cpp
+++ b/async/src/future.cpp
@@ -20,11 +20,41 @@
20 20
21using namespace Async; 21using namespace Async;
22 22
23FutureBase::PrivateBase::PrivateBase(const Private::ExecutionPtr &execution)
24 : finished(false)
25 , errorCode(0)
26 , mExecution(execution)
27{
28}
29
30FutureBase::PrivateBase::~PrivateBase()
31{
32 Private::ExecutionPtr executionPtr = mExecution.toStrongRef();
33 if (executionPtr) {
34 executionPtr->releaseFuture();
35 releaseExecution();
36 }
37}
38
39void FutureBase::PrivateBase::releaseExecution()
40{
41 mExecution.clear();
42}
43
44
45
23FutureBase::FutureBase() 46FutureBase::FutureBase()
47 : d(nullptr)
48{
49}
50
51FutureBase::FutureBase(FutureBase::PrivateBase *dd)
52 : d(dd)
24{ 53{
25} 54}
26 55
27FutureBase::FutureBase(const Async::FutureBase &other) 56FutureBase::FutureBase(const Async::FutureBase &other)
57 : d(other.d)
28{ 58{
29} 59}
30 60
@@ -32,35 +62,97 @@ FutureBase::~FutureBase()
32{ 62{
33} 63}
34 64
35FutureBase::PrivateBase::PrivateBase(const Private::ExecutionPtr &execution) 65void FutureBase::releaseExecution()
36 : mExecution(execution)
37{ 66{
67 d->releaseExecution();
38} 68}
39 69
40FutureBase::PrivateBase::~PrivateBase() 70void FutureBase::setFinished()
41{ 71{
42 Private::ExecutionPtr executionPtr = mExecution.toStrongRef(); 72 if (isFinished()) {
43 if (executionPtr) { 73 return;
44 executionPtr->releaseFuture(); 74 }
45 releaseExecution(); 75 d->finished = true;
76 for (auto watcher : d->watchers) {
77 if (watcher) {
78 watcher->futureReadyCallback();
79 }
46 } 80 }
47} 81}
48 82
49void FutureBase::PrivateBase::releaseExecution() 83bool FutureBase::isFinished() const
50{ 84{
51 mExecution.clear(); 85 return d->finished;
86}
87
88void FutureBase::setError(int code, const QString &message)
89{
90 d->errorCode = code;
91 d->errorMessage = message;
92 setFinished();
93}
94
95int FutureBase::errorCode() const
96{
97 return d->errorCode;
98}
99
100QString FutureBase::errorMessage() const
101{
102 return d->errorMessage;
103}
104
105void FutureBase::setProgress(int processed, int total)
106{
107 setProgress((qreal) processed / total);
108}
109
110void FutureBase::setProgress(qreal progress)
111{
112 for (auto watcher : d->watchers) {
113 if (watcher) {
114 watcher->futureProgressCallback(progress);
115 }
116 }
52} 117}
53 118
54 119
55 120
121void FutureBase::addWatcher(FutureWatcherBase* watcher)
122{
123 d->watchers.append(QPointer<FutureWatcherBase>(watcher));
124}
125
126
127
128
129
56FutureWatcherBase::FutureWatcherBase(QObject *parent) 130FutureWatcherBase::FutureWatcherBase(QObject *parent)
57 : QObject(parent) 131 : QObject(parent)
132 , d(new FutureWatcherBase::Private)
58{ 133{
59} 134}
60 135
61FutureWatcherBase::~FutureWatcherBase() 136FutureWatcherBase::~FutureWatcherBase()
62{ 137{
138 delete d;
63} 139}
64 140
141void FutureWatcherBase::futureReadyCallback()
142{
143 Q_EMIT futureReady();
144}
145
146void FutureWatcherBase::futureProgressCallback(qreal progress)
147{
148 Q_EMIT futureProgress(progress);
149}
65 150
66#include "future.moc" 151void FutureWatcherBase::setFutureImpl(const FutureBase &future)
152{
153 d->future = future;
154 d->future.addWatcher(this);
155 if (future.isFinished()) {
156 futureReadyCallback();
157 }
158}
diff --git a/async/src/future.h b/async/src/future.h
index cadd96d..ff199ef 100644
--- a/async/src/future.h
+++ b/async/src/future.h
@@ -29,28 +29,35 @@ class QEventLoop;
29 29
30namespace Async { 30namespace Async {
31 31
32class FutureWatcherBase;
33template<typename T>
34class FutureWatcher;
35
32namespace Private { 36namespace Private {
33class Execution; 37class Execution;
34class ExecutorBase; 38class ExecutorBase;
35 39
36typedef QSharedPointer<Execution> ExecutionPtr; 40typedef QSharedPointer<Execution> ExecutionPtr;
37 41} // namespace Private
38}
39 42
40class FutureBase 43class FutureBase
41{ 44{
42 friend class Async::Private::Execution; 45 friend class Async::Private::Execution;
46 friend class FutureWatcherBase;
43 47
44public: 48public:
45 virtual ~FutureBase(); 49 virtual ~FutureBase();
46 50
47 virtual void setFinished() = 0; 51 void setFinished();
48 virtual bool isFinished() const = 0; 52 bool isFinished() const;
49 virtual void setError(int code = 1, const QString &message = QString()) = 0; 53 void setError(int code = 1, const QString &message = QString());
54 int errorCode() const;
55 QString errorMessage() const;
50 56
51protected: 57 void setProgress(qreal progress);
52 virtual void releaseExecution() = 0; 58 void setProgress(int processed, int total);
53 59
60protected:
54 class PrivateBase : public QSharedData 61 class PrivateBase : public QSharedData
55 { 62 {
56 public: 63 public:
@@ -59,12 +66,24 @@ protected:
59 66
60 void releaseExecution(); 67 void releaseExecution();
61 68
69 bool finished;
70 int errorCode;
71 QString errorMessage;
72
73 QVector<QPointer<FutureWatcherBase>> watchers;
62 private: 74 private:
63 QWeakPointer<Async::Private::Execution> mExecution; 75 QWeakPointer<Async::Private::Execution> mExecution;
64 }; 76 };
65 77
66 FutureBase(); 78 FutureBase();
79 FutureBase(FutureBase::PrivateBase *dd);
67 FutureBase(const FutureBase &other); 80 FutureBase(const FutureBase &other);
81
82 void addWatcher(Async::FutureWatcherBase *watcher);
83 void releaseExecution();
84
85protected:
86 QExplicitlySharedDataPointer<PrivateBase> d;
68}; 87};
69 88
70template<typename T> 89template<typename T>
@@ -79,42 +98,7 @@ class FutureGeneric : public FutureBase
79 friend class FutureWatcher<T>; 98 friend class FutureWatcher<T>;
80 99
81public: 100public:
82 void setFinished() 101 void waitForFinished() const
83 {
84 if (d->finished) {
85 return;
86 }
87 d->finished = true;
88 for (auto watcher : d->watchers) {
89 if (watcher) {
90 watcher->futureReadyCallback();
91 }
92 }
93 }
94
95 bool isFinished() const
96 {
97 return d->finished;
98 }
99
100 void setError(int errorCode, const QString &message)
101 {
102 d->errorCode = errorCode;
103 d->errorMessage = message;
104 setFinished();
105 }
106
107 int errorCode() const
108 {
109 return d->errorCode;
110 }
111
112 QString errorMessage() const
113 {
114 return d->errorMessage;
115 }
116
117 void waitForFinished()
118 { 102 {
119 if (isFinished()) { 103 if (isFinished()) {
120 return; 104 return;
@@ -123,52 +107,33 @@ public:
123 QEventLoop eventLoop; 107 QEventLoop eventLoop;
124 QObject::connect(&watcher, &Async::FutureWatcher<T>::futureReady, 108 QObject::connect(&watcher, &Async::FutureWatcher<T>::futureReady,
125 &eventLoop, &QEventLoop::quit); 109 &eventLoop, &QEventLoop::quit);
126 watcher.setFuture(*static_cast<Async::Future<T>*>(this)); 110 watcher.setFuture(*static_cast<const Async::Future<T>*>(this));
127 eventLoop.exec(); 111 eventLoop.exec();
128 } 112 }
129 113
130protected: 114protected:
131 FutureGeneric(const Async::Private::ExecutionPtr &execution) 115 FutureGeneric(const Async::Private::ExecutionPtr &execution)
132 : FutureBase() 116 : FutureBase(new Private(execution))
133 , d(new Private(execution))
134 {} 117 {}
135 118
136 FutureGeneric(const FutureGeneric<T> &other) 119 FutureGeneric(const FutureGeneric<T> &other)
137 : FutureBase(other) 120 : FutureBase(other)
138 , d(other.d)
139 {} 121 {}
140 122
123protected:
141 class Private : public FutureBase::PrivateBase 124 class Private : public FutureBase::PrivateBase
142 { 125 {
143 public: 126 public:
144 Private(const Async::Private::ExecutionPtr &execution) 127 Private(const Async::Private::ExecutionPtr &execution)
145 : FutureBase::PrivateBase(execution) 128 : FutureBase::PrivateBase(execution)
146 , finished(false)
147 , errorCode(0)
148 {} 129 {}
149 130
150 typename std::conditional<std::is_void<T>::value, int /* dummy */, T>::type 131 typename std::conditional<std::is_void<T>::value, int /* dummy */, T>::type
151 value; 132 value;
152
153 QVector<QPointer<FutureWatcher<T>>> watchers;
154 bool finished;
155 int errorCode;
156 QString errorMessage;
157 }; 133 };
158
159 QExplicitlySharedDataPointer<Private> d;
160
161 void releaseExecution()
162 {
163 d->releaseExecution();
164 }
165
166 void addWatcher(FutureWatcher<T> *watcher)
167 {
168 d->watchers.append(QPointer<FutureWatcher<T>>(watcher));
169 }
170}; 134};
171 135
136
172template<typename T> 137template<typename T>
173class Future : public FutureGeneric<T> 138class Future : public FutureGeneric<T>
174{ 139{
@@ -188,12 +153,12 @@ public:
188 153
189 void setValue(const T &value) 154 void setValue(const T &value)
190 { 155 {
191 this->d->value = value; 156 static_cast<typename FutureGeneric<T>::Private*>(this->d.data())->value = value;
192 } 157 }
193 158
194 T value() const 159 T value() const
195 { 160 {
196 return this->d->value; 161 return static_cast<typename FutureGeneric<T>::Private*>(this->d.data())->value;
197 } 162 }
198 163
199protected: 164protected:
@@ -208,9 +173,6 @@ class Future<void> : public FutureGeneric<void>
208{ 173{
209 friend class Async::Private::ExecutorBase; 174 friend class Async::Private::ExecutorBase;
210 175
211 template<typename T_>
212 friend class Async::FutureWatcher;
213
214public: 176public:
215 Future() 177 Future()
216 : FutureGeneric<void>(Async::Private::ExecutionPtr()) 178 : FutureGeneric<void>(Async::Private::ExecutionPtr())
@@ -227,16 +189,38 @@ protected:
227}; 189};
228 190
229 191
192
193
194
230class FutureWatcherBase : public QObject 195class FutureWatcherBase : public QObject
231{ 196{
232 Q_OBJECT 197 Q_OBJECT
233 198
199 friend class FutureBase;
200
201Q_SIGNALS:
202 void futureReady();
203 void futureProgress(qreal progress);
204
234protected: 205protected:
235 FutureWatcherBase(QObject *parent = nullptr); 206 FutureWatcherBase(QObject *parent = nullptr);
236 virtual ~FutureWatcherBase(); 207 virtual ~FutureWatcherBase();
237 208
238Q_SIGNALS: 209 void futureReadyCallback();
239 void futureReady(); 210 void futureProgressCallback(qreal progress);
211
212 void setFutureImpl(const Async::FutureBase &future);
213
214protected:
215 class Private {
216 public:
217 Async::FutureBase future;
218 };
219
220 Private * const d;
221
222private:
223 Q_DISABLE_COPY(FutureWatcherBase);
240}; 224};
241 225
242template<typename T> 226template<typename T>
@@ -254,25 +238,16 @@ public:
254 238
255 void setFuture(const Async::Future<T> &future) 239 void setFuture(const Async::Future<T> &future)
256 { 240 {
257 mFuture = future; 241 setFutureImpl(*static_cast<const Async::FutureBase*>(&future));
258 mFuture.addWatcher(this);
259 if (future.isFinished()) {
260 futureReadyCallback();
261 }
262 } 242 }
263 243
264 Async::Future<T> future() const 244 Async::Future<T> future() const
265 { 245 {
266 return mFuture; 246 return *static_cast<Async::Future<T>*>(&d->future);
267 } 247 }
268 248
269private: 249private:
270 void futureReadyCallback() 250 Q_DISABLE_COPY(FutureWatcher<T>);
271 {
272 Q_EMIT futureReady();
273 }
274
275 Async::Future<T> mFuture;
276}; 251};
277 252
278} // namespace Async 253} // namespace Async