From 5e580299e342bd77fc7479bbfd235f4446d7f05b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Vr=C3=A1til?= Date: Mon, 18 May 2015 15:20:35 +0200 Subject: KAsync has moved to it's own kasync.git repository --- async/CMakeLists.txt | 73 ---- async/KF5AsyncConfig.cmake.in | 3 - async/autotests/CMakeLists.txt | 13 - async/autotests/asynctest.cpp | 859 ---------------------------------------- async/autotests/kjobtest.cpp | 69 ---- async/autotests/testkjob.cpp | 28 -- async/autotests/testkjob.h | 48 --- async/src/CMakeLists.txt | 63 --- async/src/async.cpp | 148 ------- async/src/async.h | 874 ----------------------------------------- async/src/async_impl.h | 81 ---- async/src/debug.cpp | 75 ---- async/src/debug.h | 82 ---- async/src/future.cpp | 158 -------- async/src/future.h | 257 ------------ 15 files changed, 2831 deletions(-) delete mode 100644 async/CMakeLists.txt delete mode 100644 async/KF5AsyncConfig.cmake.in delete mode 100644 async/autotests/CMakeLists.txt delete mode 100644 async/autotests/asynctest.cpp delete mode 100644 async/autotests/kjobtest.cpp delete mode 100644 async/autotests/testkjob.cpp delete mode 100644 async/autotests/testkjob.h delete mode 100644 async/src/CMakeLists.txt delete mode 100644 async/src/async.cpp delete mode 100644 async/src/async.h delete mode 100644 async/src/async_impl.h delete mode 100644 async/src/debug.cpp delete mode 100644 async/src/debug.h delete mode 100644 async/src/future.cpp delete mode 100644 async/src/future.h (limited to 'async') diff --git a/async/CMakeLists.txt b/async/CMakeLists.txt deleted file mode 100644 index 6a93a2c..0000000 --- a/async/CMakeLists.txt +++ /dev/null @@ -1,73 +0,0 @@ -project(KAsync) - -cmake_minimum_required(VERSION 2.8.12) - -# ECM setup -find_package(ECM 5.10.0 CONFIG REQUIRED) -set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) - -include(GenerateExportHeader) -include(ECMGenerateHeaders) -include(ECMGeneratePriFile) -include(ECMPackageConfigHelpers) -include(ECMSetupVersion) -include(FeatureSummary) -include(KDEInstallDirs) -include(KDECMakeSettings) -include(KDEFrameworkCompilerSettings) - -set(KASYNC_VERSION "4.99.0") - -set(KF5_VERSION "5.10.0") -set(QT_REQUIRED_VERSION "5.2.0") - -ecm_setup_version(${KASYNC_VERSION} - VARIABLE_PREFIX KASYNC - VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kasync_version.h" - PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF5AsyncConfigVersion.cmake" - SOVERSION 5 -) - - -########### Find packages ########### -find_package(Qt5 ${QT_REQUIRED_VERSION} REQUIRED COMPONENTS Core Test) - -option(WITH_KJOB "Enable native support for KJob in libasync API (enabled by default)" ON) -if (WITH_KJOB) - find_package(KF5CoreAddons REQUIRED ${KF5_VERSION}) - add_definitions(-DWITH_KJOB) -endif() - -########### Targets ########### -add_subdirectory(src) -add_subdirectory(autotests) - - -########### CMake Config Files ########### -set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KF5Async") - -ecm_configure_package_config_file( - "${CMAKE_CURRENT_SOURCE_DIR}/KF5AsyncConfig.cmake.in" - "${CMAKE_CURRENT_BINARY_DIR}/KF5AsyncConfig.cmake" - INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} -) - -install(FILES - "${CMAKE_CURRENT_BINARY_DIR}/KF5AsyncConfig.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/KF5AsyncConfigVersion.cmake" - DESTINATION "${CMAKECONFIG_INSTALL_DIR}" - COMPONENT Devel -) - -install(EXPORT - KF5AsyncTargets - DESTINATION "${CMAKECONFIG_INSTALL_DIR}" - FILE KF5AsyncTargets.cmake - NAMESPACE KF5::) - -install(FILES - ${CMAKE_CURRENT_BINARY_DIR}/kasync_version.h - DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5} COMPONENT Devel -) - -feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/async/KF5AsyncConfig.cmake.in b/async/KF5AsyncConfig.cmake.in deleted file mode 100644 index d11b836..0000000 --- a/async/KF5AsyncConfig.cmake.in +++ /dev/null @@ -1,3 +0,0 @@ -@PACKAGE_INIT@ - -include("${CMAKE_CURRENT_LIST_DIR}/KF5AsyncTargets.cmake") diff --git a/async/autotests/CMakeLists.txt b/async/autotests/CMakeLists.txt deleted file mode 100644 index b2209ab..0000000 --- a/async/autotests/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -include(ECMAddTests) - -ecm_add_test(asynctest.cpp - TEST_NAME asynctest - LINK_LIBRARIES KF5Async Qt5::Test -) - -if (WITH_KJOB) - ecm_add_test(kjobtest.cpp testkjob.cpp - TEST_NAME kjobtest - LINK_LIBRARIES KF5Async Qt5::Test KF5::CoreAddons - ) -endif() diff --git a/async/autotests/asynctest.cpp b/async/autotests/asynctest.cpp deleted file mode 100644 index ffc732c..0000000 --- a/async/autotests/asynctest.cpp +++ /dev/null @@ -1,859 +0,0 @@ -/* - * Copyright 2014 Daniel Vrátil - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License or (at your option) version 3 or any later version - * accepted by the membership of KDE e.V. (or its successor approved - * by the membership of KDE e.V.), which shall act as a proxy - * defined in Section 14 of version 3 of the license. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -#include "../src/async.h" - -#include -#include -#include -#include -#include - -#include - -class AsyncTest : public QObject -{ - Q_OBJECT - -public: - AsyncTest() - {} - - ~AsyncTest() - {} - -private Q_SLOTS: - void testSyncPromises(); - void testAsyncPromises(); - void testAsyncPromises2(); - void testNestedAsync(); - void testStartValue(); - - void testAsyncThen(); - void testSyncThen(); - void testJoinedThen(); - void testVoidThen(); - - void testAsyncEach(); - void testSyncEach(); - void testJoinedEach(); - void testVoidEachThen(); - void testAsyncVoidEachThen(); - - void testAsyncReduce(); - void testSyncReduce(); - void testJoinedReduce(); - void testVoidReduce(); - - void testProgressReporting(); - void testErrorHandler(); - void testErrorPropagation(); - void testErrorHandlerAsync(); - void testErrorPropagationAsync(); - void testNestedErrorPropagation(); - - void testChainingRunningJob(); - void testChainingFinishedJob(); - - void testLifetimeWithoutHandle(); - void testLifetimeWithHandle(); - - void benchmarkSyncThenExecutor(); - -private: - template - class AsyncSimulator { - public: - AsyncSimulator(KAsync::Future &future, const T &result) - : mFuture(future) - , mResult(result) - { - QObject::connect(&mTimer, &QTimer::timeout, - [this]() { - mFuture.setValue(mResult); - mFuture.setFinished(); - }); - QObject::connect(&mTimer, &QTimer::timeout, - [this]() { - delete this; - }); - mTimer.setSingleShot(true); - mTimer.start(200); - } - - AsyncSimulator(KAsync::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: - KAsync::Future mFuture; - std::function&)> mCallback; - T mResult; - QTimer mTimer; - }; -}; - - -template<> -class AsyncTest::AsyncSimulator { -public: - AsyncSimulator(KAsync::Future &future) - : mFuture(future) - { - QObject::connect(&mTimer, &QTimer::timeout, - [this]() { - mFuture.setFinished(); - }); - QObject::connect(&mTimer, &QTimer::timeout, - [this]() { - delete this; - }); - mTimer.setSingleShot(true); - mTimer.start(200); - } - -private: - KAsync::Future mFuture; - QTimer mTimer; -}; - - - -void AsyncTest::testSyncPromises() -{ - auto baseJob = KAsync::start( - [](KAsync::Future &f) { - f.setValue(42); - f.setFinished(); - }) - .then( - [](int v, KAsync::Future &f) { - f.setValue(QLatin1String("Result is ") + QString::number(v)); - f.setFinished(); - }); - - auto job = baseJob.then( - [](const QString &v, KAsync::Future &f) { - f.setValue(v.toUpper()); - f.setFinished(); - }); - - KAsync::Future future = job.exec(); - - QVERIFY(future.isFinished()); - QCOMPARE(future.value(), QString::fromLatin1("RESULT IS 42")); -} - -void AsyncTest::testAsyncPromises() -{ - auto job = KAsync::start( - [](KAsync::Future &future) { - new AsyncSimulator(future, 42); - }); - - KAsync::Future future = job.exec(); - - future.waitForFinished(); - QCOMPARE(future.value(), 42); -} - -void AsyncTest::testAsyncPromises2() -{ - bool done = false; - - auto job = KAsync::start( - [](KAsync::Future &future) { - new AsyncSimulator(future, 42); - } - ).then([&done](int result, KAsync::Future &future) { - done = true; - future.setValue(result); - future.setFinished(); - }); - auto future = job.exec(); - - QTRY_VERIFY(done); - QCOMPARE(future.value(), 42); -} - -void AsyncTest::testNestedAsync() -{ - bool done = false; - - auto job = KAsync::start( - [](KAsync::Future &future) { - auto innerJob = KAsync::start([](KAsync::Future &innerFuture) { - new AsyncSimulator(innerFuture, 42); - }).then([&future](KAsync::Future &innerThenFuture) { - future.setFinished(); - innerThenFuture.setFinished(); - }); - innerJob.exec().waitForFinished(); - } - ).then([&done](int result, KAsync::Future &future) { - done = true; - future.setValue(result); - future.setFinished(); - }); - job.exec(); - - QTRY_VERIFY(done); -} - -void AsyncTest::testStartValue() -{ - auto job = KAsync::start( - [](int in, KAsync::Future &future) { - future.setValue(in); - future.setFinished(); - }); - - auto future = job.exec(42); - QVERIFY(future.isFinished()); - QCOMPARE(future.value(), 42); -} - - - - - -void AsyncTest::testAsyncThen() -{ - auto job = KAsync::start( - [](KAsync::Future &future) { - new AsyncSimulator(future, 42); - }); - - auto future = job.exec(); - future.waitForFinished(); - - QVERIFY(future.isFinished()); - QCOMPARE(future.value(), 42); -} - - -void AsyncTest::testSyncThen() -{ - auto job = KAsync::start( - []() -> int { - return 42; - }) - .then( - [](int in) -> int { - return in * 2; - }); - - auto future = job.exec(); - QVERIFY(future.isFinished()); - QCOMPARE(future.value(), 84); -} - -void AsyncTest::testJoinedThen() -{ - auto job1 = KAsync::start( - [](int in, KAsync::Future &future) { - new AsyncSimulator(future, in * 2); - }); - - auto job2 = KAsync::start( - [](KAsync::Future &future) { - new AsyncSimulator(future, 42); - }) - .then(job1); - - auto future = job2.exec(); - future.waitForFinished(); - - QVERIFY(future.isFinished()); - QCOMPARE(future.value(), 84); -} - -void AsyncTest::testVoidThen() -{ - int check = 0; - - auto job = KAsync::start( - [&check](KAsync::Future &future) { - new AsyncSimulator(future); - ++check; - }) - .then( - [&check](KAsync::Future &future) { - new AsyncSimulator(future); - ++check; - }) - .then( - [&check]() { - ++check; - }); - - auto future = job.exec(); - future.waitForFinished(); - - QVERIFY(future.isFinished()); - QCOMPARE(check, 3); -} - - - -void AsyncTest::testAsyncEach() -{ - auto job = KAsync::start>( - [](KAsync::Future> &future) { - new AsyncSimulator>(future, { 1, 2, 3, 4 }); - }) - .each, int>( - [](const int &v, KAsync::Future> &future) { - new AsyncSimulator>(future, { v + 1 }); - }); - - auto future = job.exec(); - future.waitForFinished(); - - const QList expected({ 2, 3, 4, 5 }); - QVERIFY(future.isFinished()); - QCOMPARE(future.value(), expected); -} - -void AsyncTest::testSyncEach() -{ - auto job = KAsync::start>( - []() -> QList { - return { 1, 2, 3, 4 }; - }) - .each, int>( - [](const int &v) -> QList { - return { v + 1 }; - }); - - KAsync::Future> future = job.exec(); - - const QList expected({ 2, 3, 4, 5 }); - QVERIFY(future.isFinished()); - QCOMPARE(future.value(), expected); -} - -void AsyncTest::testJoinedEach() -{ - auto job1 = KAsync::start, int>( - [](int v, KAsync::Future> &future) { - new AsyncSimulator>(future, { v * 2 }); - }); - - auto job = KAsync::start>( - []() -> QList { - return { 1, 2, 3, 4 }; - }) - .each(job1); - - auto future = job.exec(); - future.waitForFinished(); - - const QList expected({ 2, 4, 6, 8 }); - QVERIFY(future.isFinished()); - QCOMPARE(future.value(), expected); -} - -void AsyncTest::testVoidEachThen() -{ - QList check; - auto job = KAsync::start>( - []() -> QList { - return { 1, 2, 3, 4 }; - }).each( - [&check](const int &v) { - check << v; - }).then([](){}); - - auto future = job.exec(); - - const QList expected({ 1, 2, 3, 4 }); - QVERIFY(future.isFinished()); - QCOMPARE(check, expected); -} - -void AsyncTest::testAsyncVoidEachThen() -{ - bool completedJob = false; - QList check; - auto job = KAsync::start>( - [](KAsync::Future > &future) { - new AsyncSimulator>(future, { 1, 2, 3, 4 }); - }).each( - [&check](const int &v, KAsync::Future &future) { - check << v; - new AsyncSimulator(future); - }).then([&completedJob](KAsync::Future &future) { - completedJob = true; - future.setFinished(); - }); - - auto future = job.exec(); - future.waitForFinished(); - - const QList expected({ 1, 2, 3, 4 }); - QVERIFY(future.isFinished()); - QVERIFY(completedJob); - QCOMPARE(check, expected); -} - - - - - -void AsyncTest::testAsyncReduce() -{ - auto job = KAsync::start>( - [](KAsync::Future> &future) { - new AsyncSimulator>(future, { 1, 2, 3, 4 }); - }) - .reduce>( - [](const QList &list, KAsync::Future &future) { - new AsyncSimulator(future, - [list](KAsync::Future &future) { - int sum = 0; - for (int i : list) sum += i; - future.setValue(sum); - future.setFinished(); - } - ); - }); - - KAsync::Future future = job.exec(); - future.waitForFinished(); - - QVERIFY(future.isFinished()); - QCOMPARE(future.value(), 10); -} - -void AsyncTest::testSyncReduce() -{ - auto job = KAsync::start>( - []() -> QList { - return { 1, 2, 3, 4 }; - }) - .reduce>( - [](const QList &list) -> int { - int sum = 0; - for (int i : list) sum += i; - return sum; - }); - - KAsync::Future future = job.exec(); - - QVERIFY(future.isFinished()); - QCOMPARE(future.value(), 10); -} - - -void AsyncTest::testJoinedReduce() -{ - auto job1 = KAsync::start>( - [](const QList &list, KAsync::Future &future) { - int sum = 0; - for (int i : list) sum += i; - new AsyncSimulator(future, sum); - }); - - auto job = KAsync::start>( - []() -> QList { - return { 1, 2, 3, 4 }; - }) - .reduce(job1); - - auto future = job.exec(); - future.waitForFinished(); - - QVERIFY(future.isFinished()); - QCOMPARE(future.value(), 10); -} - -void AsyncTest::testVoidReduce() -{ -// This must not compile (reduce with void result makes no sense) -#ifdef TEST_BUILD_FAIL - auto job = KAsync::start>( - []() -> QList { - return { 1, 2, 3, 4 }; - }) - .reduce>( - [](const QList &list) -> int { - return; - }); - - auto future = job.exec(); - QVERIFY(future.isFinished()); -#endif -} - - -void AsyncTest::testProgressReporting() -{ - static int progress; - progress = 0; - - auto job = KAsync::start( - [](KAsync::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; - KAsync::FutureWatcher watcher; - connect(&watcher, &KAsync::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() -{ - - { - auto job = KAsync::start( - [](KAsync::Future &f) { - f.setError(1, QLatin1String("error")); - }); - - auto future = job.exec(); - QVERIFY(future.isFinished()); - QCOMPARE(future.errorCode(), 1); - QCOMPARE(future.errorMessage(), QString::fromLatin1("error")); - } - - { - int error = 0; - auto job = KAsync::start( - [](KAsync::Future &f) { - f.setError(1, QLatin1String("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 = KAsync::start( - [](KAsync::Future &f) { - f.setError(1, QLatin1String("error")); - }) - .then( - [&called](int v, KAsync::Future &f) { - called = true; - f.setFinished(); - }, - [&error](int errorCode, const QString &errorMessage) { - error += errorCode; - } - ); - auto future = job.exec(); - QVERIFY(future.isFinished()); - QCOMPARE(future.errorCode(), 1); - QCOMPARE(future.errorMessage(), QString::fromLatin1("error")); - QCOMPARE(called, false); - QCOMPARE(error, 1); -} - -void AsyncTest::testErrorHandlerAsync() -{ - { - auto job = KAsync::start( - [](KAsync::Future &f) { - new AsyncSimulator(f, - [](KAsync::Future &f) { - f.setError(1, QLatin1String("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 = KAsync::start( - [](KAsync::Future &f) { - new AsyncSimulator(f, - [](KAsync::Future &f) { - f.setError(1, QLatin1String("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 = KAsync::start( - [](KAsync::Future &f) { - new AsyncSimulator(f, - [](KAsync::Future &f) { - f.setError(1, QLatin1String("error")); - } - ); - }) - .then( - [&called](int v, KAsync::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::testNestedErrorPropagation() -{ - int error = 0; - auto job = KAsync::start([](){}) - .then(KAsync::error(1, QLatin1String("error"))) //Nested job that throws error - .then([](KAsync::Future &future) { - //We should never get here - Q_ASSERT(false); - }, - [&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(error, 1); -} - - - - -void AsyncTest::testChainingRunningJob() -{ - int check = 0; - - auto job = KAsync::start( - [&check](KAsync::Future &future) { - QTimer *timer = new QTimer(); - QObject::connect(timer, &QTimer::timeout, - [&future, &check]() { - ++check; - future.setValue(42); - future.setFinished(); - }); - QObject::connect(timer, &QTimer::timeout, - timer, &QObject::deleteLater); - timer->setSingleShot(true); - timer->start(500); - }); - - auto future1 = job.exec(); - QTest::qWait(200); - - auto job2 = job.then( - [&check](int in) -> int { - ++check; - return in * 2; - }); - - auto future2 = job2.exec(); - QVERIFY(!future1.isFinished()); - future2.waitForFinished(); - - QEXPECT_FAIL("", "Chaining new job to a running job no longer executes the new job. " - "This is a trade-off for being able to re-execute single job multiple times.", - Abort); - - QCOMPARE(check, 2); - - QVERIFY(future1.isFinished()); - QVERIFY(future2.isFinished()); - QCOMPARE(future1.value(), 42); - QCOMPARE(future2.value(), 84); -} - -void AsyncTest::testChainingFinishedJob() -{ - int check = 0; - - auto job = KAsync::start( - [&check]() -> int { - ++check; - return 42; - }); - - auto future1 = job.exec(); - QVERIFY(future1.isFinished()); - - auto job2 = job.then( - [&check](int in) -> int { - ++check; - return in * 2; - }); - - auto future2 = job2.exec(); - QVERIFY(future2.isFinished()); - - QEXPECT_FAIL("", "Resuming finished job by chaining a new job and calling exec() is no longer suppported. " - "This is a trade-off for being able to re-execute single job multiple times.", - Abort); - - QCOMPARE(check, 2); - - QCOMPARE(future1.value(), 42); - QCOMPARE(future2.value(), 84); -} - -/* - * We want to be able to execute jobs without keeping a handle explicitly alive. - * If the future handle inside the continuation would keep the executor alive, that would probably already work. - */ -void AsyncTest::testLifetimeWithoutHandle() -{ - bool done = false; - { - auto job = KAsync::start([&done](KAsync::Future &future) { - QTimer *timer = new QTimer(); - QObject::connect(timer, &QTimer::timeout, - [&future, &done]() { - done = true; - future.setFinished(); - }); - QObject::connect(timer, &QTimer::timeout, - timer, &QObject::deleteLater); - timer->setSingleShot(true); - timer->start(500); - }); - job.exec(); - } - - QTRY_VERIFY(done); -} - -/* - * The future handle should keep the executor alive, and the future reference should probably not become invalid inside the continuation, - * until the job is done (alternatively a copy of the future inside the continuation should work as well). - */ -void AsyncTest::testLifetimeWithHandle() -{ - KAsync::Future future; - { - auto job = KAsync::start([](KAsync::Future &future) { - QTimer *timer = new QTimer(); - QObject::connect(timer, &QTimer::timeout, - [&future]() { - future.setFinished(); - }); - QObject::connect(timer, &QTimer::timeout, - timer, &QObject::deleteLater); - timer->setSingleShot(true); - timer->start(500); - }); - future = job.exec(); - } - - QTRY_VERIFY(future.isFinished()); -} - -void AsyncTest::benchmarkSyncThenExecutor() -{ - auto job = KAsync::start( - []() -> int { - return 0; - }); - - QBENCHMARK { - job.exec(); - } -} - -QTEST_MAIN(AsyncTest); - -#include "asynctest.moc" diff --git a/async/autotests/kjobtest.cpp b/async/autotests/kjobtest.cpp deleted file mode 100644 index 15e50de..0000000 --- a/async/autotests/kjobtest.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2015 Daniel Vrátil - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License or (at your option) version 3 or any later version - * accepted by the membership of KDE e.V. (or its successor approved - * by the membership of KDE e.V.), which shall act as a proxy - * defined in Section 14 of version 3 of the license. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -#include "../src/async.h" -#include "testkjob.h" - -#include - -class KJobTest : public QObject -{ - Q_OBJECT - -public: - KJobTest() - {} - - ~KJobTest() - {} - -private Q_SLOTS: - void testSingleKJob(); - void testKJobChain(); - -}; - -void KJobTest::testSingleKJob() -{ - auto job = KAsync::start(); - - auto future = job.exec(42); - future.waitForFinished(); - - QVERIFY(future.isFinished()); - QCOMPARE(future.value(), 42); -} - -void KJobTest::testKJobChain() -{ - auto job = KAsync::start() - .then(); - - auto future = job.exec(42); - future.waitForFinished(); - - QVERIFY(future.isFinished()); - QCOMPARE(future.value(), 42); -} - -QTEST_MAIN(KJobTest) - -#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 @@ -#include "testkjob.h" - -TestKJob::TestKJob(int result) - : mResult(result) -{ - connect(&mTimer, &QTimer::timeout, - this, &TestKJob::onTimeout); - mTimer.setSingleShot(true); - mTimer.setInterval(200); -} - -TestKJob::~TestKJob() -{} - -void TestKJob::start() -{ - mTimer.start(); -} - -int TestKJob::result() -{ - return mResult; -} - -void TestKJob::onTimeout() -{ - emitResult(); -} \ 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 @@ -/* - * Copyright 2015 Daniel Vrátil - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License or (at your option) version 3 or any later version - * accepted by the membership of KDE e.V. (or its successor approved - * by the membership of KDE e.V.), which shall act as a proxy - * defined in Section 14 of version 3 of the license. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -#ifndef TESTKJOB_H -#define TESTKJOB_H - -#include -#include - -class TestKJob : public KJob -{ - Q_OBJECT - -public: - TestKJob(int result); - ~TestKJob(); - - void start(); - - int result(); - -private Q_SLOTS: - void onTimeout(); - -private: - int mResult; - QTimer mTimer; -}; - -#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 05d08b6..0000000 --- a/async/src/CMakeLists.txt +++ /dev/null @@ -1,63 +0,0 @@ -set(kasync_SRCS - async.cpp - future.cpp - debug.cpp -) - -set(kasync_priv_HEADERS - async_impl.h - debug.h -) - -ecm_generate_headers(kasync_HEADERS - HEADER_NAMES - Async - Future -) - - -add_library(KF5Async ${kasync_SRCS}) -add_library(KF5::Async ALIAS KF5Async) - -generate_export_header(KF5Async BASE_NAME kasync) - -target_include_directories(KF5Async INTERFACE "$") -target_include_directories(KF5Async PUBLIC "$") - -target_link_libraries(KF5Async - PUBLIC - Qt5::Core -) -if (WITH_KJOB) - target_link_libraries(KF5Async PUBLIC KF5::CoreAddons) -endif () - - -set_target_properties(KF5Async PROPERTIES - VERSION ${KASYNC_VERSION_STRING} - SOVERSION ${KASYNC_SOVERSION} - EXPORT_NAME KAsync -) - -ecm_generate_pri_file(BASE_NAME KAsync - LIB_NAME KF5Async - FILENAME_VAR PRI_FILENAME -) - -install(TARGETS - KF5Async - EXPORT KF5AsyncTargets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS} -) - -install(FILES - ${CMAKE_CURRENT_BINARY_DIR}/kasync_export.h - ${kasync_HEADERS} - ${kasync_priv_HEADERS} - DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/Async - COMPONENT Devel -) - -install(FILES - ${PRI_FILENAME} - DESTINATION ${ECM_MKSPECS_INSTALL_DIR} -) diff --git a/async/src/async.cpp b/async/src/async.cpp deleted file mode 100644 index c57c9ad..0000000 --- a/async/src/async.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2014 Daniel Vrátil - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library. If not, see . - */ - -#include "async.h" - -#include -#include -#include -#include - -using namespace KAsync; - -Private::Execution::Execution(const Private::ExecutorBasePtr &executor) - : executor(executor) - , resultBase(nullptr) - , isRunning(false) - , isFinished(false) -{ -} - -Private::Execution::~Execution() -{ - if (resultBase) { - resultBase->releaseExecution(); - delete resultBase; - } - prevExecution.reset(); -} - -void Private::Execution::setFinished() -{ - isFinished = true; - //executor.clear(); -#ifndef QT_NO_DEBUG - if (tracer) { - delete tracer; - } -#endif -} - -void Private::Execution::releaseFuture() -{ - resultBase = 0; -} - -bool Private::Execution::errorWasHandled() const -{ - Execution *exec = const_cast(this); - while (exec) { - if (exec->executor->hasErrorFunc()) { - return true; - } - exec = exec->prevExecution.data(); - } - return false; -} - - - - - -Private::ExecutorBase::ExecutorBase(const ExecutorBasePtr &parent) - : mPrev(parent) -{ -} - -Private::ExecutorBase::~ExecutorBase() -{ -} - - - - -JobBase::JobBase(const Private::ExecutorBasePtr &executor) - : mExecutor(executor) -{ -} - -JobBase::~JobBase() -{ -} - -static void asyncWhile(const std::function)> &body, const std::function &completionHandler) { - body([body, completionHandler](bool complete) { - if (complete) { - completionHandler(); - } else { - asyncWhile(body, completionHandler); - } - }); -} - -Job KAsync::dowhile(Condition condition, ThenTask body) -{ - return KAsync::start([body, condition](KAsync::Future &future) { - asyncWhile([condition, body](std::function whileCallback) { - KAsync::start(body).then([whileCallback, condition]() { - whileCallback(!condition()); - }).exec(); - }, - [&future]() { //while complete - future.setFinished(); - }); - }); -} - -Job KAsync::dowhile(ThenTask body) -{ - return KAsync::start([body](KAsync::Future &future) { - asyncWhile([body](std::function whileCallback) { - KAsync::start(body).then([whileCallback](bool result) { - whileCallback(!result); - //FIXME this return value is only required because .then doesn't work - return true; - }).exec(); - }, - [&future]() { //while complete - future.setFinished(); - }); - }); -} - -Job KAsync::wait(int delay) -{ - auto timer = QSharedPointer::create(); - return KAsync::start([timer, delay](KAsync::Future &future) { - timer->setSingleShot(true); - QObject::connect(timer.data(), &QTimer::timeout, [&future]() { - future.setFinished(); - }); - timer->start(delay); - }); -} - diff --git a/async/src/async.h b/async/src/async.h deleted file mode 100644 index 152f98e..0000000 --- a/async/src/async.h +++ /dev/null @@ -1,874 +0,0 @@ -/* - * Copyright 2014 - 2015 Daniel Vrátil - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library. If not, see . - */ - -#ifndef KASYNC_H -#define KASYNC_H - -#include "kasync_export.h" - -#include -#include -#include -#include -#include - -#include "future.h" -#include "debug.h" -#include "async_impl.h" - -#include -#include -#include - -#include - -#ifdef WITH_KJOB -#include -#endif - - -/* - * API to help write async code. - * - * This API is based around jobs that take lambdas to execute asynchronous tasks. Each async operation can take a continuation, - * that can then be used to execute further async operations. That way it is possible to build async chains of operations, - * that can be stored and executed later on. Jobs can be composed, similarly to functions. - * - * Relations between the components: - * * Job: API wrapper around Executors chain. Can be destroyed while still running, - * because the actual execution happens in the background - * * Executor: Describes task to execute. Executors form a linked list matching the - * order in which they will be executed. The Executor chain is destroyed when - * the parent Job is destroyed. However if the Job is still running it is - * guaranteed that the Executor chain will not be destroyed until the execution - * is finished. - * * Execution: The running execution of the task stored in Executor. Each call to Job::exec() - * instantiates new Execution chain, which makes it possible for the Job to be - * executed multiple times (even in parallel). - * * Future: Representation of the result that is being calculated - * - * - * TODO: Composed progress reporting - * TODO: Possibility to abort a job through future (perhaps optional?) - * TODO: Support for timeout, specified during exec call, after which the error handler gets called with a defined errorCode. - */ -namespace KAsync { - -template -class Executor; - -class JobBase; - -template -class Job; - -template -using ThenTask = typename detail::identity&)>>::type; -template -using SyncThenTask = typename detail::identity>::type; -template -using EachTask = typename detail::identity&)>>::type; -template -using SyncEachTask = typename detail::identity>::type; -template -using ReduceTask = typename detail::identity&)>>::type; -template -using SyncReduceTask = typename detail::identity>::type; - -using ErrorHandler = std::function; -using Condition = std::function; - -namespace Private -{ - -class ExecutorBase; -typedef QSharedPointer ExecutorBasePtr; - -struct KASYNC_EXPORT Execution { - Execution(const ExecutorBasePtr &executor); - ~Execution(); - void setFinished(); - - template - KAsync::Future* result() const - { - return static_cast*>(resultBase); - } - - void releaseFuture(); - bool errorWasHandled() const; - - ExecutorBasePtr executor; - FutureBase *resultBase; - bool isRunning; - bool isFinished; - - ExecutionPtr prevExecution; - -#ifndef QT_NO_DEBUG - Tracer *tracer; -#endif -}; - - -typedef QSharedPointer ExecutionPtr; - -class KASYNC_EXPORT ExecutorBase -{ - template - friend class Executor; - - template - friend class KAsync::Job; - - friend class Execution; - friend class KAsync::Tracer; - -public: - virtual ~ExecutorBase(); - virtual ExecutionPtr exec(const ExecutorBasePtr &self) = 0; - -protected: - ExecutorBase(const ExecutorBasePtr &parent); - - template - KAsync::Future* createFuture(const ExecutionPtr &execution) const; - - virtual bool hasErrorFunc() const = 0; - virtual bool handleError(const ExecutionPtr &execution) = 0; - - ExecutorBasePtr mPrev; - -#ifndef QT_NO_DEBUG - QString mExecutorName; -#endif -}; - -template -class Executor : public ExecutorBase -{ -protected: - Executor(ErrorHandler errorFunc, const Private::ExecutorBasePtr &parent) - : ExecutorBase(parent) - , mErrorFunc(errorFunc) - {} - - virtual ~Executor() {} - virtual void run(const ExecutionPtr &execution) = 0; - - ExecutionPtr exec(const ExecutorBasePtr &self); - bool hasErrorFunc() const { return (bool) mErrorFunc; } - bool handleError(const ExecutionPtr &execution); - - std::function mErrorFunc; -}; - -template -class ThenExecutor: public Executor::type, Out, In ...> -{ -public: - ThenExecutor(ThenTask then, ErrorHandler errorFunc, const ExecutorBasePtr &parent); - void run(const ExecutionPtr &execution); -private: - ThenTask mFunc; -}; - -template -class EachExecutor : public Executor -{ -public: - EachExecutor(EachTask each, ErrorHandler errorFunc, const ExecutorBasePtr &parent); - void run(const ExecutionPtr &execution); -private: - EachTask mFunc; - QVector*> mFutureWatchers; -}; - -template -class ReduceExecutor : public ThenExecutor -{ -public: - ReduceExecutor(ReduceTask reduce, ErrorHandler errorFunc, const ExecutorBasePtr &parent); -private: - ReduceTask mFunc; -}; - -template -class SyncThenExecutor : public Executor::type, Out, In ...> -{ -public: - SyncThenExecutor(SyncThenTask then, ErrorHandler errorFunc, const ExecutorBasePtr &parent); - void run(const ExecutionPtr &execution); - -private: - void run(const ExecutionPtr &execution, std::false_type); // !std::is_void - void run(const ExecutionPtr &execution, std::true_type); // std::is_void - SyncThenTask mFunc; -}; - -template -class SyncReduceExecutor : public SyncThenExecutor -{ -public: - SyncReduceExecutor(SyncReduceTask reduce, ErrorHandler errorFunc, const ExecutorBasePtr &parent); -private: - SyncReduceTask mFunc; -}; - -template -class SyncEachExecutor : public Executor -{ -public: - SyncEachExecutor(SyncEachTask each, ErrorHandler errorFunc, const ExecutorBasePtr &parent); - void run(const ExecutionPtr &execution); -private: - void run(KAsync::Future *future, const typename PrevOut::value_type &arg, std::false_type); // !std::is_void - void run(KAsync::Future *future, const typename PrevOut::value_type &arg, std::true_type); // std::is_void - SyncEachTask mFunc; -}; - -} // namespace Private - -/** - * Start an asynchronous job sequence. - * - * KAsync::start() is your starting point to build a chain of jobs to be executed - * asynchronously. - * - * @param func An asynchronous function to be executed. The function must have - * void return type, and accept exactly one argument of type @p KAsync::Future, - * where @p In is type of the result. - */ -template -Job start(ThenTask func, ErrorHandler errorFunc = ErrorHandler()); - -template -Job start(SyncThenTask func, ErrorHandler errorFunc = ErrorHandler()); - -#ifdef WITH_KJOB -template -Job start(); -#endif - -/** - * Async while loop. - * - * The loop continues while @param condition returns true. - */ -KASYNC_EXPORT Job dowhile(Condition condition, ThenTask func); - -/** - * Async while loop. - * - * Loop continues while body returns true. - */ -KASYNC_EXPORT Job dowhile(ThenTask body); - -/** - * Iterate over a container. - * - * Use in conjunction with .each - */ -template -Job iterate(const Out &container); - -/** - * Async delay. - */ -KASYNC_EXPORT Job wait(int delay); - -/** - * A null job. - * - * An async noop. - * - */ -template -Job null(); - -/** - * An error job. - * - * An async error. - * - */ -template -Job error(int errorCode = 1, const QString &errorMessage = QString()); - -class KASYNC_EXPORT JobBase -{ - template - friend class Job; - -public: - JobBase(const Private::ExecutorBasePtr &executor); - ~JobBase(); - -protected: - Private::ExecutorBasePtr mExecutor; -}; - -/** - * An Asynchronous job - * - * A single instance of Job represents a single method that will be executed - * asynchrously. The Job is started by @p Job::exec(), which returns @p KAsync::Future - * immediatelly. The Future will be set to finished state once the asynchronous - * task has finished. You can use @p KAsync::Future::waitForFinished() to wait for - * for the Future in blocking manner. - * - * It is possible to chain multiple Jobs one after another in different fashion - * (sequential, parallel, etc.). Calling Job::exec() will then return a pending - * @p KAsync::Future, and will execute the entire chain of jobs. - * - * @code - * auto job = Job::start>( - * [](KAsync::Future> &future) { - * MyREST::PendingUsers *pu = MyREST::requestListOfUsers(); - * QObject::connect(pu, &PendingOperation::finished, - * [&](PendingOperation *pu) { - * future->setValue(dynamic_cast(pu)->userIds()); - * future->setFinished(); - * }); - * }) - * .each, int>( - * [](const int &userId, KAsync::Future> &future) { - * MyREST::PendingUser *pu = MyREST::requestUserDetails(userId); - * QObject::connect(pu, &PendingOperation::finished, - * [&](PendingOperation *pu) { - * future->setValue(Qlist() << dynamic_cast(pu)->user()); - * future->setFinished(); - * }); - * }); - * - * KAsync::Future> usersFuture = job.exec(); - * usersFuture.waitForFinished(); - * QList users = usersFuture.value(); - * @endcode - * - * In the example above, calling @p job.exec() will first invoke the first job, - * which will retrieve a list of IDs, and then will invoke the second function - * for each single entry in the list returned by the first function. - */ -template -class Job : public JobBase -{ - template - friend class Job; - - template - friend Job start(KAsync::ThenTask func, ErrorHandler errorFunc); - - template - friend Job start(KAsync::SyncThenTask func, ErrorHandler errorFunc); - -#ifdef WITH_KJOB - template - friend Job start(); -#endif - -public: - template - Job then(ThenTask func, ErrorHandler errorFunc = ErrorHandler()) - { - return Job(Private::ExecutorBasePtr( - new Private::ThenExecutor(func, errorFunc, mExecutor))); - } - - template - Job then(SyncThenTask func, ErrorHandler errorFunc = ErrorHandler()) - { - return Job(Private::ExecutorBasePtr( - new Private::SyncThenExecutor(func, errorFunc, mExecutor))); - } - - template - Job then(Job otherJob, ErrorHandler errorFunc = ErrorHandler()) - { - return then(nestedJobWrapper(otherJob), errorFunc); - } - -#ifdef WITH_KJOB - template - Job then() - { - return start(); - } -#endif - - template - Job each(EachTask func, ErrorHandler errorFunc = ErrorHandler()) - { - eachInvariants(); - return Job(Private::ExecutorBasePtr( - new Private::EachExecutor(func, errorFunc, mExecutor))); - } - - template - Job each(SyncEachTask func, ErrorHandler errorFunc = ErrorHandler()) - { - eachInvariants(); - return Job(Private::ExecutorBasePtr( - new Private::SyncEachExecutor(func, errorFunc, mExecutor))); - } - - template - Job each(Job otherJob, ErrorHandler errorFunc = ErrorHandler()) - { - eachInvariants(); - return each(nestedJobWrapper(otherJob), errorFunc); - } - - template - Job reduce(ReduceTask func, ErrorHandler errorFunc = ErrorHandler()) - { - reduceInvariants(); - return Job(Private::ExecutorBasePtr( - new Private::ReduceExecutor(func, errorFunc, mExecutor))); - } - - template - Job reduce(SyncReduceTask func, ErrorHandler errorFunc = ErrorHandler()) - { - reduceInvariants(); - return Job(Private::ExecutorBasePtr( - new Private::SyncReduceExecutor(func, errorFunc, mExecutor))); - } - - template - Job reduce(Job otherJob, ErrorHandler errorFunc = ErrorHandler()) - { - return reduce(nestedJobWrapper(otherJob), errorFunc); - } - - template - KAsync::Future exec(FirstIn in) - { - // Inject a fake sync executor that will return the initial value - Private::ExecutorBasePtr first = mExecutor; - while (first->mPrev) { - first = first->mPrev; - } - auto init = new Private::SyncThenExecutor( - [in]() -> FirstIn { - return in; - }, - ErrorHandler(), Private::ExecutorBasePtr()); - first->mPrev = Private::ExecutorBasePtr(init); - - auto result = exec(); - // Remove the injected executor - first->mPrev.reset(); - return result; - } - - KAsync::Future exec() - { - Private::ExecutionPtr execution = mExecutor->exec(mExecutor); - KAsync::Future result = *execution->result(); - - return result; - } - -private: - Job(Private::ExecutorBasePtr executor) - : JobBase(executor) - {} - - template - void eachInvariants() - { - static_assert(detail::isIterable::value, - "The 'Each' task can only be connected to a job that returns a list or an array."); - static_assert(std::is_void::value || detail::isIterable::value, - "The result type of 'Each' task must be void, a list or an array."); - } - - template - void reduceInvariants() - { - static_assert(KAsync::detail::isIterable::value, - "The 'Result' task can only be connected to a job that returns a list or an array"); - static_assert(std::is_same::value, - "The return type of previous task must be compatible with input type of this task"); - } - - template - inline std::function&)> nestedJobWrapper(Job otherJob) { - return [otherJob](InOther ... in, KAsync::Future &future) { - // copy by value is const - auto job = otherJob; - FutureWatcher *watcher = new FutureWatcher(); - QObject::connect(watcher, &FutureWatcherBase::futureReady, - [watcher, future]() { - // FIXME: We pass future by value, because using reference causes the - // future to get deleted before this lambda is invoked, leading to crash - // in copyFutureValue() - // copy by value is const - auto outFuture = future; - KAsync::detail::copyFutureValue(watcher->future(), outFuture); - if (watcher->future().errorCode()) { - outFuture.setError(watcher->future().errorCode(), watcher->future().errorMessage()); - } else { - outFuture.setFinished(); - } - delete watcher; - }); - watcher->setFuture(job.exec(in ...)); - }; - } -}; - -} // namespace KAsync - - -// ********** Out of line definitions **************** - -namespace KAsync { - -template -Job start(ThenTask func, ErrorHandler error) -{ - return Job(Private::ExecutorBasePtr( - new Private::ThenExecutor(func, error, Private::ExecutorBasePtr()))); -} - -template -Job start(SyncThenTask func, ErrorHandler error) -{ - return Job(Private::ExecutorBasePtr( - new Private::SyncThenExecutor(func, error, Private::ExecutorBasePtr()))); -} - -#ifdef WITH_KJOB -template -Job start() -{ - return Job(Private::ExecutorBasePtr( - new Private::ThenExecutor([](const Args & ... args, KAsync::Future &future) - { - KJobType *job = new KJobType(args ...); - job->connect(job, &KJob::finished, - [&future](KJob *job) { - if (job->error()) { - future.setError(job->error(), job->errorString()); - } else { - future.setValue((static_cast(job)->*KJobResultMethod)()); - future.setFinished(); - } - }); - job->start(); - }, ErrorHandler(), Private::ExecutorBasePtr()))); -} -#endif - - -template -Job null() -{ - return KAsync::start( - [](KAsync::Future &future) { - future.setFinished(); - }); -} - -template -Job error(int errorCode, const QString &errorMessage) -{ - return KAsync::start( - [errorCode, errorMessage](KAsync::Future &future) { - future.setError(errorCode, errorMessage); - }); -} - -template -Job iterate(const Out &container) -{ - return KAsync::start( - [container]() { - return container; - }); -} - - -namespace Private { - -template -KAsync::Future* ExecutorBase::createFuture(const ExecutionPtr &execution) const -{ - return new KAsync::Future(execution); -} - -template -ExecutionPtr Executor::exec(const ExecutorBasePtr &self) -{ - // Passing 'self' to execution ensures that the Executor chain remains - // valid until the entire execution is finished - ExecutionPtr execution = ExecutionPtr::create(self); -#ifndef QT_NO_DEBUG - execution->tracer = new Tracer(execution.data()); // owned by execution -#endif - - // chainup - execution->prevExecution = mPrev ? mPrev->exec(mPrev) : ExecutionPtr(); - - execution->resultBase = ExecutorBase::createFuture(execution); - auto fw = new KAsync::FutureWatcher(); - QObject::connect(fw, &KAsync::FutureWatcher::futureReady, - [fw, execution, this]() { - handleError(execution); - execution->setFinished(); - delete fw; - }); - fw->setFuture(*execution->result()); - - KAsync::Future *prevFuture = execution->prevExecution ? execution->prevExecution->result() : nullptr; - if (!prevFuture || prevFuture->isFinished()) { - if (prevFuture) { // prevFuture implies execution->prevExecution - if (prevFuture->errorCode()) { - // Propagate the errorCode and message to the outer Future - execution->resultBase->setError(prevFuture->errorCode(), prevFuture->errorMessage()); - if (!execution->errorWasHandled()) { - if (handleError(execution)) { - return execution; - } - } else { - return execution; - } - } else { - // Propagate error (if any) - } - } - - execution->isRunning = true; - run(execution); - } else { - auto prevFutureWatcher = new KAsync::FutureWatcher(); - QObject::connect(prevFutureWatcher, &KAsync::FutureWatcher::futureReady, - [prevFutureWatcher, execution, this]() { - auto prevFuture = prevFutureWatcher->future(); - assert(prevFuture.isFinished()); - delete prevFutureWatcher; - auto prevExecutor = execution->executor->mPrev; - if (prevFuture.errorCode()) { - execution->resultBase->setError(prevFuture.errorCode(), prevFuture.errorMessage()); - if (!execution->errorWasHandled()) { - if (handleError(execution)) { - return; - } - } else { - return; - } - } - - - // propagate error (if any) - execution->isRunning = true; - run(execution); - }); - - prevFutureWatcher->setFuture(*static_cast*>(prevFuture)); - } - - return execution; -} - -template -bool Executor::handleError(const ExecutionPtr &execution) -{ - assert(execution->resultBase->isFinished()); - if (execution->resultBase->errorCode()) { - if (mErrorFunc) { - mErrorFunc(execution->resultBase->errorCode(), - execution->resultBase->errorMessage()); - return true; - } - } - - return false; -} - - -template -ThenExecutor::ThenExecutor(ThenTask then, ErrorHandler error, const ExecutorBasePtr &parent) - : Executor::type, Out, In ...>(error, parent) - , mFunc(then) -{ - STORE_EXECUTOR_NAME("ThenExecutor", Out, In ...); -} - -template -void ThenExecutor::run(const ExecutionPtr &execution) -{ - KAsync::Future::type> *prevFuture = nullptr; - if (execution->prevExecution) { - prevFuture = execution->prevExecution->result::type>(); - assert(prevFuture->isFinished()); - } - - ThenExecutor::mFunc(prevFuture ? prevFuture->value() : In() ..., *execution->result()); -} - -template -EachExecutor::EachExecutor(EachTask each, ErrorHandler error, const ExecutorBasePtr &parent) - : Executor(error, parent) - , mFunc(each) -{ - STORE_EXECUTOR_NAME("EachExecutor", PrevOut, Out, In); -} - -template -void EachExecutor::run(const ExecutionPtr &execution) -{ - assert(execution->prevExecution); - auto prevFuture = execution->prevExecution->result(); - assert(prevFuture->isFinished()); - - auto out = execution->result(); - if (prevFuture->value().isEmpty()) { - out->setFinished(); - return; - } - - for (auto arg : prevFuture->value()) { - //We have to manually manage the lifetime of these temporary futures - KAsync::Future *future = new KAsync::Future(); - EachExecutor::mFunc(arg, *future); - auto fw = new KAsync::FutureWatcher(); - mFutureWatchers.append(fw); - QObject::connect(fw, &KAsync::FutureWatcher::futureReady, - [out, fw, this, future]() { - assert(fw->future().isFinished()); - const int index = mFutureWatchers.indexOf(fw); - assert(index > -1); - mFutureWatchers.removeAt(index); - KAsync::detail::aggregateFutureValue(fw->future(), *out); - if (mFutureWatchers.isEmpty()) { - out->setFinished(); - } - delete fw; - delete future; - }); - fw->setFuture(*future); - } -} - -template -ReduceExecutor::ReduceExecutor(ReduceTask reduce, ErrorHandler errorFunc, const ExecutorBasePtr &parent) - : ThenExecutor(reduce, errorFunc, parent) -{ - STORE_EXECUTOR_NAME("ReduceExecutor", Out, In); -} - -template -SyncThenExecutor::SyncThenExecutor(SyncThenTask then, ErrorHandler errorFunc, const ExecutorBasePtr &parent) - : Executor::type, Out, In ...>(errorFunc, parent) - , mFunc(then) -{ - STORE_EXECUTOR_NAME("SyncThenExecutor", Out, In ...); -} - -template -void SyncThenExecutor::run(const ExecutionPtr &execution) -{ - if (execution->prevExecution) { - assert(execution->prevExecution->resultBase->isFinished()); - } - - run(execution, std::is_void()); - execution->resultBase->setFinished(); -} - -template -void SyncThenExecutor::run(const ExecutionPtr &execution, std::false_type) -{ - KAsync::Future::type> *prevFuture = - execution->prevExecution - ? execution->prevExecution->result::type>() - : nullptr; - (void) prevFuture; // silence 'set but not used' warning - KAsync::Future *future = execution->result(); - future->setValue(SyncThenExecutor::mFunc(prevFuture ? prevFuture->value() : In() ...)); -} - -template -void SyncThenExecutor::run(const ExecutionPtr &execution, std::true_type) -{ - KAsync::Future::type> *prevFuture = - execution->prevExecution - ? execution->prevExecution->result::type>() - : nullptr; - (void) prevFuture; // silence 'set but not used' warning - SyncThenExecutor::mFunc(prevFuture ? prevFuture->value() : In() ...); -} - -template -SyncEachExecutor::SyncEachExecutor(SyncEachTask each, ErrorHandler errorFunc, const ExecutorBasePtr &parent) - : Executor(errorFunc, parent) - , mFunc(each) -{ - STORE_EXECUTOR_NAME("SyncEachExecutor", PrevOut, Out, In); -} - -template -void SyncEachExecutor::run(const ExecutionPtr &execution) -{ - assert(execution->prevExecution); - auto *prevFuture = execution->prevExecution->result(); - assert(prevFuture->isFinished()); - - auto out = execution->result(); - if (prevFuture->value().isEmpty()) { - out->setFinished(); - return; - } - - for (auto arg : prevFuture->value()) { - run(out, arg, std::is_void()); - } - out->setFinished(); -} - -template -void SyncEachExecutor::run(KAsync::Future *out, const typename PrevOut::value_type &arg, std::false_type) -{ - out->setValue(out->value() + SyncEachExecutor::mFunc(arg)); -} - -template -void SyncEachExecutor::run(KAsync::Future * /* unused */, const typename PrevOut::value_type &arg, std::true_type) -{ - SyncEachExecutor::mFunc(arg); -} - -template -SyncReduceExecutor::SyncReduceExecutor(SyncReduceTask reduce, ErrorHandler errorFunc, const ExecutorBasePtr &parent) - : SyncThenExecutor(reduce, errorFunc, parent) -{ - STORE_EXECUTOR_NAME("SyncReduceExecutor", Out, In); -} - - -} // namespace Private - -} // namespace KAsync - - - -#endif // KASYNC_H - - diff --git a/async/src/async_impl.h b/async/src/async_impl.h deleted file mode 100644 index 5b4e393..0000000 --- a/async/src/async_impl.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2014 - 2015 Daniel Vrátil - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library. If not, see . - */ - -#ifndef KASYNC_IMPL_H -#define KASYNC_IMPL_H - -#include "async.h" -#include - -namespace KAsync { - -namespace detail { - -template -struct identity -{ - typedef T type; -}; - -template -struct isIterable { - enum { value = 0 }; -}; - -template -struct isIterable::type> { - enum { value = 1 }; -}; - -template -struct prevOut { - using type = typename std::tuple_element<0, std::tuple>::type; -}; - -template -inline typename std::enable_if::value, void>::type -copyFutureValue(const KAsync::Future &in, KAsync::Future &out) -{ - out.setValue(in.value()); -} - -template -inline typename std::enable_if::value, void>::type -copyFutureValue(const KAsync::Future &in, KAsync::Future &out) -{ - // noop -} - -template -inline typename std::enable_if::value, void>::type -aggregateFutureValue(const KAsync::Future &in, KAsync::Future &out) -{ - out.setValue(out.value() + in.value()); -} - -template -inline typename std::enable_if::value, void>::type -aggregateFutureValue(const KAsync::Future &in, KAsync::Future &out) -{ - // noop -} - -} // namespace Detail - -} // namespace KAsync - -#endif // KASYNC_IMPL_H diff --git a/async/src/debug.cpp b/async/src/debug.cpp deleted file mode 100644 index 64a3a3b..0000000 --- a/async/src/debug.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2015 Daniel Vrátil - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library. If not, see . - */ - -#include "debug.h" -#include "async.h" - -#include - -#ifdef __GNUG__ -#include -#include -#endif - -namespace KAsync -{ - -Q_LOGGING_CATEGORY(Debug, "org.kde.async", QtWarningMsg); -Q_LOGGING_CATEGORY(Trace, "org.kde.async.trace", QtWarningMsg); - -QString demangleName(const char *name) -{ -#ifdef __GNUG__ - int status = 1; // uses -3 to 0 error codes - std::unique_ptr demangled(abi::__cxa_demangle(name, 0, 0, &status), std::free); - if (status == 0) { - return QString::fromLatin1(demangled.get()); - } -#endif - return QString::fromLatin1(name); -} - -} - -using namespace KAsync; - -int Tracer::lastId = 0; - -Tracer::Tracer(Private::Execution *execution) - : mId(lastId++) - , mExecution(execution) -{ - msg(KAsync::Tracer::Start); -} - -Tracer::~Tracer() -{ - msg(KAsync::Tracer::End); - // FIXME: Does this work on parallel executions? - --lastId; - --mId; -} - -void Tracer::msg(Tracer::MsgType msgType) -{ -#ifndef QT_NO_DEBUG - qCDebug(Trace).nospace() << (QString().fill(QLatin1Char(' '), mId * 2) % - (msgType == KAsync::Tracer::Start ? QStringLiteral(" START ") : QStringLiteral(" END ")) % - QString::number(mId) % QStringLiteral(" ") % - mExecution->executor->mExecutorName); -#endif -} diff --git a/async/src/debug.h b/async/src/debug.h deleted file mode 100644 index b2b2ff7..0000000 --- a/async/src/debug.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2015 Daniel Vrátil - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library. If not, see . - */ - -#ifndef KASYNC_DEBUG_H -#define KASYNC_DEBUG_H - -#include "kasync_export.h" - -#include -#include - -#ifndef QT_NO_DEBUG -#include -#endif - -namespace KAsync -{ - -Q_DECLARE_LOGGING_CATEGORY(Debug) -Q_DECLARE_LOGGING_CATEGORY(Trace) - -KASYNC_EXPORT QString demangleName(const char *name); - -namespace Private -{ -class Execution; -} - -class KASYNC_EXPORT Tracer -{ -public: - Tracer(Private::Execution *execution); - ~Tracer(); - -private: - enum MsgType { - Start, - End - }; - void msg(MsgType); - - int mId; - Private::Execution *mExecution; - - static int lastId; -}; - -} - -#ifndef QT_NO_DEBUG - template - QString storeExecutorNameExpanded() { - return KAsync::demangleName(typeid(T).name()); - } - - template - typename std::enable_if::type - storeExecutorNameExpanded() { - return storeExecutorNameExpanded() % QStringLiteral(", ") % storeExecutorNameExpanded(); - } - - #define STORE_EXECUTOR_NAME(name, ...) \ - ExecutorBase::mExecutorName = QStringLiteral(name) % QStringLiteral("<") % storeExecutorNameExpanded<__VA_ARGS__>() % QStringLiteral(">") -#else - #define STORE_EXECUTOR_NAME(...) -#endif - -#endif // KASYNC_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 9281cc8..0000000 --- a/async/src/future.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2014 Daniel Vrátil - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library. If not, see . - */ - -#include "future.h" -#include "async.h" - -using namespace KAsync; - -FutureBase::PrivateBase::PrivateBase(const Private::ExecutionPtr &execution) - : finished(false) - , errorCode(0) - , mExecution(execution) -{ -} - -FutureBase::PrivateBase::~PrivateBase() -{ - Private::ExecutionPtr executionPtr = mExecution.toStrongRef(); - if (executionPtr) { - executionPtr->releaseFuture(); - releaseExecution(); - } -} - -void FutureBase::PrivateBase::releaseExecution() -{ - mExecution.clear(); -} - - - -FutureBase::FutureBase() - : d(nullptr) -{ -} - -FutureBase::FutureBase(FutureBase::PrivateBase *dd) - : d(dd) -{ -} - -FutureBase::FutureBase(const KAsync::FutureBase &other) - : d(other.d) -{ -} - -FutureBase::~FutureBase() -{ -} - -void FutureBase::releaseExecution() -{ - d->releaseExecution(); -} - -void FutureBase::setFinished() -{ - if (isFinished()) { - return; - } - d->finished = true; - for (auto watcher : d->watchers) { - if (watcher) { - watcher->futureReadyCallback(); - } - } -} - -bool FutureBase::isFinished() const -{ - return d->finished; -} - -void FutureBase::setError(int code, const QString &message) -{ - d->errorCode = code; - d->errorMessage = message; - setFinished(); -} - -int FutureBase::errorCode() const -{ - return d->errorCode; -} - -QString FutureBase::errorMessage() const -{ - return d->errorMessage; -} - -void FutureBase::setProgress(int processed, int total) -{ - setProgress((qreal) processed / total); -} - -void FutureBase::setProgress(qreal progress) -{ - for (auto watcher : d->watchers) { - if (watcher) { - watcher->futureProgressCallback(progress); - } - } -} - - - -void FutureBase::addWatcher(FutureWatcherBase* watcher) -{ - d->watchers.append(QPointer(watcher)); -} - - - - - -FutureWatcherBase::FutureWatcherBase(QObject *parent) - : QObject(parent) - , d(new FutureWatcherBase::Private) -{ -} - -FutureWatcherBase::~FutureWatcherBase() -{ - delete d; -} - -void FutureWatcherBase::futureReadyCallback() -{ - Q_EMIT futureReady(); -} - -void FutureWatcherBase::futureProgressCallback(qreal progress) -{ - Q_EMIT futureProgress(progress); -} - -void FutureWatcherBase::setFutureImpl(const FutureBase &future) -{ - d->future = future; - d->future.addWatcher(this); - if (future.isFinished()) { - futureReadyCallback(); - } -} diff --git a/async/src/future.h b/async/src/future.h deleted file mode 100644 index b2b723e..0000000 --- a/async/src/future.h +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Copyright 2014 Daniel Vrátil - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library. If not, see . - */ - -#ifndef FUTURE_H -#define FUTURE_H - -#include "kasync_export.h" - -class QEventLoop; - -#include - -#include -#include -#include -#include - -namespace KAsync { - -class FutureWatcherBase; -template -class FutureWatcher; - -namespace Private { -class Execution; -class ExecutorBase; - -typedef QSharedPointer ExecutionPtr; -} // namespace Private - -class KASYNC_EXPORT FutureBase -{ - friend class KAsync::Private::Execution; - friend class FutureWatcherBase; - -public: - virtual ~FutureBase(); - - void setFinished(); - bool isFinished() const; - void setError(int code = 1, const QString &message = QString()); - int errorCode() const; - QString errorMessage() const; - - void setProgress(qreal progress); - void setProgress(int processed, int total); - -protected: - class PrivateBase : public QSharedData - { - public: - PrivateBase(const KAsync::Private::ExecutionPtr &execution); - virtual ~PrivateBase(); - - void releaseExecution(); - - bool finished; - int errorCode; - QString errorMessage; - - QVector> watchers; - private: - QWeakPointer mExecution; - }; - - FutureBase(); - FutureBase(FutureBase::PrivateBase *dd); - FutureBase(const FutureBase &other); - - void addWatcher(KAsync::FutureWatcherBase *watcher); - void releaseExecution(); - -protected: - QExplicitlySharedDataPointer d; -}; - -template -class FutureWatcher; - -template -class Future; - -template -class FutureGeneric : public FutureBase -{ - friend class FutureWatcher; - -public: - void waitForFinished() const - { - if (isFinished()) { - return; - } - FutureWatcher watcher; - QEventLoop eventLoop; - QObject::connect(&watcher, &KAsync::FutureWatcher::futureReady, - &eventLoop, &QEventLoop::quit); - watcher.setFuture(*static_cast*>(this)); - eventLoop.exec(); - } - -protected: - FutureGeneric(const KAsync::Private::ExecutionPtr &execution) - : FutureBase(new Private(execution)) - {} - - FutureGeneric(const FutureGeneric &other) - : FutureBase(other) - {} - -protected: - class Private : public FutureBase::PrivateBase - { - public: - Private(const KAsync::Private::ExecutionPtr &execution) - : FutureBase::PrivateBase(execution) - {} - - typename std::conditional::value, int /* dummy */, T>::type - value; - }; -}; - - -template -class Future : public FutureGeneric -{ - friend class KAsync::Private::ExecutorBase; - - template - friend class KAsync::FutureWatcher; - -public: - Future() - : FutureGeneric(KAsync::Private::ExecutionPtr()) - {} - - Future(const Future &other) - : FutureGeneric(other) - {} - - void setValue(const T &value) - { - static_cast::Private*>(this->d.data())->value = value; - } - - T value() const - { - return static_cast::Private*>(this->d.data())->value; - } - -protected: - Future(const KAsync::Private::ExecutionPtr &execution) - : FutureGeneric(execution) - {} - -}; - -template<> -class Future : public FutureGeneric -{ - friend class KAsync::Private::ExecutorBase; - -public: - Future() - : FutureGeneric(KAsync::Private::ExecutionPtr()) - {} - - Future(const Future &other) - : FutureGeneric(other) - {} - -protected: - Future(const KAsync::Private::ExecutionPtr &execution) - : FutureGeneric(execution) - {} -}; - - - - - -class KASYNC_EXPORT FutureWatcherBase : public QObject -{ - Q_OBJECT - - friend class FutureBase; - -Q_SIGNALS: - void futureReady(); - void futureProgress(qreal progress); - -protected: - FutureWatcherBase(QObject *parent = nullptr); - virtual ~FutureWatcherBase(); - - void futureReadyCallback(); - void futureProgressCallback(qreal progress); - - void setFutureImpl(const KAsync::FutureBase &future); - -protected: - class Private { - public: - KAsync::FutureBase future; - }; - - Private * const d; - -private: - Q_DISABLE_COPY(FutureWatcherBase); -}; - -template -class FutureWatcher : public FutureWatcherBase -{ - friend class KAsync::FutureGeneric; - -public: - FutureWatcher(QObject *parent = nullptr) - : FutureWatcherBase(parent) - {} - - ~FutureWatcher() - {} - - void setFuture(const KAsync::Future &future) - { - setFutureImpl(*static_cast(&future)); - } - - KAsync::Future future() const - { - return *static_cast*>(&d->future); - } - -private: - Q_DISABLE_COPY(FutureWatcher); -}; - -} // namespace Async - -#endif // FUTURE_H -- cgit v1.2.3