summaryrefslogtreecommitdiffstats
path: root/async
diff options
context:
space:
mode:
Diffstat (limited to 'async')
-rw-r--r--async/CMakeLists.txt12
-rw-r--r--async/autotests/CMakeLists.txt11
-rw-r--r--async/autotests/asynctest.cpp859
-rw-r--r--async/autotests/kjobtest.cpp69
-rw-r--r--async/autotests/testkjob.cpp28
-rw-r--r--async/autotests/testkjob.h48
-rw-r--r--async/src/CMakeLists.txt17
-rw-r--r--async/src/async.cpp148
-rw-r--r--async/src/async.h872
-rw-r--r--async/src/async_impl.h81
-rw-r--r--async/src/debug.cpp75
-rw-r--r--async/src/debug.h80
-rw-r--r--async/src/future.cpp158
-rw-r--r--async/src/future.h255
14 files changed, 0 insertions, 2713 deletions
diff --git a/async/CMakeLists.txt b/async/CMakeLists.txt
deleted file mode 100644
index a6b53f8..0000000
--- a/async/CMakeLists.txt
+++ /dev/null
@@ -1,12 +0,0 @@
1project(libasync)
2
3option(WITH_KJOB "Enable native support for KJob in libasync API (enabled by default)" ON)
4
5if (WITH_KJOB)
6 set(MINUMUM_FRAMEWORKS_VERSION "5.8.0")
7 find_package(KF5CoreAddons REQUIRED ${MINUMUM_FRAMEWORKS_VERSION})
8 add_definitions(-DWITH_KJOB)
9endif()
10
11add_subdirectory(src)
12add_subdirectory(autotests) \ No newline at end of file
diff --git a/async/autotests/CMakeLists.txt b/async/autotests/CMakeLists.txt
deleted file mode 100644
index 8116f13..0000000
--- a/async/autotests/CMakeLists.txt
+++ /dev/null
@@ -1,11 +0,0 @@
1include_directories(../src ${CMAKE_CURRENT_BINARY_DIR})
2
3add_executable(asynctest asynctest.cpp)
4qt5_use_modules(asynctest Test)
5target_link_libraries(asynctest akonadi2async Qt5::Core Qt5::Test)
6
7if (WITH_KJOB)
8 add_executable(kjobtest kjobtest.cpp testkjob.cpp)
9 qt5_use_modules(kjobtest Test)
10 target_link_libraries(kjobtest akonadi2async Qt5::Core Qt5::Test KF5::CoreAddons)
11endif () \ No newline at end of file
diff --git a/async/autotests/asynctest.cpp b/async/autotests/asynctest.cpp
deleted file mode 100644
index 9bc9f6b..0000000
--- a/async/autotests/asynctest.cpp
+++ /dev/null
@@ -1,859 +0,0 @@
1/*
2 * Copyright 2014 Daniel Vrátil <dvratil@redhat.com>
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of
7 * the License or (at your option) version 3 or any later version
8 * accepted by the membership of KDE e.V. (or its successor approved
9 * by the membership of KDE e.V.), which shall act as a proxy
10 * defined in Section 14 of version 3 of the license.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22#include "../src/async.h"
23
24#include <QObject>
25#include <QString>
26#include <QTimer>
27#include <QtTest/QTest>
28#include <QDebug>
29
30#include <functional>
31
32class AsyncTest : public QObject
33{
34 Q_OBJECT
35
36public:
37 AsyncTest()
38 {}
39
40 ~AsyncTest()
41 {}
42
43private Q_SLOTS:
44 void testSyncPromises();
45 void testAsyncPromises();
46 void testAsyncPromises2();
47 void testNestedAsync();
48 void testStartValue();
49
50 void testAsyncThen();
51 void testSyncThen();
52 void testJoinedThen();
53 void testVoidThen();
54
55 void testAsyncEach();
56 void testSyncEach();
57 void testJoinedEach();
58 void testVoidEachThen();
59 void testAsyncVoidEachThen();
60
61 void testAsyncReduce();
62 void testSyncReduce();
63 void testJoinedReduce();
64 void testVoidReduce();
65
66 void testProgressReporting();
67 void testErrorHandler();
68 void testErrorPropagation();
69 void testErrorHandlerAsync();
70 void testErrorPropagationAsync();
71 void testNestedErrorPropagation();
72
73 void testChainingRunningJob();
74 void testChainingFinishedJob();
75
76 void testLifetimeWithoutHandle();
77 void testLifetimeWithHandle();
78
79 void benchmarkSyncThenExecutor();
80
81private:
82 template<typename T>
83 class AsyncSimulator {
84 public:
85 AsyncSimulator(Async::Future<T> &future, const T &result)
86 : mFuture(future)
87 , mResult(result)
88 {
89 QObject::connect(&mTimer, &QTimer::timeout,
90 [this]() {
91 mFuture.setValue(mResult);
92 mFuture.setFinished();
93 });
94 QObject::connect(&mTimer, &QTimer::timeout,
95 [this]() {
96 delete this;
97 });
98 mTimer.setSingleShot(true);
99 mTimer.start(200);
100 }
101
102 AsyncSimulator(Async::Future<T> &future, std::function<void(Async::Future<T>&)> callback)
103 : mFuture(future)
104 , mCallback(callback)
105 {
106 QObject::connect(&mTimer, &QTimer::timeout,
107 [this]() {
108 mCallback(mFuture);
109 });
110 QObject::connect(&mTimer, &QTimer::timeout,
111 [this]() {
112 delete this;
113 });
114 mTimer.setSingleShot(true);
115 mTimer.start(200);
116 }
117
118 private:
119 Async::Future<T> mFuture;
120 std::function<void(Async::Future<T>&)> mCallback;
121 T mResult;
122 QTimer mTimer;
123 };
124};
125
126
127template<>
128class AsyncTest::AsyncSimulator<void> {
129public:
130 AsyncSimulator(Async::Future<void> &future)
131 : mFuture(future)
132 {
133 QObject::connect(&mTimer, &QTimer::timeout,
134 [this]() {
135 mFuture.setFinished();
136 });
137 QObject::connect(&mTimer, &QTimer::timeout,
138 [this]() {
139 delete this;
140 });
141 mTimer.setSingleShot(true);
142 mTimer.start(200);
143 }
144
145private:
146 Async::Future<void> mFuture;
147 QTimer mTimer;
148};
149
150
151
152void AsyncTest::testSyncPromises()
153{
154 auto baseJob = Async::start<int>(
155 [](Async::Future<int> &f) {
156 f.setValue(42);
157 f.setFinished();
158 })
159 .then<QString, int>(
160 [](int v, Async::Future<QString> &f) {
161 f.setValue("Result is " + QString::number(v));
162 f.setFinished();
163 });
164
165 auto job = baseJob.then<QString, QString>(
166 [](const QString &v, Async::Future<QString> &f) {
167 f.setValue(v.toUpper());
168 f.setFinished();
169 });
170
171 Async::Future<QString> future = job.exec();
172
173 QVERIFY(future.isFinished());
174 QCOMPARE(future.value(), QString::fromLatin1("RESULT IS 42"));
175}
176
177void AsyncTest::testAsyncPromises()
178{
179 auto job = Async::start<int>(
180 [](Async::Future<int> &future) {
181 new AsyncSimulator<int>(future, 42);
182 });
183
184 Async::Future<int> future = job.exec();
185
186 future.waitForFinished();
187 QCOMPARE(future.value(), 42);
188}
189
190void AsyncTest::testAsyncPromises2()
191{
192 bool done = false;
193
194 auto job = Async::start<int>(
195 [](Async::Future<int> &future) {
196 new AsyncSimulator<int>(future, 42);
197 }
198 ).then<int, int>([&done](int result, Async::Future<int> &future) {
199 done = true;
200 future.setValue(result);
201 future.setFinished();
202 });
203 auto future = job.exec();
204
205 QTRY_VERIFY(done);
206 QCOMPARE(future.value(), 42);
207}
208
209void AsyncTest::testNestedAsync()
210{
211 bool done = false;
212
213 auto job = Async::start<int>(
214 [](Async::Future<int> &future) {
215 auto innerJob = Async::start<int>([](Async::Future<int> &innerFuture) {
216 new AsyncSimulator<int>(innerFuture, 42);
217 }).then<void>([&future](Async::Future<void> &innerThenFuture) {
218 future.setFinished();
219 innerThenFuture.setFinished();
220 });
221 innerJob.exec().waitForFinished();
222 }
223 ).then<int, int>([&done](int result, Async::Future<int> &future) {
224 done = true;
225 future.setValue(result);
226 future.setFinished();
227 });
228 job.exec();
229
230 QTRY_VERIFY(done);
231}
232
233void AsyncTest::testStartValue()
234{
235 auto job = Async::start<int, int>(
236 [](int in, Async::Future<int> &future) {
237 future.setValue(in);
238 future.setFinished();
239 });
240
241 auto future = job.exec(42);
242 QVERIFY(future.isFinished());
243 QCOMPARE(future.value(), 42);
244}
245
246
247
248
249
250void AsyncTest::testAsyncThen()
251{
252 auto job = Async::start<int>(
253 [](Async::Future<int> &future) {
254 new AsyncSimulator<int>(future, 42);
255 });
256
257 auto future = job.exec();
258 future.waitForFinished();
259
260 QVERIFY(future.isFinished());
261 QCOMPARE(future.value(), 42);
262}
263
264
265void AsyncTest::testSyncThen()
266{
267 auto job = Async::start<int>(
268 []() -> int {
269 return 42;
270 })
271 .then<int, int>(
272 [](int in) -> int {
273 return in * 2;
274 });
275
276 auto future = job.exec();
277 QVERIFY(future.isFinished());
278 QCOMPARE(future.value(), 84);
279}
280
281void AsyncTest::testJoinedThen()
282{
283 auto job1 = Async::start<int, int>(
284 [](int in, Async::Future<int> &future) {
285 new AsyncSimulator<int>(future, in * 2);
286 });
287
288 auto job2 = Async::start<int>(
289 [](Async::Future<int> &future) {
290 new AsyncSimulator<int>(future, 42);
291 })
292 .then<int>(job1);
293
294 auto future = job2.exec();
295 future.waitForFinished();
296
297 QVERIFY(future.isFinished());
298 QCOMPARE(future.value(), 84);
299}
300
301void AsyncTest::testVoidThen()
302{
303 int check = 0;
304
305 auto job = Async::start<void>(
306 [&check](Async::Future<void> &future) {
307 new AsyncSimulator<void>(future);
308 ++check;
309 })
310 .then<void>(
311 [&check](Async::Future<void> &future) {
312 new AsyncSimulator<void>(future);
313 ++check;
314 })
315 .then<void>(
316 [&check]() {
317 ++check;
318 });
319
320 auto future = job.exec();
321 future.waitForFinished();
322
323 QVERIFY(future.isFinished());
324 QCOMPARE(check, 3);
325}
326
327
328
329void AsyncTest::testAsyncEach()
330{
331 auto job = Async::start<QList<int>>(
332 [](Async::Future<QList<int>> &future) {
333 new AsyncSimulator<QList<int>>(future, { 1, 2, 3, 4 });
334 })
335 .each<QList<int>, int>(
336 [](const int &v, Async::Future<QList<int>> &future) {
337 new AsyncSimulator<QList<int>>(future, { v + 1 });
338 });
339
340 auto future = job.exec();
341 future.waitForFinished();
342
343 const QList<int> expected({ 2, 3, 4, 5 });
344 QVERIFY(future.isFinished());
345 QCOMPARE(future.value(), expected);
346}
347
348void AsyncTest::testSyncEach()
349{
350 auto job = Async::start<QList<int>>(
351 []() -> QList<int> {
352 return { 1, 2, 3, 4 };
353 })
354 .each<QList<int>, int>(
355 [](const int &v) -> QList<int> {
356 return { v + 1 };
357 });
358
359 Async::Future<QList<int>> future = job.exec();
360
361 const QList<int> expected({ 2, 3, 4, 5 });
362 QVERIFY(future.isFinished());
363 QCOMPARE(future.value(), expected);
364}
365
366void AsyncTest::testJoinedEach()
367{
368 auto job1 = Async::start<QList<int>, int>(
369 [](int v, Async::Future<QList<int>> &future) {
370 new AsyncSimulator<QList<int>>(future, { v * 2 });
371 });
372
373 auto job = Async::start<QList<int>>(
374 []() -> QList<int> {
375 return { 1, 2, 3, 4 };
376 })
377 .each(job1);
378
379 auto future = job.exec();
380 future.waitForFinished();
381
382 const QList<int> expected({ 2, 4, 6, 8 });
383 QVERIFY(future.isFinished());
384 QCOMPARE(future.value(), expected);
385}
386
387void AsyncTest::testVoidEachThen()
388{
389 QList<int> check;
390 auto job = Async::start<QList<int>>(
391 []() -> QList<int> {
392 return { 1, 2, 3, 4 };
393 }).each<void, int>(
394 [&check](const int &v) {
395 check << v;
396 }).then<void>([](){});
397
398 auto future = job.exec();
399
400 const QList<int> expected({ 1, 2, 3, 4 });
401 QVERIFY(future.isFinished());
402 QCOMPARE(check, expected);
403}
404
405void AsyncTest::testAsyncVoidEachThen()
406{
407 bool completedJob = false;
408 QList<int> check;
409 auto job = Async::start<QList<int>>(
410 [](Async::Future<QList<int> > &future) {
411 new AsyncSimulator<QList<int>>(future, { 1, 2, 3, 4 });
412 }).each<void, int>(
413 [&check](const int &v, Async::Future<void> &future) {
414 check << v;
415 new AsyncSimulator<void>(future);
416 }).then<void>([&completedJob](Async::Future<void> &future) {
417 completedJob = true;
418 future.setFinished();
419 });
420
421 auto future = job.exec();
422 future.waitForFinished();
423
424 const QList<int> expected({ 1, 2, 3, 4 });
425 QVERIFY(future.isFinished());
426 QVERIFY(completedJob);
427 QCOMPARE(check, expected);
428}
429
430
431
432
433
434void AsyncTest::testAsyncReduce()
435{
436 auto job = Async::start<QList<int>>(
437 [](Async::Future<QList<int>> &future) {
438 new AsyncSimulator<QList<int>>(future, { 1, 2, 3, 4 });
439 })
440 .reduce<int, QList<int>>(
441 [](const QList<int> &list, Async::Future<int> &future) {
442 new AsyncSimulator<int>(future,
443 [list](Async::Future<int> &future) {
444 int sum = 0;
445 for (int i : list) sum += i;
446 future.setValue(sum);
447 future.setFinished();
448 }
449 );
450 });
451
452 Async::Future<int> future = job.exec();
453 future.waitForFinished();
454
455 QVERIFY(future.isFinished());
456 QCOMPARE(future.value(), 10);
457}
458
459void AsyncTest::testSyncReduce()
460{
461 auto job = Async::start<QList<int>>(
462 []() -> QList<int> {
463 return { 1, 2, 3, 4 };
464 })
465 .reduce<int, QList<int>>(
466 [](const QList<int> &list) -> int {
467 int sum = 0;
468 for (int i : list) sum += i;
469 return sum;
470 });
471
472 Async::Future<int> future = job.exec();
473
474 QVERIFY(future.isFinished());
475 QCOMPARE(future.value(), 10);
476}
477
478
479void AsyncTest::testJoinedReduce()
480{
481 auto job1 = Async::start<int, QList<int>>(
482 [](const QList<int> &list, Async::Future<int> &future) {
483 int sum = 0;
484 for (int i : list) sum += i;
485 new AsyncSimulator<int>(future, sum);
486 });
487
488 auto job = Async::start<QList<int>>(
489 []() -> QList<int> {
490 return { 1, 2, 3, 4 };
491 })
492 .reduce(job1);
493
494 auto future = job.exec();
495 future.waitForFinished();
496
497 QVERIFY(future.isFinished());
498 QCOMPARE(future.value(), 10);
499}
500
501void AsyncTest::testVoidReduce()
502{
503// This must not compile (reduce with void result makes no sense)
504#ifdef TEST_BUILD_FAIL
505 auto job = Async::start<QList<int>>(
506 []() -> QList<int> {
507 return { 1, 2, 3, 4 };
508 })
509 .reduce<void, QList<int>>(
510 [](const QList<int> &list) -> int {
511 return;
512 });
513
514 auto future = job.exec();
515 QVERIFY(future.isFinished());
516#endif
517}
518
519
520void AsyncTest::testProgressReporting()
521{
522 static int progress;
523 progress = 0;
524
525 auto job = Async::start<void>(
526 [](Async::Future<void> &f) {
527 QTimer *timer = new QTimer();
528 connect(timer, &QTimer::timeout,
529 [&f, timer]() {
530 f.setProgress(++progress);
531 if (progress == 100) {
532 timer->stop();
533 timer->deleteLater();
534 f.setFinished();
535 }
536 });
537 timer->start(1);
538 });
539
540 int progressCheck = 0;
541 Async::FutureWatcher<void> watcher;
542 connect(&watcher, &Async::FutureWatcher<void>::futureProgress,
543 [&progressCheck](qreal progress) {
544 progressCheck++;
545 // FIXME: Don't use Q_ASSERT in unit tests
546 Q_ASSERT((int) progress == progressCheck);
547 });
548 watcher.setFuture(job.exec());
549 watcher.future().waitForFinished();
550
551 QVERIFY(watcher.future().isFinished());
552 QCOMPARE(progressCheck, 100);
553}
554
555void AsyncTest::testErrorHandler()
556{
557
558 {
559 auto job = Async::start<int>(
560 [](Async::Future<int> &f) {
561 f.setError(1, "error");
562 });
563
564 auto future = job.exec();
565 QVERIFY(future.isFinished());
566 QCOMPARE(future.errorCode(), 1);
567 QCOMPARE(future.errorMessage(), QString::fromLatin1("error"));
568 }
569
570 {
571 int error = 0;
572 auto job = Async::start<int>(
573 [](Async::Future<int> &f) {
574 f.setError(1, "error");
575 },
576 [&error](int errorCode, const QString &errorMessage) {
577 error += errorCode;
578 }
579 );
580
581 auto future = job.exec();
582 QVERIFY(future.isFinished());
583 QCOMPARE(error, 1);
584 QCOMPARE(future.errorCode(), 1);
585 QCOMPARE(future.errorMessage(), QString::fromLatin1("error"));
586 }
587}
588
589void AsyncTest::testErrorPropagation()
590{
591 int error = 0;
592 bool called = false;
593 auto job = Async::start<int>(
594 [](Async::Future<int> &f) {
595 f.setError(1, "error");
596 })
597 .then<int, int>(
598 [&called](int v, Async::Future<int> &f) {
599 called = true;
600 f.setFinished();
601 },
602 [&error](int errorCode, const QString &errorMessage) {
603 error += errorCode;
604 }
605 );
606 auto future = job.exec();
607 QVERIFY(future.isFinished());
608 QCOMPARE(future.errorCode(), 1);
609 QCOMPARE(future.errorMessage(), QString::fromLatin1("error"));
610 QCOMPARE(called, false);
611 QCOMPARE(error, 1);
612}
613
614void AsyncTest::testErrorHandlerAsync()
615{
616 {
617 auto job = Async::start<int>(
618 [](Async::Future<int> &f) {
619 new AsyncSimulator<int>(f,
620 [](Async::Future<int> &f) {
621 f.setError(1, "error");
622 }
623 );
624 }
625 );
626
627 auto future = job.exec();
628 future.waitForFinished();
629
630 QVERIFY(future.isFinished());
631 QCOMPARE(future.errorCode(), 1);
632 QCOMPARE(future.errorMessage(), QString::fromLatin1("error"));
633 }
634
635 {
636 int error = 0;
637 auto job = Async::start<int>(
638 [](Async::Future<int> &f) {
639 new AsyncSimulator<int>(f,
640 [](Async::Future<int> &f) {
641 f.setError(1, "error");
642 }
643 );
644 },
645 [&error](int errorCode, const QString &errorMessage) {
646 error += errorCode;
647 }
648 );
649
650 auto future = job.exec();
651 future.waitForFinished();
652
653 QVERIFY(future.isFinished());
654 QCOMPARE(error, 1);
655 QCOMPARE(future.errorCode(), 1);
656 QCOMPARE(future.errorMessage(), QString::fromLatin1("error"));
657 }
658}
659
660void AsyncTest::testErrorPropagationAsync()
661{
662 int error = 0;
663 bool called = false;
664 auto job = Async::start<int>(
665 [](Async::Future<int> &f) {
666 new AsyncSimulator<int>(f,
667 [](Async::Future<int> &f) {
668 f.setError(1, "error");
669 }
670 );
671 })
672 .then<int, int>(
673 [&called](int v, Async::Future<int> &f) {
674 called = true;
675 f.setFinished();
676 },
677 [&error](int errorCode, const QString &errorMessage) {
678 error += errorCode;
679 }
680 );
681
682 auto future = job.exec();
683 future.waitForFinished();
684
685 QVERIFY(future.isFinished());
686 QCOMPARE(future.errorCode(), 1);
687 QCOMPARE(future.errorMessage(), QString::fromLatin1("error"));
688 QCOMPARE(called, false);
689 QCOMPARE(error, 1);
690}
691
692void AsyncTest::testNestedErrorPropagation()
693{
694 int error = 0;
695 auto job = Async::start<void>([](){})
696 .then<void>(Async::error<void>(1, "error")) //Nested job that throws error
697 .then<void>([](Async::Future<void> &future) {
698 //We should never get here
699 Q_ASSERT(false);
700 },
701 [&error](int errorCode, const QString &errorMessage) {
702 error += errorCode;
703 }
704 );
705 auto future = job.exec();
706
707 future.waitForFinished();
708
709 QVERIFY(future.isFinished());
710 QCOMPARE(future.errorCode(), 1);
711 QCOMPARE(future.errorMessage(), QString::fromLatin1("error"));
712 QCOMPARE(error, 1);
713}
714
715
716
717
718void AsyncTest::testChainingRunningJob()
719{
720 int check = 0;
721
722 auto job = Async::start<int>(
723 [&check](Async::Future<int> &future) {
724 QTimer *timer = new QTimer();
725 QObject::connect(timer, &QTimer::timeout,
726 [&future, &check]() {
727 ++check;
728 future.setValue(42);
729 future.setFinished();
730 });
731 QObject::connect(timer, &QTimer::timeout,
732 timer, &QObject::deleteLater);
733 timer->setSingleShot(true);
734 timer->start(500);
735 });
736
737 auto future1 = job.exec();
738 QTest::qWait(200);
739
740 auto job2 = job.then<int, int>(
741 [&check](int in) -> int {
742 ++check;
743 return in * 2;
744 });
745
746 auto future2 = job2.exec();
747 QVERIFY(!future1.isFinished());
748 future2.waitForFinished();
749
750 QEXPECT_FAIL("", "Chaining new job to a running job no longer executes the new job. "
751 "This is a trade-off for being able to re-execute single job multiple times.",
752 Abort);
753
754 QCOMPARE(check, 2);
755
756 QVERIFY(future1.isFinished());
757 QVERIFY(future2.isFinished());
758 QCOMPARE(future1.value(), 42);
759 QCOMPARE(future2.value(), 84);
760}
761
762void AsyncTest::testChainingFinishedJob()
763{
764 int check = 0;
765
766 auto job = Async::start<int>(
767 [&check]() -> int {
768 ++check;
769 return 42;
770 });
771
772 auto future1 = job.exec();
773 QVERIFY(future1.isFinished());
774
775 auto job2 = job.then<int, int>(
776 [&check](int in) -> int {
777 ++check;
778 return in * 2;
779 });
780
781 auto future2 = job2.exec();
782 QVERIFY(future2.isFinished());
783
784 QEXPECT_FAIL("", "Resuming finished job by chaining a new job and calling exec() is no longer suppported. "
785 "This is a trade-off for being able to re-execute single job multiple times.",
786 Abort);
787
788 QCOMPARE(check, 2);
789
790 QCOMPARE(future1.value(), 42);
791 QCOMPARE(future2.value(), 84);
792}
793
794/*
795 * We want to be able to execute jobs without keeping a handle explicitly alive.
796 * If the future handle inside the continuation would keep the executor alive, that would probably already work.
797 */
798void AsyncTest::testLifetimeWithoutHandle()
799{
800 bool done = false;
801 {
802 auto job = Async::start<void>([&done](Async::Future<void> &future) {
803 QTimer *timer = new QTimer();
804 QObject::connect(timer, &QTimer::timeout,
805 [&future, &done]() {
806 done = true;
807 future.setFinished();
808 });
809 QObject::connect(timer, &QTimer::timeout,
810 timer, &QObject::deleteLater);
811 timer->setSingleShot(true);
812 timer->start(500);
813 });
814 job.exec();
815 }
816
817 QTRY_VERIFY(done);
818}
819
820/*
821 * The future handle should keep the executor alive, and the future reference should probably not become invalid inside the continuation,
822 * until the job is done (alternatively a copy of the future inside the continuation should work as well).
823 */
824void AsyncTest::testLifetimeWithHandle()
825{
826 Async::Future<void> future;
827 {
828 auto job = Async::start<void>([](Async::Future<void> &future) {
829 QTimer *timer = new QTimer();
830 QObject::connect(timer, &QTimer::timeout,
831 [&future]() {
832 future.setFinished();
833 });
834 QObject::connect(timer, &QTimer::timeout,
835 timer, &QObject::deleteLater);
836 timer->setSingleShot(true);
837 timer->start(500);
838 });
839 future = job.exec();
840 }
841
842 QTRY_VERIFY(future.isFinished());
843}
844
845void AsyncTest::benchmarkSyncThenExecutor()
846{
847 auto job = Async::start<int>(
848 []() -> int {
849 return 0;
850 });
851
852 QBENCHMARK {
853 job.exec();
854 }
855}
856
857QTEST_MAIN(AsyncTest);
858
859#include "asynctest.moc"
diff --git a/async/autotests/kjobtest.cpp b/async/autotests/kjobtest.cpp
deleted file mode 100644
index be92d68..0000000
--- a/async/autotests/kjobtest.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
1/*
2 * Copyright 2015 Daniel Vrátil <dvratil@redhat.com>
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of
7 * the License or (at your option) version 3 or any later version
8 * accepted by the membership of KDE e.V. (or its successor approved
9 * by the membership of KDE e.V.), which shall act as a proxy
10 * defined in Section 14 of version 3 of the license.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22#include "../src/async.h"
23#include "testkjob.h"
24
25#include <QtTest/QTest>
26
27class KJobTest : public QObject
28{
29 Q_OBJECT
30
31public:
32 KJobTest()
33 {}
34
35 ~KJobTest()
36 {}
37
38private Q_SLOTS:
39 void testSingleKJob();
40 void testKJobChain();
41
42};
43
44void KJobTest::testSingleKJob()
45{
46 auto job = Async::start<int, TestKJob, &TestKJob::result, int>();
47
48 auto future = job.exec(42);
49 future.waitForFinished();
50
51 QVERIFY(future.isFinished());
52 QCOMPARE(future.value(), 42);
53}
54
55void KJobTest::testKJobChain()
56{
57 auto job = Async::start<int, TestKJob, &TestKJob::result, int>()
58 .then<int, TestKJob, &TestKJob::result, int>();
59
60 auto future = job.exec(42);
61 future.waitForFinished();
62
63 QVERIFY(future.isFinished());
64 QCOMPARE(future.value(), 42);
65}
66
67QTEST_MAIN(KJobTest)
68
69#include "kjobtest.moc" \ No newline at end of file
diff --git a/async/autotests/testkjob.cpp b/async/autotests/testkjob.cpp
deleted file mode 100644
index b86f913..0000000
--- a/async/autotests/testkjob.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
1#include "testkjob.h"
2
3TestKJob::TestKJob(int result)
4 : mResult(result)
5{
6 connect(&mTimer, &QTimer::timeout,
7 this, &TestKJob::onTimeout);
8 mTimer.setSingleShot(true);
9 mTimer.setInterval(200);
10}
11
12TestKJob::~TestKJob()
13{}
14
15void TestKJob::start()
16{
17 mTimer.start();
18}
19
20int TestKJob::result()
21{
22 return mResult;
23}
24
25void TestKJob::onTimeout()
26{
27 emitResult();
28} \ No newline at end of file
diff --git a/async/autotests/testkjob.h b/async/autotests/testkjob.h
deleted file mode 100644
index eead98e..0000000
--- a/async/autotests/testkjob.h
+++ /dev/null
@@ -1,48 +0,0 @@
1/*
2 * Copyright 2015 Daniel Vrátil <dvratil@redhat.com>
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of
7 * the License or (at your option) version 3 or any later version
8 * accepted by the membership of KDE e.V. (or its successor approved
9 * by the membership of KDE e.V.), which shall act as a proxy
10 * defined in Section 14 of version 3 of the license.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22#ifndef TESTKJOB_H
23#define TESTKJOB_H
24
25#include <KJob>
26#include <QTimer>
27
28class TestKJob : public KJob
29{
30 Q_OBJECT
31
32public:
33 TestKJob(int result);
34 ~TestKJob();
35
36 void start();
37
38 int result();
39
40private Q_SLOTS:
41 void onTimeout();
42
43private:
44 int mResult;
45 QTimer mTimer;
46};
47
48#endif // TESTKJOB_H \ No newline at end of file
diff --git a/async/src/CMakeLists.txt b/async/src/CMakeLists.txt
deleted file mode 100644
index becc8ee..0000000
--- a/async/src/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
1project(akonadi2async)
2
3include_directories(${CMAKE_CURRENT_BINARY_DIR})
4
5set(async_SRCS
6 async.cpp
7 future.cpp
8 debug.cpp
9)
10
11add_library(${PROJECT_NAME} SHARED ${async_SRCS})
12target_link_libraries(${PROJECT_NAME} PUBLIC Qt5::Core)
13if (WITH_KJOB)
14 target_link_libraries(${PROJECT_NAME} PUBLIC KF5::CoreAddons)
15endif ()
16
17install(TARGETS ${PROJECT_NAME} ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
diff --git a/async/src/async.cpp b/async/src/async.cpp
deleted file mode 100644
index 0e86a84..0000000
--- a/async/src/async.cpp
+++ /dev/null
@@ -1,148 +0,0 @@
1/*
2 * Copyright 2014 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 "async.h"
19
20#include <QCoreApplication>
21#include <QDebug>
22#include <QEventLoop>
23#include <QTimer>
24
25using namespace Async;
26
27Private::Execution::Execution(const Private::ExecutorBasePtr &executor)
28 : executor(executor)
29 , resultBase(nullptr)
30 , isRunning(false)
31 , isFinished(false)
32{
33}
34
35Private::Execution::~Execution()
36{
37 if (resultBase) {
38 resultBase->releaseExecution();
39 delete resultBase;
40 }
41 prevExecution.reset();
42}
43
44void Private::Execution::setFinished()
45{
46 isFinished = true;
47 //executor.clear();
48#ifndef QT_NO_DEBUG
49 if (tracer) {
50 delete tracer;
51 }
52#endif
53}
54
55void Private::Execution::releaseFuture()
56{
57 resultBase = 0;
58}
59
60bool Private::Execution::errorWasHandled() const
61{
62 Execution *exec = const_cast<Execution*>(this);
63 while (exec) {
64 if (exec->executor->hasErrorFunc()) {
65 return true;
66 }
67 exec = exec->prevExecution.data();
68 }
69 return false;
70}
71
72
73
74
75
76Private::ExecutorBase::ExecutorBase(const ExecutorBasePtr &parent)
77 : mPrev(parent)
78{
79}
80
81Private::ExecutorBase::~ExecutorBase()
82{
83}
84
85
86
87
88JobBase::JobBase(const Private::ExecutorBasePtr &executor)
89 : mExecutor(executor)
90{
91}
92
93JobBase::~JobBase()
94{
95}
96
97static void asyncWhile(const std::function<void(std::function<void(bool)>)> &body, const std::function<void()> &completionHandler) {
98 body([body, completionHandler](bool complete) {
99 if (complete) {
100 completionHandler();
101 } else {
102 asyncWhile(body, completionHandler);
103 }
104 });
105}
106
107Job<void> Async::dowhile(Condition condition, ThenTask<void> body)
108{
109 return Async::start<void>([body, condition](Async::Future<void> &future) {
110 asyncWhile([condition, body](std::function<void(bool)> whileCallback) {
111 Async::start<void>(body).then<void>([whileCallback, condition]() {
112 whileCallback(!condition());
113 }).exec();
114 },
115 [&future]() { //while complete
116 future.setFinished();
117 });
118 });
119}
120
121Job<void> Async::dowhile(ThenTask<bool> body)
122{
123 return Async::start<void>([body](Async::Future<void> &future) {
124 asyncWhile([body](std::function<void(bool)> whileCallback) {
125 Async::start<bool>(body).then<bool, bool>([whileCallback](bool result) {
126 whileCallback(!result);
127 //FIXME this return value is only required because .then<bool, void> doesn't work
128 return true;
129 }).exec();
130 },
131 [&future]() { //while complete
132 future.setFinished();
133 });
134 });
135}
136
137Job<void> Async::wait(int delay)
138{
139 auto timer = QSharedPointer<QTimer>::create();
140 return Async::start<void>([timer, delay](Async::Future<void> &future) {
141 timer->setSingleShot(true);
142 QObject::connect(timer.data(), &QTimer::timeout, [&future]() {
143 future.setFinished();
144 });
145 timer->start(delay);
146 });
147}
148
diff --git a/async/src/async.h b/async/src/async.h
deleted file mode 100644
index b1f1121..0000000
--- a/async/src/async.h
+++ /dev/null
@@ -1,872 +0,0 @@
1/*
2 * Copyright 2014 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_H
19#define ASYNC_H
20
21#include <functional>
22#include <list>
23#include <type_traits>
24#include <cassert>
25#include <iterator>
26
27#include "future.h"
28#include "debug.h"
29#include "async_impl.h"
30
31#include <QVector>
32#include <QObject>
33#include <QSharedPointer>
34
35#include <QDebug>
36
37#ifdef WITH_KJOB
38#include <KJob>
39#endif
40
41
42/*
43 * API to help write async code.
44 *
45 * This API is based around jobs that take lambdas to execute asynchronous tasks. Each async operation can take a continuation,
46 * that can then be used to execute further async operations. That way it is possible to build async chains of operations,
47 * that can be stored and executed later on. Jobs can be composed, similarly to functions.
48 *
49 * Relations between the components:
50 * * Job: API wrapper around Executors chain. Can be destroyed while still running,
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).
60 * * Future: Representation of the result that is being calculated
61 *
62 *
63 * TODO: Composed progress reporting
64 * TODO: Possibility to abort a job through future (perhaps optional?)
65 * TODO: Support for timeout, specified during exec call, after which the error handler gets called with a defined errorCode.
66 */
67namespace Async {
68
69template<typename PrevOut, typename Out, typename ... In>
70class Executor;
71
72class JobBase;
73
74template<typename Out, typename ... In>
75class Job;
76
77template<typename Out, typename ... In>
78using ThenTask = typename detail::identity<std::function<void(In ..., Async::Future<Out>&)>>::type;
79template<typename Out, typename ... In>
80using SyncThenTask = typename detail::identity<std::function<Out(In ...)>>::type;
81template<typename Out, typename In>
82using EachTask = typename detail::identity<std::function<void(In, Async::Future<Out>&)>>::type;
83template<typename Out, typename In>
84using SyncEachTask = typename detail::identity<std::function<Out(In)>>::type;
85template<typename Out, typename In>
86using ReduceTask = typename detail::identity<std::function<void(In, Async::Future<Out>&)>>::type;
87template<typename Out, typename In>
88using SyncReduceTask = typename detail::identity<std::function<Out(In)>>::type;
89
90using ErrorHandler = std::function<void(int, const QString &)>;
91using Condition = std::function<bool()>;
92
93namespace Private
94{
95
96class ExecutorBase;
97typedef QSharedPointer<ExecutorBase> ExecutorBasePtr;
98
99struct Execution {
100 Execution(const ExecutorBasePtr &executor);
101 ~Execution();
102 void setFinished();
103
104 template<typename T>
105 Async::Future<T>* result() const
106 {
107 return static_cast<Async::Future<T>*>(resultBase);
108 }
109
110 void releaseFuture();
111 bool errorWasHandled() const;
112
113 ExecutorBasePtr executor;
114 FutureBase *resultBase;
115 bool isRunning;
116 bool isFinished;
117
118 ExecutionPtr prevExecution;
119
120#ifndef QT_NO_DEBUG
121 Tracer *tracer;
122#endif
123};
124
125
126typedef QSharedPointer<Execution> ExecutionPtr;
127
128class ExecutorBase
129{
130 template<typename PrevOut, typename Out, typename ... In>
131 friend class Executor;
132
133 template<typename Out, typename ... In>
134 friend class Async::Job;
135
136 friend class Execution;
137 friend class Async::Tracer;
138
139public:
140 virtual ~ExecutorBase();
141 virtual ExecutionPtr exec(const ExecutorBasePtr &self) = 0;
142
143protected:
144 ExecutorBase(const ExecutorBasePtr &parent);
145
146 template<typename T>
147 Async::Future<T>* createFuture(const ExecutionPtr &execution) const;
148
149 virtual bool hasErrorFunc() const = 0;
150 virtual bool handleError(const ExecutionPtr &execution) = 0;
151
152 ExecutorBasePtr mPrev;
153
154#ifndef QT_NO_DEBUG
155 QString mExecutorName;
156#endif
157};
158
159template<typename PrevOut, typename Out, typename ... In>
160class Executor : public ExecutorBase
161{
162protected:
163 Executor(ErrorHandler errorFunc, const Private::ExecutorBasePtr &parent)
164 : ExecutorBase(parent)
165 , mErrorFunc(errorFunc)
166 {}
167
168 virtual ~Executor() {}
169 virtual void run(const ExecutionPtr &execution) = 0;
170
171 ExecutionPtr exec(const ExecutorBasePtr &self);
172 bool hasErrorFunc() const { return (bool) mErrorFunc; }
173 bool handleError(const ExecutionPtr &execution);
174
175 std::function<void(int, const QString &)> mErrorFunc;
176};
177
178template<typename Out, typename ... In>
179class ThenExecutor: public Executor<typename detail::prevOut<In ...>::type, Out, In ...>
180{
181public:
182 ThenExecutor(ThenTask<Out, In ...> then, ErrorHandler errorFunc, const ExecutorBasePtr &parent);
183 void run(const ExecutionPtr &execution);
184private:
185 ThenTask<Out, In ...> mFunc;
186};
187
188template<typename PrevOut, typename Out, typename In>
189class EachExecutor : public Executor<PrevOut, Out, In>
190{
191public:
192 EachExecutor(EachTask<Out, In> each, ErrorHandler errorFunc, const ExecutorBasePtr &parent);
193 void run(const ExecutionPtr &execution);
194private:
195 EachTask<Out, In> mFunc;
196 QVector<Async::FutureWatcher<Out>*> mFutureWatchers;
197};
198
199template<typename Out, typename In>
200class ReduceExecutor : public ThenExecutor<Out, In>
201{
202public:
203 ReduceExecutor(ReduceTask<Out, In> reduce, ErrorHandler errorFunc, const ExecutorBasePtr &parent);
204private:
205 ReduceTask<Out, In> mFunc;
206};
207
208template<typename Out, typename ... In>
209class SyncThenExecutor : public Executor<typename detail::prevOut<In ...>::type, Out, In ...>
210{
211public:
212 SyncThenExecutor(SyncThenTask<Out, In ...> then, ErrorHandler errorFunc, const ExecutorBasePtr &parent);
213 void run(const ExecutionPtr &execution);
214
215private:
216 void run(const ExecutionPtr &execution, std::false_type); // !std::is_void<Out>
217 void run(const ExecutionPtr &execution, std::true_type); // std::is_void<Out>
218 SyncThenTask<Out, In ...> mFunc;
219};
220
221template<typename Out, typename In>
222class SyncReduceExecutor : public SyncThenExecutor<Out, In>
223{
224public:
225 SyncReduceExecutor(SyncReduceTask<Out, In> reduce, ErrorHandler errorFunc, const ExecutorBasePtr &parent);
226private:
227 SyncReduceTask<Out, In> mFunc;
228};
229
230template<typename PrevOut, typename Out, typename In>
231class SyncEachExecutor : public Executor<PrevOut, Out, In>
232{
233public:
234 SyncEachExecutor(SyncEachTask<Out, In> each, ErrorHandler errorFunc, const ExecutorBasePtr &parent);
235 void run(const ExecutionPtr &execution);
236private:
237 void run(Async::Future<Out> *future, const typename PrevOut::value_type &arg, std::false_type); // !std::is_void<Out>
238 void run(Async::Future<Out> *future, const typename PrevOut::value_type &arg, std::true_type); // std::is_void<Out>
239 SyncEachTask<Out, In> mFunc;
240};
241
242} // namespace Private
243
244/**
245 * Start an asynchronous job sequence.
246 *
247 * Async::start() is your starting point to build a chain of jobs to be executed
248 * asynchronously.
249 *
250 * @param func An asynchronous function to be executed. The function must have
251 * void return type, and accept exactly one argument of type @p Async::Future<In>,
252 * where @p In is type of the result.
253 */
254template<typename Out, typename ... In>
255Job<Out, In ...> start(ThenTask<Out, In ...> func, ErrorHandler errorFunc = ErrorHandler());
256
257template<typename Out, typename ... In>
258Job<Out, In ...> start(SyncThenTask<Out, In ...> func, ErrorHandler errorFunc = ErrorHandler());
259
260#ifdef WITH_KJOB
261template<typename ReturnType, typename KJobType, ReturnType (KJobType::*KJobResultMethod)(), typename ... Args>
262Job<ReturnType, Args ...> start();
263#endif
264
265/**
266 * Async while loop.
267 *
268 * The loop continues while @param condition returns true.
269 */
270Job<void> dowhile(Condition condition, ThenTask<void> func);
271
272/**
273 * Async while loop.
274 *
275 * Loop continues while body returns true.
276 */
277Job<void> dowhile(ThenTask<bool> body);
278
279/**
280 * Iterate over a container.
281 *
282 * Use in conjunction with .each
283 */
284template<typename Out>
285Job<Out> iterate(const Out &container);
286
287/**
288 * Async delay.
289 */
290Job<void> wait(int delay);
291
292/**
293 * A null job.
294 *
295 * An async noop.
296 *
297 */
298template<typename Out>
299Job<Out> null();
300
301/**
302 * An error job.
303 *
304 * An async error.
305 *
306 */
307template<typename Out>
308Job<Out> error(int errorCode = 1, const QString &errorMessage = QString());
309
310class JobBase
311{
312 template<typename Out, typename ... In>
313 friend class Job;
314
315public:
316 JobBase(const Private::ExecutorBasePtr &executor);
317 ~JobBase();
318
319protected:
320 Private::ExecutorBasePtr mExecutor;
321};
322
323/**
324 * An Asynchronous job
325 *
326 * A single instance of Job represents a single method that will be executed
327 * asynchrously. The Job is started by @p Job::exec(), which returns @p Async::Future
328 * immediatelly. The Future will be set to finished state once the asynchronous
329 * task has finished. You can use @p Async::Future::waitForFinished() to wait for
330 * for the Future in blocking manner.
331 *
332 * It is possible to chain multiple Jobs one after another in different fashion
333 * (sequential, parallel, etc.). Calling Job::exec() will then return a pending
334 * @p Async::Future, and will execute the entire chain of jobs.
335 *
336 * @code
337 * auto job = Job::start<QList<int>>(
338 * [](Async::Future<QList<int>> &future) {
339 * MyREST::PendingUsers *pu = MyREST::requestListOfUsers();
340 * QObject::connect(pu, &PendingOperation::finished,
341 * [&](PendingOperation *pu) {
342 * future->setValue(dynamic_cast<MyREST::PendingUsers*>(pu)->userIds());
343 * future->setFinished();
344 * });
345 * })
346 * .each<QList<MyREST::User>, int>(
347 * [](const int &userId, Async::Future<QList<MyREST::User>> &future) {
348 * MyREST::PendingUser *pu = MyREST::requestUserDetails(userId);
349 * QObject::connect(pu, &PendingOperation::finished,
350 * [&](PendingOperation *pu) {
351 * future->setValue(Qlist<MyREST::User>() << dynamic_cast<MyREST::PendingUser*>(pu)->user());
352 * future->setFinished();
353 * });
354 * });
355 *
356 * Async::Future<QList<MyREST::User>> usersFuture = job.exec();
357 * usersFuture.waitForFinished();
358 * QList<MyRest::User> users = usersFuture.value();
359 * @endcode
360 *
361 * In the example above, calling @p job.exec() will first invoke the first job,
362 * which will retrieve a list of IDs, and then will invoke the second function
363 * for each single entry in the list returned by the first function.
364 */
365template<typename Out, typename ... In>
366class Job : public JobBase
367{
368 template<typename OutOther, typename ... InOther>
369 friend class Job;
370
371 template<typename OutOther, typename ... InOther>
372 friend Job<OutOther, InOther ...> start(Async::ThenTask<OutOther, InOther ...> func, ErrorHandler errorFunc);
373
374 template<typename OutOther, typename ... InOther>
375 friend Job<OutOther, InOther ...> start(Async::SyncThenTask<OutOther, InOther ...> func, ErrorHandler errorFunc);
376
377#ifdef WITH_KJOB
378 template<typename ReturnType, typename KJobType, ReturnType (KJobType::*KJobResultMethod)(), typename ... Args>
379 friend Job<ReturnType, Args ...> start();
380#endif
381
382public:
383 template<typename OutOther, typename ... InOther>
384 Job<OutOther, InOther ...> then(ThenTask<OutOther, InOther ...> func, ErrorHandler errorFunc = ErrorHandler())
385 {
386 return Job<OutOther, InOther ...>(Private::ExecutorBasePtr(
387 new Private::ThenExecutor<OutOther, InOther ...>(func, errorFunc, mExecutor)));
388 }
389
390 template<typename OutOther, typename ... InOther>
391 Job<OutOther, InOther ...> then(SyncThenTask<OutOther, InOther ...> func, ErrorHandler errorFunc = ErrorHandler())
392 {
393 return Job<OutOther, InOther ...>(Private::ExecutorBasePtr(
394 new Private::SyncThenExecutor<OutOther, InOther ...>(func, errorFunc, mExecutor)));
395 }
396
397 template<typename OutOther, typename ... InOther>
398 Job<OutOther, InOther ...> then(Job<OutOther, InOther ...> otherJob, ErrorHandler errorFunc = ErrorHandler())
399 {
400 return then<OutOther, InOther ...>(nestedJobWrapper<OutOther, InOther ...>(otherJob), errorFunc);
401 }
402
403#ifdef WITH_KJOB
404 template<typename ReturnType, typename KJobType, ReturnType (KJobType::*KJobResultMethod)(), typename ... Args>
405 Job<ReturnType, Args ...> then()
406 {
407 return start<ReturnType, KJobType, KJobResultMethod, Args ...>();
408 }
409#endif
410
411 template<typename OutOther, typename InOther>
412 Job<OutOther, InOther> each(EachTask<OutOther, InOther> func, ErrorHandler errorFunc = ErrorHandler())
413 {
414 eachInvariants<OutOther>();
415 return Job<OutOther, InOther>(Private::ExecutorBasePtr(
416 new Private::EachExecutor<Out, OutOther, InOther>(func, errorFunc, mExecutor)));
417 }
418
419 template<typename OutOther, typename InOther>
420 Job<OutOther, InOther> each(SyncEachTask<OutOther, InOther> func, ErrorHandler errorFunc = ErrorHandler())
421 {
422 eachInvariants<OutOther>();
423 return Job<OutOther, InOther>(Private::ExecutorBasePtr(
424 new Private::SyncEachExecutor<Out, OutOther, InOther>(func, errorFunc, mExecutor)));
425 }
426
427 template<typename OutOther, typename InOther>
428 Job<OutOther, InOther> each(Job<OutOther, InOther> otherJob, ErrorHandler errorFunc = ErrorHandler())
429 {
430 eachInvariants<OutOther>();
431 return each<OutOther, InOther>(nestedJobWrapper<OutOther, InOther>(otherJob), errorFunc);
432 }
433
434 template<typename OutOther, typename InOther>
435 Job<OutOther, InOther> reduce(ReduceTask<OutOther, InOther> func, ErrorHandler errorFunc = ErrorHandler())
436 {
437 reduceInvariants<InOther>();
438 return Job<OutOther, InOther>(Private::ExecutorBasePtr(
439 new Private::ReduceExecutor<OutOther, InOther>(func, errorFunc, mExecutor)));
440 }
441
442 template<typename OutOther, typename InOther>
443 Job<OutOther, InOther> reduce(SyncReduceTask<OutOther, InOther> func, ErrorHandler errorFunc = ErrorHandler())
444 {
445 reduceInvariants<InOther>();
446 return Job<OutOther, InOther>(Private::ExecutorBasePtr(
447 new Private::SyncReduceExecutor<OutOther, InOther>(func, errorFunc, mExecutor)));
448 }
449
450 template<typename OutOther, typename InOther>
451 Job<OutOther, InOther> reduce(Job<OutOther, InOther> otherJob, ErrorHandler errorFunc = ErrorHandler())
452 {
453 return reduce<OutOther, InOther>(nestedJobWrapper<OutOther, InOther>(otherJob), errorFunc);
454 }
455
456 template<typename FirstIn>
457 Async::Future<Out> exec(FirstIn in)
458 {
459 // Inject a fake sync executor that will return the initial value
460 Private::ExecutorBasePtr first = mExecutor;
461 while (first->mPrev) {
462 first = first->mPrev;
463 }
464 auto init = new Private::SyncThenExecutor<FirstIn>(
465 [in]() -> FirstIn {
466 return in;
467 },
468 ErrorHandler(), Private::ExecutorBasePtr());
469 first->mPrev = Private::ExecutorBasePtr(init);
470
471 auto result = exec();
472 // Remove the injected executor
473 first->mPrev.reset();
474 return result;
475 }
476
477 Async::Future<Out> exec()
478 {
479 Private::ExecutionPtr execution = mExecutor->exec(mExecutor);
480 Async::Future<Out> result = *execution->result<Out>();
481
482 return result;
483 }
484
485private:
486 Job(Private::ExecutorBasePtr executor)
487 : JobBase(executor)
488 {}
489
490 template<typename OutOther>
491 void eachInvariants()
492 {
493 static_assert(detail::isIterable<Out>::value,
494 "The 'Each' task can only be connected to a job that returns a list or an array.");
495 static_assert(std::is_void<OutOther>::value || detail::isIterable<OutOther>::value,
496 "The result type of 'Each' task must be void, a list or an array.");
497 }
498
499 template<typename InOther>
500 void reduceInvariants()
501 {
502 static_assert(Async::detail::isIterable<Out>::value,
503 "The 'Result' task can only be connected to a job that returns a list or an array");
504 static_assert(std::is_same<typename Out::value_type, typename InOther::value_type>::value,
505 "The return type of previous task must be compatible with input type of this task");
506 }
507
508 template<typename OutOther, typename ... InOther>
509 inline std::function<void(InOther ..., Async::Future<OutOther>&)> nestedJobWrapper(Job<OutOther, InOther ...> otherJob) {
510 return [otherJob](InOther ... in, Async::Future<OutOther> &future) {
511 // copy by value is const
512 auto job = otherJob;
513 FutureWatcher<OutOther> *watcher = new FutureWatcher<OutOther>();
514 QObject::connect(watcher, &FutureWatcherBase::futureReady,
515 [watcher, future]() {
516 // FIXME: We pass future by value, because using reference causes the
517 // future to get deleted before this lambda is invoked, leading to crash
518 // in copyFutureValue()
519 // copy by value is const
520 auto outFuture = future;
521 Async::detail::copyFutureValue(watcher->future(), outFuture);
522 if (watcher->future().errorCode()) {
523 outFuture.setError(watcher->future().errorCode(), watcher->future().errorMessage());
524 } else {
525 outFuture.setFinished();
526 }
527 delete watcher;
528 });
529 watcher->setFuture(job.exec(in ...));
530 };
531 }
532};
533
534} // namespace Async
535
536
537// ********** Out of line definitions ****************
538
539namespace Async {
540
541template<typename Out, typename ... In>
542Job<Out, In ...> start(ThenTask<Out, In ...> func, ErrorHandler error)
543{
544 return Job<Out, In...>(Private::ExecutorBasePtr(
545 new Private::ThenExecutor<Out, In ...>(func, error, Private::ExecutorBasePtr())));
546}
547
548template<typename Out, typename ... In>
549Job<Out, In ...> start(SyncThenTask<Out, In ...> func, ErrorHandler error)
550{
551 return Job<Out, In...>(Private::ExecutorBasePtr(
552 new Private::SyncThenExecutor<Out, In ...>(func, error, Private::ExecutorBasePtr())));
553}
554
555#ifdef WITH_KJOB
556template<typename ReturnType, typename KJobType, ReturnType (KJobType::*KJobResultMethod)(), typename ... Args>
557Job<ReturnType, Args ...> start()
558{
559 return Job<ReturnType, Args ...>(Private::ExecutorBasePtr(
560 new Private::ThenExecutor<ReturnType, Args ...>([](const Args & ... args, Async::Future<ReturnType> &future)
561 {
562 KJobType *job = new KJobType(args ...);
563 job->connect(job, &KJob::finished,
564 [&future](KJob *job) {
565 if (job->error()) {
566 future.setError(job->error(), job->errorString());
567 } else {
568 future.setValue((static_cast<KJobType*>(job)->*KJobResultMethod)());
569 future.setFinished();
570 }
571 });
572 job->start();
573 }, ErrorHandler(), Private::ExecutorBasePtr())));
574}
575#endif
576
577
578template<typename Out>
579Job<Out> null()
580{
581 return Async::start<Out>(
582 [](Async::Future<Out> &future) {
583 future.setFinished();
584 });
585}
586
587template<typename Out>
588Job<Out> error(int errorCode, const QString &errorMessage)
589{
590 return Async::start<Out>(
591 [errorCode, errorMessage](Async::Future<Out> &future) {
592 future.setError(errorCode, errorMessage);
593 });
594}
595
596template<typename Out>
597Job<Out> iterate(const Out &container)
598{
599 return Async::start<Out>(
600 [container]() {
601 return container;
602 });
603}
604
605
606namespace Private {
607
608template<typename T>
609Async::Future<T>* ExecutorBase::createFuture(const ExecutionPtr &execution) const
610{
611 return new Async::Future<T>(execution);
612}
613
614template<typename PrevOut, typename Out, typename ... In>
615ExecutionPtr Executor<PrevOut, Out, In ...>::exec(const ExecutorBasePtr &self)
616{
617 // Passing 'self' to execution ensures that the Executor chain remains
618 // valid until the entire execution is finished
619 ExecutionPtr execution = ExecutionPtr::create(self);
620#ifndef QT_NO_DEBUG
621 execution->tracer = new Tracer(execution.data()); // owned by execution
622#endif
623
624 // chainup
625 execution->prevExecution = mPrev ? mPrev->exec(mPrev) : ExecutionPtr();
626
627 execution->resultBase = ExecutorBase::createFuture<Out>(execution);
628 auto fw = new Async::FutureWatcher<Out>();
629 QObject::connect(fw, &Async::FutureWatcher<Out>::futureReady,
630 [fw, execution, this]() {
631 handleError(execution);
632 execution->setFinished();
633 delete fw;
634 });
635 fw->setFuture(*execution->result<Out>());
636
637 Async::Future<PrevOut> *prevFuture = execution->prevExecution ? execution->prevExecution->result<PrevOut>() : nullptr;
638 if (!prevFuture || prevFuture->isFinished()) {
639 if (prevFuture) { // prevFuture implies execution->prevExecution
640 if (prevFuture->errorCode()) {
641 // Propagate the errorCode and message to the outer Future
642 execution->resultBase->setError(prevFuture->errorCode(), prevFuture->errorMessage());
643 if (!execution->errorWasHandled()) {
644 if (handleError(execution)) {
645 return execution;
646 }
647 } else {
648 return execution;
649 }
650 } else {
651 // Propagate error (if any)
652 }
653 }
654
655 execution->isRunning = true;
656 run(execution);
657 } else {
658 auto prevFutureWatcher = new Async::FutureWatcher<PrevOut>();
659 QObject::connect(prevFutureWatcher, &Async::FutureWatcher<PrevOut>::futureReady,
660 [prevFutureWatcher, execution, this]() {
661 auto prevFuture = prevFutureWatcher->future();
662 assert(prevFuture.isFinished());
663 delete prevFutureWatcher;
664 auto prevExecutor = execution->executor->mPrev;
665 if (prevFuture.errorCode()) {
666 execution->resultBase->setError(prevFuture.errorCode(), prevFuture.errorMessage());
667 if (!execution->errorWasHandled()) {
668 if (handleError(execution)) {
669 return;
670 }
671 } else {
672 return;
673 }
674 }
675
676
677 // propagate error (if any)
678 execution->isRunning = true;
679 run(execution);
680 });
681
682 prevFutureWatcher->setFuture(*static_cast<Async::Future<PrevOut>*>(prevFuture));
683 }
684
685 return execution;
686}
687
688template<typename PrevOut, typename Out, typename ... In>
689bool Executor<PrevOut, Out, In ...>::handleError(const ExecutionPtr &execution)
690{
691 assert(execution->resultBase->isFinished());
692 if (execution->resultBase->errorCode()) {
693 if (mErrorFunc) {
694 mErrorFunc(execution->resultBase->errorCode(),
695 execution->resultBase->errorMessage());
696 return true;
697 }
698 }
699
700 return false;
701}
702
703
704template<typename Out, typename ... In>
705ThenExecutor<Out, In ...>::ThenExecutor(ThenTask<Out, In ...> then, ErrorHandler error, const ExecutorBasePtr &parent)
706 : Executor<typename detail::prevOut<In ...>::type, Out, In ...>(error, parent)
707 , mFunc(then)
708{
709 STORE_EXECUTOR_NAME("ThenExecutor", Out, In ...);
710}
711
712template<typename Out, typename ... In>
713void ThenExecutor<Out, In ...>::run(const ExecutionPtr &execution)
714{
715 Async::Future<typename detail::prevOut<In ...>::type> *prevFuture = nullptr;
716 if (execution->prevExecution) {
717 prevFuture = execution->prevExecution->result<typename detail::prevOut<In ...>::type>();
718 assert(prevFuture->isFinished());
719 }
720
721 ThenExecutor<Out, In ...>::mFunc(prevFuture ? prevFuture->value() : In() ..., *execution->result<Out>());
722}
723
724template<typename PrevOut, typename Out, typename In>
725EachExecutor<PrevOut, Out, In>::EachExecutor(EachTask<Out, In> each, ErrorHandler error, const ExecutorBasePtr &parent)
726 : Executor<PrevOut, Out, In>(error, parent)
727 , mFunc(each)
728{
729 STORE_EXECUTOR_NAME("EachExecutor", PrevOut, Out, In);
730}
731
732template<typename PrevOut, typename Out, typename In>
733void EachExecutor<PrevOut, Out, In>::run(const ExecutionPtr &execution)
734{
735 assert(execution->prevExecution);
736 auto prevFuture = execution->prevExecution->result<PrevOut>();
737 assert(prevFuture->isFinished());
738
739 auto out = execution->result<Out>();
740 if (prevFuture->value().isEmpty()) {
741 out->setFinished();
742 return;
743 }
744
745 for (auto arg : prevFuture->value()) {
746 //We have to manually manage the lifetime of these temporary futures
747 Async::Future<Out> *future = new Async::Future<Out>();
748 EachExecutor<PrevOut, Out, In>::mFunc(arg, *future);
749 auto fw = new Async::FutureWatcher<Out>();
750 mFutureWatchers.append(fw);
751 QObject::connect(fw, &Async::FutureWatcher<Out>::futureReady,
752 [out, fw, this, future]() {
753 assert(fw->future().isFinished());
754 const int index = mFutureWatchers.indexOf(fw);
755 assert(index > -1);
756 mFutureWatchers.removeAt(index);
757 Async::detail::aggregateFutureValue<Out>(fw->future(), *out);
758 if (mFutureWatchers.isEmpty()) {
759 out->setFinished();
760 }
761 delete fw;
762 delete future;
763 });
764 fw->setFuture(*future);
765 }
766}
767
768template<typename Out, typename In>
769ReduceExecutor<Out, In>::ReduceExecutor(ReduceTask<Out, In> reduce, ErrorHandler errorFunc, const ExecutorBasePtr &parent)
770 : ThenExecutor<Out, In>(reduce, errorFunc, parent)
771{
772 STORE_EXECUTOR_NAME("ReduceExecutor", Out, In);
773}
774
775template<typename Out, typename ... In>
776SyncThenExecutor<Out, In ...>::SyncThenExecutor(SyncThenTask<Out, In ...> then, ErrorHandler errorFunc, const ExecutorBasePtr &parent)
777 : Executor<typename detail::prevOut<In ...>::type, Out, In ...>(errorFunc, parent)
778 , mFunc(then)
779{
780 STORE_EXECUTOR_NAME("SyncThenExecutor", Out, In ...);
781}
782
783template<typename Out, typename ... In>
784void SyncThenExecutor<Out, In ...>::run(const ExecutionPtr &execution)
785{
786 if (execution->prevExecution) {
787 assert(execution->prevExecution->resultBase->isFinished());
788 }
789
790 run(execution, std::is_void<Out>());
791 execution->resultBase->setFinished();
792}
793
794template<typename Out, typename ... In>
795void SyncThenExecutor<Out, In ...>::run(const ExecutionPtr &execution, std::false_type)
796{
797 Async::Future<typename detail::prevOut<In ...>::type> *prevFuture =
798 execution->prevExecution
799 ? execution->prevExecution->result<typename detail::prevOut<In ...>::type>()
800 : nullptr;
801 (void) prevFuture; // silence 'set but not used' warning
802 Async::Future<Out> *future = execution->result<Out>();
803 future->setValue(SyncThenExecutor<Out, In...>::mFunc(prevFuture ? prevFuture->value() : In() ...));
804}
805
806template<typename Out, typename ... In>
807void SyncThenExecutor<Out, In ...>::run(const ExecutionPtr &execution, std::true_type)
808{
809 Async::Future<typename detail::prevOut<In ...>::type> *prevFuture =
810 execution->prevExecution
811 ? execution->prevExecution->result<typename detail::prevOut<In ...>::type>()
812 : nullptr;
813 (void) prevFuture; // silence 'set but not used' warning
814 SyncThenExecutor<Out, In ...>::mFunc(prevFuture ? prevFuture->value() : In() ...);
815}
816
817template<typename PrevOut, typename Out, typename In>
818SyncEachExecutor<PrevOut, Out, In>::SyncEachExecutor(SyncEachTask<Out, In> each, ErrorHandler errorFunc, const ExecutorBasePtr &parent)
819 : Executor<PrevOut, Out, In>(errorFunc, parent)
820 , mFunc(each)
821{
822 STORE_EXECUTOR_NAME("SyncEachExecutor", PrevOut, Out, In);
823}
824
825template<typename PrevOut, typename Out, typename In>
826void SyncEachExecutor<PrevOut, Out, In>::run(const ExecutionPtr &execution)
827{
828 assert(execution->prevExecution);
829 auto *prevFuture = execution->prevExecution->result<PrevOut>();
830 assert(prevFuture->isFinished());
831
832 auto out = execution->result<Out>();
833 if (prevFuture->value().isEmpty()) {
834 out->setFinished();
835 return;
836 }
837
838 for (auto arg : prevFuture->value()) {
839 run(out, arg, std::is_void<Out>());
840 }
841 out->setFinished();
842}
843
844template<typename PrevOut, typename Out, typename In>
845void SyncEachExecutor<PrevOut, Out, In>::run(Async::Future<Out> *out, const typename PrevOut::value_type &arg, std::false_type)
846{
847 out->setValue(out->value() + SyncEachExecutor<PrevOut, Out, In>::mFunc(arg));
848}
849
850template<typename PrevOut, typename Out, typename In>
851void SyncEachExecutor<PrevOut, Out, In>::run(Async::Future<Out> * /* unused */, const typename PrevOut::value_type &arg, std::true_type)
852{
853 SyncEachExecutor<PrevOut, Out, In>::mFunc(arg);
854}
855
856template<typename Out, typename In>
857SyncReduceExecutor<Out, In>::SyncReduceExecutor(SyncReduceTask<Out, In> reduce, ErrorHandler errorFunc, const ExecutorBasePtr &parent)
858 : SyncThenExecutor<Out, In>(reduce, errorFunc, parent)
859{
860 STORE_EXECUTOR_NAME("SyncReduceExecutor", Out, In);
861}
862
863
864} // namespace Private
865
866} // namespace Async
867
868
869
870#endif // ASYNC_H
871
872
diff --git a/async/src/async_impl.h b/async/src/async_impl.h
deleted file mode 100644
index 8c74193..0000000
--- a/async/src/async_impl.h
+++ /dev/null
@@ -1,81 +0,0 @@
1/*
2 * Copyright 2014 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_IMPL_H
19#define ASYNC_IMPL_H
20
21#include "async.h"
22#include <type_traits>
23
24namespace Async {
25
26namespace detail {
27
28template<typename T>
29struct identity
30{
31 typedef T type;
32};
33
34template<typename T, typename Enable = void>
35struct isIterable {
36 enum { value = 0 };
37};
38
39template<typename T>
40struct isIterable<T, typename std::conditional<false, typename T::iterator, void>::type> {
41 enum { value = 1 };
42};
43
44template<typename ... T>
45struct prevOut {
46 using type = typename std::tuple_element<0, std::tuple<T ..., void>>::type;
47};
48
49template<typename T>
50inline typename std::enable_if<!std::is_void<T>::value, void>::type
51copyFutureValue(const Async::Future<T> &in, Async::Future<T> &out)
52{
53 out.setValue(in.value());
54}
55
56template<typename T>
57inline typename std::enable_if<std::is_void<T>::value, void>::type
58copyFutureValue(const Async::Future<T> &in, Async::Future<T> &out)
59{
60 // noop
61}
62
63template<typename T>
64inline typename std::enable_if<!std::is_void<T>::value, void>::type
65aggregateFutureValue(const Async::Future<T> &in, Async::Future<T> &out)
66{
67 out.setValue(out.value() + in.value());
68}
69
70template<typename T>
71inline typename std::enable_if<std::is_void<T>::value, void>::type
72aggregateFutureValue(const Async::Future<T> &in, Async::Future<T> &out)
73{
74 // noop
75}
76
77} // namespace Detail
78
79} // namespace Async
80
81#endif // ASYNC_IMPL_H
diff --git a/async/src/debug.cpp b/async/src/debug.cpp
deleted file mode 100644
index fdf2fa1..0000000
--- a/async/src/debug.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
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#ifndef QT_NO_DEBUG
70 qCDebug(Trace).nospace() << (QString().fill(QLatin1Char(' '), mId * 2) %
71 (msgType == Async::Tracer::Start ? " START " : " END ") %
72 QString::number(mId) % " " %
73 mExecution->executor->mExecutorName);
74#endif
75}
diff --git a/async/src/debug.h b/async/src/debug.h
deleted file mode 100644
index c453eb3..0000000
--- a/async/src/debug.h
+++ /dev/null
@@ -1,80 +0,0 @@
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
deleted file mode 100644
index 4f3cd80..0000000
--- a/async/src/future.cpp
+++ /dev/null
@@ -1,158 +0,0 @@
1/*
2 * Copyright 2014 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 "future.h"
19#include "async.h"
20
21using namespace Async;
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
46FutureBase::FutureBase()
47 : d(nullptr)
48{
49}
50
51FutureBase::FutureBase(FutureBase::PrivateBase *dd)
52 : d(dd)
53{
54}
55
56FutureBase::FutureBase(const Async::FutureBase &other)
57 : d(other.d)
58{
59}
60
61FutureBase::~FutureBase()
62{
63}
64
65void FutureBase::releaseExecution()
66{
67 d->releaseExecution();
68}
69
70void FutureBase::setFinished()
71{
72 if (isFinished()) {
73 return;
74 }
75 d->finished = true;
76 for (auto watcher : d->watchers) {
77 if (watcher) {
78 watcher->futureReadyCallback();
79 }
80 }
81}
82
83bool FutureBase::isFinished() const
84{
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 }
117}
118
119
120
121void FutureBase::addWatcher(FutureWatcherBase* watcher)
122{
123 d->watchers.append(QPointer<FutureWatcherBase>(watcher));
124}
125
126
127
128
129
130FutureWatcherBase::FutureWatcherBase(QObject *parent)
131 : QObject(parent)
132 , d(new FutureWatcherBase::Private)
133{
134}
135
136FutureWatcherBase::~FutureWatcherBase()
137{
138 delete d;
139}
140
141void FutureWatcherBase::futureReadyCallback()
142{
143 Q_EMIT futureReady();
144}
145
146void FutureWatcherBase::futureProgressCallback(qreal progress)
147{
148 Q_EMIT futureProgress(progress);
149}
150
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
deleted file mode 100644
index ff199ef..0000000
--- a/async/src/future.h
+++ /dev/null
@@ -1,255 +0,0 @@
1/*
2 * Copyright 2014 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 FUTURE_H
19#define FUTURE_H
20
21class QEventLoop;
22
23#include <type_traits>
24
25#include <QSharedDataPointer>
26#include <QPointer>
27#include <QVector>
28#include <QEventLoop>
29
30namespace Async {
31
32class FutureWatcherBase;
33template<typename T>
34class FutureWatcher;
35
36namespace Private {
37class Execution;
38class ExecutorBase;
39
40typedef QSharedPointer<Execution> ExecutionPtr;
41} // namespace Private
42
43class FutureBase
44{
45 friend class Async::Private::Execution;
46 friend class FutureWatcherBase;
47
48public:
49 virtual ~FutureBase();
50
51 void setFinished();
52 bool isFinished() const;
53 void setError(int code = 1, const QString &message = QString());
54 int errorCode() const;
55 QString errorMessage() const;
56
57 void setProgress(qreal progress);
58 void setProgress(int processed, int total);
59
60protected:
61 class PrivateBase : public QSharedData
62 {
63 public:
64 PrivateBase(const Async::Private::ExecutionPtr &execution);
65 virtual ~PrivateBase();
66
67 void releaseExecution();
68
69 bool finished;
70 int errorCode;
71 QString errorMessage;
72
73 QVector<QPointer<FutureWatcherBase>> watchers;
74 private:
75 QWeakPointer<Async::Private::Execution> mExecution;
76 };
77
78 FutureBase();
79 FutureBase(FutureBase::PrivateBase *dd);
80 FutureBase(const FutureBase &other);
81
82 void addWatcher(Async::FutureWatcherBase *watcher);
83 void releaseExecution();
84
85protected:
86 QExplicitlySharedDataPointer<PrivateBase> d;
87};
88
89template<typename T>
90class FutureWatcher;
91
92template<typename T>
93class Future;
94
95template<typename T>
96class FutureGeneric : public FutureBase
97{
98 friend class FutureWatcher<T>;
99
100public:
101 void waitForFinished() const
102 {
103 if (isFinished()) {
104 return;
105 }
106 FutureWatcher<T> watcher;
107 QEventLoop eventLoop;
108 QObject::connect(&watcher, &Async::FutureWatcher<T>::futureReady,
109 &eventLoop, &QEventLoop::quit);
110 watcher.setFuture(*static_cast<const Async::Future<T>*>(this));
111 eventLoop.exec();
112 }
113
114protected:
115 FutureGeneric(const Async::Private::ExecutionPtr &execution)
116 : FutureBase(new Private(execution))
117 {}
118
119 FutureGeneric(const FutureGeneric<T> &other)
120 : FutureBase(other)
121 {}
122
123protected:
124 class Private : public FutureBase::PrivateBase
125 {
126 public:
127 Private(const Async::Private::ExecutionPtr &execution)
128 : FutureBase::PrivateBase(execution)
129 {}
130
131 typename std::conditional<std::is_void<T>::value, int /* dummy */, T>::type
132 value;
133 };
134};
135
136
137template<typename T>
138class Future : public FutureGeneric<T>
139{
140 friend class Async::Private::ExecutorBase;
141
142 template<typename T_>
143 friend class Async::FutureWatcher;
144
145public:
146 Future()
147 : FutureGeneric<T>(Async::Private::ExecutionPtr())
148 {}
149
150 Future(const Future<T> &other)
151 : FutureGeneric<T>(other)
152 {}
153
154 void setValue(const T &value)
155 {
156 static_cast<typename FutureGeneric<T>::Private*>(this->d.data())->value = value;
157 }
158
159 T value() const
160 {
161 return static_cast<typename FutureGeneric<T>::Private*>(this->d.data())->value;
162 }
163
164protected:
165 Future(const Async::Private::ExecutionPtr &execution)
166 : FutureGeneric<T>(execution)
167 {}
168
169};
170
171template<>
172class Future<void> : public FutureGeneric<void>
173{
174 friend class Async::Private::ExecutorBase;
175
176public:
177 Future()
178 : FutureGeneric<void>(Async::Private::ExecutionPtr())
179 {}
180
181 Future(const Future<void> &other)
182 : FutureGeneric<void>(other)
183 {}
184
185protected:
186 Future(const Async::Private::ExecutionPtr &execution)
187 : FutureGeneric<void>(execution)
188 {}
189};
190
191
192
193
194
195class FutureWatcherBase : public QObject
196{
197 Q_OBJECT
198
199 friend class FutureBase;
200
201Q_SIGNALS:
202 void futureReady();
203 void futureProgress(qreal progress);
204
205protected:
206 FutureWatcherBase(QObject *parent = nullptr);
207 virtual ~FutureWatcherBase();
208
209 void futureReadyCallback();
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);
224};
225
226template<typename T>
227class FutureWatcher : public FutureWatcherBase
228{
229 friend class Async::FutureGeneric<T>;
230
231public:
232 FutureWatcher(QObject *parent = nullptr)
233 : FutureWatcherBase(parent)
234 {}
235
236 ~FutureWatcher()
237 {}
238
239 void setFuture(const Async::Future<T> &future)
240 {
241 setFutureImpl(*static_cast<const Async::FutureBase*>(&future));
242 }
243
244 Async::Future<T> future() const
245 {
246 return *static_cast<Async::Future<T>*>(&d->future);
247 }
248
249private:
250 Q_DISABLE_COPY(FutureWatcher<T>);
251};
252
253} // namespace Async
254
255#endif // FUTURE_H