From 9d0a8497f9693bccdd1c031104ea5837dc62e4ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Vr=C3=A1til?= Date: Fri, 3 Apr 2015 16:34:05 +0200 Subject: Async: fix crash in Job::nestedJobWrapper --- async/autotests/asynctest.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'async/autotests/asynctest.cpp') diff --git a/async/autotests/asynctest.cpp b/async/autotests/asynctest.cpp index c507721..61ab8bc 100644 --- a/async/autotests/asynctest.cpp +++ b/async/autotests/asynctest.cpp @@ -340,8 +340,6 @@ void AsyncTest::testSyncEach() void AsyncTest::testJoinedEach() { - QFAIL("Crashes due to bad lifetime of Future"); - auto job1 = Async::start, int>( [](int v, Async::Future> &future) { new AsyncSimulator>(future, { v * 2 }); -- cgit v1.2.3 From 27bc3300d670d65b9c5332308605ef8263c3939c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Vr=C3=A1til?= Date: Fri, 3 Apr 2015 18:17:12 +0200 Subject: Async: implement progress reporting through future This is a simplified progress reporting, since it does not report progress of ther overcall Job chain, but only of individual tasks, which makes it only really useful on one-task Jobs. TODO: propagate subjob progress to the Future user gets copy of TODO: compound progress reporting (be able to report a progress of the overall Job chain) --- async/autotests/asynctest.cpp | 62 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) (limited to 'async/autotests/asynctest.cpp') diff --git a/async/autotests/asynctest.cpp b/async/autotests/asynctest.cpp index 61ab8bc..7b8d430 100644 --- a/async/autotests/asynctest.cpp +++ b/async/autotests/asynctest.cpp @@ -60,6 +60,7 @@ private Q_SLOTS: void testJoinedReduce(); void testVoidReduce(); + void testProgressReporting(); void testErrorHandler(); void testChainingRunningJob(); @@ -69,6 +70,7 @@ private Q_SLOTS: void testLifetimeWithHandle(); void benchmarkSyncThenExecutor(); + void benchmarkAllTests(); private: template @@ -471,6 +473,40 @@ void AsyncTest::testVoidReduce() } +void AsyncTest::testProgressReporting() +{ + static int progress; + progress = 0; + + auto job = Async::start( + [](Async::Future &f) { + QTimer *timer = new QTimer(); + connect(timer, &QTimer::timeout, + [&f, timer]() { + f.setProgress(++progress); + if (progress == 100) { + timer->stop(); + timer->deleteLater(); + f.setFinished(); + } + }); + timer->start(1); + }); + + int progressCheck = 0; + Async::FutureWatcher watcher; + connect(&watcher, &Async::FutureWatcher::futureProgress, + [&progressCheck](qreal progress) { + progressCheck++; + // FIXME: Don't use Q_ASSERT in unit tests + Q_ASSERT((int) progress == progressCheck); + }); + watcher.setFuture(job.exec()); + watcher.future().waitForFinished(); + + QVERIFY(watcher.future().isFinished()); + QCOMPARE(progressCheck, 100); +} void AsyncTest::testErrorHandler() { @@ -634,6 +670,32 @@ void AsyncTest::benchmarkSyncThenExecutor() } } +void AsyncTest::benchmarkAllTests() +{ + QBENCHMARK { + testSyncPromises(); + testAsyncPromises(); + testAsyncPromises2(); + testNestedAsync(); + testStartValue(); + + testAsyncThen(); + testSyncThen(); + testJoinedThen(); + testVoidThen(); + + testAsyncEach(); + testSyncEach(); + testJoinedEach(); + testVoidEach(); + + testAsyncReduce(); + testSyncReduce(); + testJoinedReduce(); + testVoidReduce(); + } +} + QTEST_MAIN(AsyncTest); -- cgit v1.2.3 From 1cb20215e384712ccbc98f3ecc4711451730c696 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Vr=C3=A1til?= Date: Fri, 3 Apr 2015 18:52:38 +0200 Subject: Async: remove the silly all-tests benchmark --- async/autotests/asynctest.cpp | 29 ----------------------------- 1 file changed, 29 deletions(-) (limited to 'async/autotests/asynctest.cpp') diff --git a/async/autotests/asynctest.cpp b/async/autotests/asynctest.cpp index 7b8d430..65e604f 100644 --- a/async/autotests/asynctest.cpp +++ b/async/autotests/asynctest.cpp @@ -70,7 +70,6 @@ private Q_SLOTS: void testLifetimeWithHandle(); void benchmarkSyncThenExecutor(); - void benchmarkAllTests(); private: template @@ -670,34 +669,6 @@ void AsyncTest::benchmarkSyncThenExecutor() } } -void AsyncTest::benchmarkAllTests() -{ - QBENCHMARK { - testSyncPromises(); - testAsyncPromises(); - testAsyncPromises2(); - testNestedAsync(); - testStartValue(); - - testAsyncThen(); - testSyncThen(); - testJoinedThen(); - testVoidThen(); - - testAsyncEach(); - testSyncEach(); - testJoinedEach(); - testVoidEach(); - - testAsyncReduce(); - testSyncReduce(); - testJoinedReduce(); - testVoidReduce(); - } -} - - - QTEST_MAIN(AsyncTest); #include "asynctest.moc" -- cgit v1.2.3 From c4fc276f4af964ce586e009ea3d0c728bb660331 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Vr=C3=A1til?= Date: Sun, 5 Apr 2015 01:05:55 +0200 Subject: Async: improve error handling and error propagation The error is now propagated to the top-most (user-owned) Future. When an error occurs, no further tasks are executed. The first error handler function in the chain is also called, if there's any. --- async/autotests/asynctest.cpp | 166 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 151 insertions(+), 15 deletions(-) (limited to 'async/autotests/asynctest.cpp') diff --git a/async/autotests/asynctest.cpp b/async/autotests/asynctest.cpp index 65e604f..d709567 100644 --- a/async/autotests/asynctest.cpp +++ b/async/autotests/asynctest.cpp @@ -27,6 +27,8 @@ #include #include +#include + class AsyncTest : public QObject { Q_OBJECT @@ -62,6 +64,9 @@ private Q_SLOTS: void testProgressReporting(); void testErrorHandler(); + void testErrorPropagation(); + void testErrorHandlerAsync(); + void testErrorPropagationAsync(); void testChainingRunningJob(); void testChainingFinishedJob(); @@ -92,8 +97,25 @@ private: mTimer.start(200); } + AsyncSimulator(Async::Future &future, std::function&)> callback) + : mFuture(future) + , mCallback(callback) + { + QObject::connect(&mTimer, &QTimer::timeout, + [this]() { + mCallback(mFuture); + }); + QObject::connect(&mTimer, &QTimer::timeout, + [this]() { + delete this; + }); + mTimer.setSingleShot(true); + mTimer.start(200); + } + private: Async::Future mFuture; + std::function&)> mCallback; T mResult; QTimer mTimer; }; @@ -390,18 +412,14 @@ void AsyncTest::testAsyncReduce() }) .reduce>( [](const QList &list, Async::Future &future) { - QTimer *timer = new QTimer(); - QObject::connect(timer, &QTimer::timeout, - [list, &future]() { - int sum = 0; - for (int i : list) sum += i; - future.setValue(sum); - future.setFinished(); - }); - QObject::connect(timer, &QTimer::timeout, - timer, &QObject::deleteLater); - timer->setSingleShot(true); - timer->start(0); + new AsyncSimulator(future, + [list](Async::Future &future) { + int sum = 0; + for (int i : list) sum += i; + future.setValue(sum); + future.setFinished(); + } + ); }); Async::Future future = job.exec(); @@ -508,28 +526,146 @@ void AsyncTest::testProgressReporting() } void AsyncTest::testErrorHandler() +{ + + { + auto job = Async::start( + [](Async::Future &f) { + f.setError(1, "error"); + }); + + auto future = job.exec(); + QVERIFY(future.isFinished()); + QCOMPARE(future.errorCode(), 1); + QCOMPARE(future.errorMessage(), QString::fromLatin1("error")); + } + + { + int error = 0; + auto job = Async::start( + [](Async::Future &f) { + f.setError(1, "error"); + }, + [&error](int errorCode, const QString &errorMessage) { + error += errorCode; + } + ); + + auto future = job.exec(); + QVERIFY(future.isFinished()); + QCOMPARE(error, 1); + QCOMPARE(future.errorCode(), 1); + QCOMPARE(future.errorMessage(), QString::fromLatin1("error")); + } +} + +void AsyncTest::testErrorPropagation() { int error = 0; + bool called = false; auto job = Async::start( [](Async::Future &f) { f.setError(1, "error"); }) .then( - [](int v, Async::Future &f) { + [&called](int v, Async::Future &f) { + called = true; f.setFinished(); }, [&error](int errorCode, const QString &errorMessage) { - error = errorCode; + error += errorCode; } ); auto future = job.exec(); - future.waitForFinished(); + QVERIFY(future.isFinished()); + QCOMPARE(future.errorCode(), 1); + QCOMPARE(future.errorMessage(), QString::fromLatin1("error")); + QCOMPARE(called, false); QCOMPARE(error, 1); +} + +void AsyncTest::testErrorHandlerAsync() +{ + { + auto job = Async::start( + [](Async::Future &f) { + new AsyncSimulator(f, + [](Async::Future &f) { + f.setError(1, "error"); + } + ); + } + ); + + auto future = job.exec(); + future.waitForFinished(); + + QVERIFY(future.isFinished()); + QCOMPARE(future.errorCode(), 1); + QCOMPARE(future.errorMessage(), QString::fromLatin1("error")); + } + + { + int error = 0; + auto job = Async::start( + [](Async::Future &f) { + new AsyncSimulator(f, + [](Async::Future &f) { + f.setError(1, "error"); + } + ); + }, + [&error](int errorCode, const QString &errorMessage) { + error += errorCode; + } + ); + + auto future = job.exec(); + future.waitForFinished(); + + QVERIFY(future.isFinished()); + QCOMPARE(error, 1); + QCOMPARE(future.errorCode(), 1); + QCOMPARE(future.errorMessage(), QString::fromLatin1("error")); + } +} + +void AsyncTest::testErrorPropagationAsync() +{ + int error = 0; + bool called = false; + auto job = Async::start( + [](Async::Future &f) { + new AsyncSimulator(f, + [](Async::Future &f) { + f.setError(1, "error"); + } + ); + }) + .then( + [&called](int v, Async::Future &f) { + called = true; + f.setFinished(); + }, + [&error](int errorCode, const QString &errorMessage) { + error += errorCode; + } + ); + + auto future = job.exec(); + future.waitForFinished(); + QVERIFY(future.isFinished()); + QCOMPARE(future.errorCode(), 1); + QCOMPARE(future.errorMessage(), QString::fromLatin1("error")); + QCOMPARE(called, false); + QCOMPARE(error, 1); } + + void AsyncTest::testChainingRunningJob() { int check = 0; -- cgit v1.2.3