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/src/async.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'async/src') diff --git a/async/src/async.h b/async/src/async.h index c6ca9e7..7425604 100644 --- a/async/src/async.h +++ b/async/src/async.h @@ -499,9 +499,14 @@ private: auto job = otherJob; FutureWatcher *watcher = new FutureWatcher(); QObject::connect(watcher, &FutureWatcherBase::futureReady, - [watcher, &future]() { - Async::detail::copyFutureValue(watcher->future(), future); - future.setFinished(); + [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; + Async::detail::copyFutureValue(watcher->future(), outFuture); + outFuture.setFinished(); delete watcher; }); watcher->setFuture(job.exec(in ...)); -- cgit v1.2.3 From cc6156e187b376ba127338100cefbc99ca41f47b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Vr=C3=A1til?= Date: Fri, 3 Apr 2015 16:34:38 +0200 Subject: Async: one TODO done, few more to go --- async/src/async.h | 1 - 1 file changed, 1 deletion(-) (limited to 'async/src') diff --git a/async/src/async.h b/async/src/async.h index 7425604..6cb3825 100644 --- a/async/src/async.h +++ b/async/src/async.h @@ -59,7 +59,6 @@ * TODO: Progress reporting through future * 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. - * TODO: Repeated execution of a job to facilitate i.e. an async while loop of a job? */ namespace Async { -- cgit v1.2.3 From 160255f96e46818b6df63b00f17f502df9ab0a79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Vr=C3=A1til?= Date: Fri, 3 Apr 2015 17:55:23 +0200 Subject: Async: move as much Future code as possible from public header to .cpp --- async/src/future.cpp | 92 ++++++++++++++++++++++++++++++---- async/src/future.h | 136 ++++++++++++++++++++------------------------------- 2 files changed, 135 insertions(+), 93 deletions(-) (limited to 'async/src') diff --git a/async/src/future.cpp b/async/src/future.cpp index 50a326a..970060b 100644 --- a/async/src/future.cpp +++ b/async/src/future.cpp @@ -20,11 +20,41 @@ using namespace Async; +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 Async::FutureBase &other) + : d(other.d) { } @@ -32,35 +62,77 @@ FutureBase::~FutureBase() { } -FutureBase::PrivateBase::PrivateBase(const Private::ExecutionPtr &execution) - : mExecution(execution) +void FutureBase::releaseExecution() { + d->releaseExecution(); } -FutureBase::PrivateBase::~PrivateBase() +void FutureBase::setFinished() { - Private::ExecutionPtr executionPtr = mExecution.toStrongRef(); - if (executionPtr) { - executionPtr->releaseFuture(); - releaseExecution(); + if (isFinished()) { + return; + } + d->finished = true; + for (auto watcher : d->watchers) { + if (watcher) { + watcher->futureReadyCallback(); + } } } -void FutureBase::PrivateBase::releaseExecution() +bool FutureBase::isFinished() const { - mExecution.clear(); + 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::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(); +} -#include "future.moc" +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 index cadd96d..bc18f00 100644 --- a/async/src/future.h +++ b/async/src/future.h @@ -29,28 +29,32 @@ class QEventLoop; namespace Async { +class FutureWatcherBase; +template +class FutureWatcher; + namespace Private { class Execution; class ExecutorBase; typedef QSharedPointer ExecutionPtr; - -} +} // namespace Private class FutureBase { friend class Async::Private::Execution; + friend class FutureWatcherBase; public: virtual ~FutureBase(); - virtual void setFinished() = 0; - virtual bool isFinished() const = 0; - virtual void setError(int code = 1, const QString &message = QString()) = 0; + void setFinished(); + bool isFinished() const; + void setError(int code = 1, const QString &message = QString()); + int errorCode() const; + QString errorMessage() const; protected: - virtual void releaseExecution() = 0; - class PrivateBase : public QSharedData { public: @@ -59,12 +63,24 @@ protected: void releaseExecution(); + bool finished; + int errorCode; + QString errorMessage; + + QVector> watchers; private: QWeakPointer mExecution; }; FutureBase(); + FutureBase(FutureBase::PrivateBase *dd); FutureBase(const FutureBase &other); + + void addWatcher(Async::FutureWatcherBase *watcher); + void releaseExecution(); + +protected: + QExplicitlySharedDataPointer d; }; template @@ -79,41 +95,6 @@ class FutureGeneric : public FutureBase friend class FutureWatcher; public: - void setFinished() - { - if (d->finished) { - return; - } - d->finished = true; - for (auto watcher : d->watchers) { - if (watcher) { - watcher->futureReadyCallback(); - } - } - } - - bool isFinished() const - { - return d->finished; - } - - void setError(int errorCode, const QString &message) - { - d->errorCode = errorCode; - d->errorMessage = message; - setFinished(); - } - - int errorCode() const - { - return d->errorCode; - } - - QString errorMessage() const - { - return d->errorMessage; - } - void waitForFinished() { if (isFinished()) { @@ -129,46 +110,27 @@ public: protected: FutureGeneric(const Async::Private::ExecutionPtr &execution) - : FutureBase() - , d(new Private(execution)) + : FutureBase(new Private(execution)) {} FutureGeneric(const FutureGeneric &other) : FutureBase(other) - , d(other.d) {} +protected: class Private : public FutureBase::PrivateBase { public: Private(const Async::Private::ExecutionPtr &execution) : FutureBase::PrivateBase(execution) - , finished(false) - , errorCode(0) {} typename std::conditional::value, int /* dummy */, T>::type value; - - QVector>> watchers; - bool finished; - int errorCode; - QString errorMessage; }; - - QExplicitlySharedDataPointer d; - - void releaseExecution() - { - d->releaseExecution(); - } - - void addWatcher(FutureWatcher *watcher) - { - d->watchers.append(QPointer>(watcher)); - } }; + template class Future : public FutureGeneric { @@ -188,12 +150,12 @@ public: void setValue(const T &value) { - this->d->value = value; + static_cast::Private*>(this->d.data())->value = value; } T value() const { - return this->d->value; + return static_cast::Private*>(this->d.data())->value; } protected: @@ -208,9 +170,6 @@ class Future : public FutureGeneric { friend class Async::Private::ExecutorBase; - template - friend class Async::FutureWatcher; - public: Future() : FutureGeneric(Async::Private::ExecutionPtr()) @@ -227,16 +186,36 @@ protected: }; + + + class FutureWatcherBase : public QObject { Q_OBJECT + friend class FutureBase; + +Q_SIGNALS: + void futureReady(); + protected: FutureWatcherBase(QObject *parent = nullptr); virtual ~FutureWatcherBase(); -Q_SIGNALS: - void futureReady(); + void futureReadyCallback(); + + void setFutureImpl(const Async::FutureBase &future); + +protected: + class Private { + public: + Async::FutureBase future; + }; + + Private * const d; + +private: + Q_DISABLE_COPY(FutureWatcherBase); }; template @@ -254,25 +233,16 @@ public: void setFuture(const Async::Future &future) { - mFuture = future; - mFuture.addWatcher(this); - if (future.isFinished()) { - futureReadyCallback(); - } + setFutureImpl(*static_cast(&future)); } Async::Future future() const { - return mFuture; + return *static_cast*>(&d->future); } private: - void futureReadyCallback() - { - Q_EMIT futureReady(); - } - - Async::Future mFuture; + Q_DISABLE_COPY(FutureWatcher); }; } // namespace Async -- 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/src/async.h | 2 +- async/src/future.cpp | 20 ++++++++++++++++++++ async/src/future.h | 5 +++++ 3 files changed, 26 insertions(+), 1 deletion(-) (limited to 'async/src') diff --git a/async/src/async.h b/async/src/async.h index 6cb3825..c316639 100644 --- a/async/src/async.h +++ b/async/src/async.h @@ -56,7 +56,7 @@ * valid until the task is complete. To abort a job it has to be killed through the future handle. * TODO: Can we tie the lifetime of the executor to the last available future handle? * - * TODO: Progress reporting through future + * 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. */ diff --git a/async/src/future.cpp b/async/src/future.cpp index 970060b..4f3cd80 100644 --- a/async/src/future.cpp +++ b/async/src/future.cpp @@ -102,6 +102,21 @@ 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) { @@ -128,6 +143,11 @@ void FutureWatcherBase::futureReadyCallback() Q_EMIT futureReady(); } +void FutureWatcherBase::futureProgressCallback(qreal progress) +{ + Q_EMIT futureProgress(progress); +} + void FutureWatcherBase::setFutureImpl(const FutureBase &future) { d->future = future; diff --git a/async/src/future.h b/async/src/future.h index bc18f00..32c1015 100644 --- a/async/src/future.h +++ b/async/src/future.h @@ -54,6 +54,9 @@ public: int errorCode() const; QString errorMessage() const; + void setProgress(qreal progress); + void setProgress(int processed, int total); + protected: class PrivateBase : public QSharedData { @@ -197,12 +200,14 @@ class FutureWatcherBase : public QObject Q_SIGNALS: void futureReady(); + void futureProgress(qreal progress); protected: FutureWatcherBase(QObject *parent = nullptr); virtual ~FutureWatcherBase(); void futureReadyCallback(); + void futureProgressCallback(qreal progress); void setFutureImpl(const Async::FutureBase &future); -- cgit v1.2.3 From 64df12d07dc601d1af660cf50fbd4540d8b5e96e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Vr=C3=A1til?= Date: Fri, 3 Apr 2015 18:52:17 +0200 Subject: Async: update components and lifetime documentation --- async/src/async.h | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'async/src') diff --git a/async/src/async.h b/async/src/async.h index c316639..89ca0d0 100644 --- a/async/src/async.h +++ b/async/src/async.h @@ -46,15 +46,18 @@ * that can be stored and executed later on. Jobs can be composed, similarly to functions. * * Relations between the components: - * * Job: description of what should happen - * * Executor: Running execution of a job, the process that calculates the result. + * * 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 * - * Lifetime: - * * Before a job is executed is treated like a normal value on the stack. - * * As soon as the job is executed, a heap allocated executor keeps the task running until complete. The associated future handle remains - * valid until the task is complete. To abort a job it has to be killed through the future handle. - * TODO: Can we tie the lifetime of the executor to the last available future handle? * * TODO: Composed progress reporting * TODO: Possibility to abort a job through future (perhaps optional?) -- 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/src/async.cpp | 46 +++++++++++++++ async/src/async.h | 157 +++++++++++++++++++++++++++------------------------- 2 files changed, 129 insertions(+), 74 deletions(-) (limited to 'async/src') diff --git a/async/src/async.cpp b/async/src/async.cpp index 5e26bd8..c780878 100644 --- a/async/src/async.cpp +++ b/async/src/async.cpp @@ -24,6 +24,50 @@ using namespace Async; +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(); +} + +void Private::Execution::releaseFuture() +{ + resultBase = 0; +} + +bool Private::Execution::errorWasHandled() const +{ + Execution * const exec = this; + while (exec) { + if (exec->executor->hasErrorFunc()) { + return true; + } + exec = exec->prevExecution.data(); + } + return false; +} + + + + + Private::ExecutorBase::ExecutorBase(const ExecutorBasePtr &parent) : mPrev(parent) { @@ -34,6 +78,8 @@ Private::ExecutorBase::~ExecutorBase() } + + JobBase::JobBase(const Private::ExecutorBasePtr &executor) : mExecutor(executor) { diff --git a/async/src/async.h b/async/src/async.h index 89ca0d0..adc0b69 100644 --- a/async/src/async.h +++ b/async/src/async.h @@ -96,27 +96,9 @@ class ExecutorBase; typedef QSharedPointer ExecutorBasePtr; struct Execution { - Execution(const ExecutorBasePtr &executor) - : executor(executor) - , resultBase(nullptr) - , isRunning(false) - , isFinished(false) - {} - - ~Execution() - { - if (resultBase) { - resultBase->releaseExecution(); - delete resultBase; - } - prevExecution.reset(); - } - - void setFinished() - { - isFinished = true; - executor.clear(); - } + Execution(const ExecutorBasePtr &executor); + ~Execution(); + void setFinished(); template Async::Future* result() @@ -124,10 +106,8 @@ struct Execution { return static_cast*>(resultBase); } - void releaseFuture() - { - resultBase = 0; - } + void releaseFuture(); + bool errorWasHandled() const; ExecutorBasePtr executor; FutureBase *resultBase; @@ -147,6 +127,8 @@ class ExecutorBase template friend class Async::Job; + friend class Execution; + public: virtual ~ExecutorBase(); virtual ExecutionPtr exec(const ExecutorBasePtr &self) = 0; @@ -157,6 +139,9 @@ protected: template Async::Future* createFuture(const ExecutionPtr &execution) const; + virtual bool hasErrorFunc() const = 0; + virtual bool handleError(const ExecutionPtr &execution) = 0; + ExecutorBasePtr mPrev; }; @@ -164,14 +149,17 @@ template class Executor : public ExecutorBase { protected: - Executor(ErrorHandler errorHandler, const Private::ExecutorBasePtr &parent) + Executor(ErrorHandler errorFunc, const Private::ExecutorBasePtr &parent) : ExecutorBase(parent) - , mErrorFunc(errorHandler) + , 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; }; @@ -180,7 +168,7 @@ template class ThenExecutor: public Executor::type, Out, In ...> { public: - ThenExecutor(ThenTask then, ErrorHandler errorHandler, const ExecutorBasePtr &parent); + ThenExecutor(ThenTask then, ErrorHandler errorFunc, const ExecutorBasePtr &parent); void run(const ExecutionPtr &execution); private: ThenTask mFunc; @@ -190,7 +178,7 @@ template class EachExecutor : public Executor { public: - EachExecutor(EachTask each, ErrorHandler errorHandler, const ExecutorBasePtr &parent); + EachExecutor(EachTask each, ErrorHandler errorFunc, const ExecutorBasePtr &parent); void run(const ExecutionPtr &execution); private: EachTask mFunc; @@ -201,7 +189,7 @@ template class ReduceExecutor : public ThenExecutor { public: - ReduceExecutor(ReduceTask reduce, ErrorHandler errorHandler, const ExecutorBasePtr &parent); + ReduceExecutor(ReduceTask reduce, ErrorHandler errorFunc, const ExecutorBasePtr &parent); private: ReduceTask mFunc; }; @@ -210,7 +198,7 @@ template class SyncThenExecutor : public Executor::type, Out, In ...> { public: - SyncThenExecutor(SyncThenTask then, ErrorHandler errorHandler, const ExecutorBasePtr &parent); + SyncThenExecutor(SyncThenTask then, ErrorHandler errorFunc, const ExecutorBasePtr &parent); void run(const ExecutionPtr &execution); private: @@ -223,7 +211,7 @@ template class SyncReduceExecutor : public SyncThenExecutor { public: - SyncReduceExecutor(SyncReduceTask reduce, ErrorHandler errorHandler, const ExecutorBasePtr &parent); + SyncReduceExecutor(SyncReduceTask reduce, ErrorHandler errorFunc, const ExecutorBasePtr &parent); private: SyncReduceTask mFunc; }; @@ -232,7 +220,7 @@ template class SyncEachExecutor : public Executor { public: - SyncEachExecutor(SyncEachTask each, ErrorHandler errorHandler, const ExecutorBasePtr &parent); + SyncEachExecutor(SyncEachTask each, ErrorHandler errorFunc, const ExecutorBasePtr &parent); void run(const ExecutionPtr &execution); private: void run(Async::Future *future, const typename PrevOut::value_type &arg, std::false_type); // !std::is_void @@ -253,10 +241,10 @@ private: * where @p In is type of the result. */ template -Job start(ThenTask func); +Job start(ThenTask func, ErrorHandler errorFunc = ErrorHandler()); template -Job start(SyncThenTask func); +Job start(SyncThenTask func, ErrorHandler errorFunc = ErrorHandler()); #ifdef WITH_KJOB template @@ -358,10 +346,10 @@ class Job : public JobBase friend class Job; template - friend Job start(Async::ThenTask func); + friend Job start(Async::ThenTask func, ErrorHandler errorFunc); template - friend Job start(Async::SyncThenTask func); + friend Job start(Async::SyncThenTask func, ErrorHandler errorFunc); #ifdef WITH_KJOB template @@ -524,17 +512,17 @@ private: namespace Async { template -Job start(ThenTask func) +Job start(ThenTask func, ErrorHandler error) { return Job(Private::ExecutorBasePtr( - new Private::ThenExecutor(func, ErrorHandler(), Private::ExecutorBasePtr()))); + new Private::ThenExecutor(func, error, Private::ExecutorBasePtr()))); } template -Job start(SyncThenTask func) +Job start(SyncThenTask func, ErrorHandler error) { return Job(Private::ExecutorBasePtr( - new Private::SyncThenExecutor(func, ErrorHandler(), Private::ExecutorBasePtr()))); + new Private::SyncThenExecutor(func, error, Private::ExecutorBasePtr()))); } #ifdef WITH_KJOB @@ -596,17 +584,12 @@ ExecutionPtr Executor::exec(const ExecutorBasePtr &self) // chainup execution->prevExecution = mPrev ? mPrev->exec(mPrev) : ExecutionPtr(); - /* - } else if (mPrev && !mPrevFuture) { - // If previous job is running or finished, just get it's future - mPrevFuture = static_cast*>(mPrev->result()); - } - */ execution->resultBase = this->createFuture(execution); auto fw = new Async::FutureWatcher(); QObject::connect(fw, &Async::FutureWatcher::futureReady, [fw, execution, this]() { + handleError(execution); execution->setFinished(); delete fw; }); @@ -614,44 +597,70 @@ ExecutionPtr Executor::exec(const ExecutorBasePtr &self) Async::Future *prevFuture = execution->prevExecution ? execution->prevExecution->result() : nullptr; if (!prevFuture || prevFuture->isFinished()) { - if (prevFuture && prevFuture->errorCode() != 0) { - if (mErrorFunc) { - mErrorFunc(prevFuture->errorCode(), prevFuture->errorMessage()); - execution->resultBase->setFinished(); - execution->setFinished(); - return execution; + 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 the error to next caller + // Propagate error (if any) } } + execution->isRunning = true; run(execution); } else { - auto futureWatcher = new Async::FutureWatcher(); - QObject::connect(futureWatcher, &Async::FutureWatcher::futureReady, - [futureWatcher, execution, this]() { - auto prevFuture = futureWatcher->future(); + auto prevFutureWatcher = new Async::FutureWatcher(); + QObject::connect(prevFutureWatcher, &Async::FutureWatcher::futureReady, + [prevFutureWatcher, execution, this]() { + auto prevFuture = prevFutureWatcher->future(); assert(prevFuture.isFinished()); - delete futureWatcher; - if (prevFuture.errorCode() != 0) { - if (mErrorFunc) { - mErrorFunc(prevFuture.errorCode(), prevFuture.errorMessage()); - execution->resultBase->setFinished(); - return; + 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 { - // Propagate the error to next caller + return; } } + + + // propagate error (if any) execution->isRunning = true; run(execution); }); - futureWatcher->setFuture(*static_cast*>(prevFuture)); + 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) @@ -715,14 +724,14 @@ void EachExecutor::run(const ExecutionPtr &execution) } template -ReduceExecutor::ReduceExecutor(ReduceTask reduce, ErrorHandler error, const ExecutorBasePtr &parent) - : ThenExecutor(reduce, error, parent) +ReduceExecutor::ReduceExecutor(ReduceTask reduce, ErrorHandler errorFunc, const ExecutorBasePtr &parent) + : ThenExecutor(reduce, errorFunc, parent) { } template -SyncThenExecutor::SyncThenExecutor(SyncThenTask then, ErrorHandler errorHandler, const ExecutorBasePtr &parent) - : Executor::type, Out, In ...>(errorHandler, parent) +SyncThenExecutor::SyncThenExecutor(SyncThenTask then, ErrorHandler errorFunc, const ExecutorBasePtr &parent) + : Executor::type, Out, In ...>(errorFunc, parent) , mFunc(then) { } @@ -762,8 +771,8 @@ void SyncThenExecutor::run(const ExecutionPtr &execution, std::true } template -SyncEachExecutor::SyncEachExecutor(SyncEachTask each, ErrorHandler errorHandler, const ExecutorBasePtr &parent) - : Executor(errorHandler, parent) +SyncEachExecutor::SyncEachExecutor(SyncEachTask each, ErrorHandler errorFunc, const ExecutorBasePtr &parent) + : Executor(errorFunc, parent) , mFunc(each) { } @@ -800,8 +809,8 @@ void SyncEachExecutor::run(Async::Future * /* unused */, } template -SyncReduceExecutor::SyncReduceExecutor(SyncReduceTask reduce, ErrorHandler errorHandler, const ExecutorBasePtr &parent) - : SyncThenExecutor(reduce, errorHandler, parent) +SyncReduceExecutor::SyncReduceExecutor(SyncReduceTask reduce, ErrorHandler errorFunc, const ExecutorBasePtr &parent) + : SyncThenExecutor(reduce, errorFunc, parent) { } -- cgit v1.2.3 From e069326a13905e3d3db9db58fbe69afa908063d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Vr=C3=A1til?= Date: Mon, 6 Apr 2015 19:03:24 +0200 Subject: Async: const'ify --- async/src/async.h | 2 +- async/src/future.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'async/src') diff --git a/async/src/async.h b/async/src/async.h index adc0b69..2430a27 100644 --- a/async/src/async.h +++ b/async/src/async.h @@ -101,7 +101,7 @@ struct Execution { void setFinished(); template - Async::Future* result() + Async::Future* result() const { return static_cast*>(resultBase); } diff --git a/async/src/future.h b/async/src/future.h index 32c1015..bce00bb 100644 --- a/async/src/future.h +++ b/async/src/future.h @@ -98,7 +98,7 @@ class FutureGeneric : public FutureBase friend class FutureWatcher; public: - void waitForFinished() + void waitForFinished() const { if (isFinished()) { return; -- cgit v1.2.3 From 14cf20a4a7165c48df52f1b2c35745e2a47d6b5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Vr=C3=A1til?= Date: Fri, 10 Apr 2015 10:49:58 +0200 Subject: Async: fix build --- async/src/async.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'async/src') diff --git a/async/src/async.cpp b/async/src/async.cpp index c780878..6f28084 100644 --- a/async/src/async.cpp +++ b/async/src/async.cpp @@ -54,7 +54,7 @@ void Private::Execution::releaseFuture() bool Private::Execution::errorWasHandled() const { - Execution * const exec = this; + Execution *exec = const_cast(this); while (exec) { if (exec->executor->hasErrorFunc()) { return true; -- cgit v1.2.3 From f87c777c3c9e4fe9ca4c685363db572f1dce25e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Vr=C3=A1til?= Date: Fri, 10 Apr 2015 10:55:06 +0200 Subject: Async: fix build for real this time --- async/src/future.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'async/src') diff --git a/async/src/future.h b/async/src/future.h index bce00bb..ff199ef 100644 --- a/async/src/future.h +++ b/async/src/future.h @@ -107,7 +107,7 @@ public: QEventLoop eventLoop; QObject::connect(&watcher, &Async::FutureWatcher::futureReady, &eventLoop, &QEventLoop::quit); - watcher.setFuture(*static_cast*>(this)); + watcher.setFuture(*static_cast*>(this)); eventLoop.exec(); } -- cgit v1.2.3 From 8d5f4e8485db0bfc0745a9852bac9eceab3b769f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Vr=C3=A1til?= Date: Fri, 10 Apr 2015 19:09:47 +0200 Subject: Async: use baseclass instead of this-> to refer to parent class member --- async/src/async.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'async/src') diff --git a/async/src/async.h b/async/src/async.h index 2430a27..2243046 100644 --- a/async/src/async.h +++ b/async/src/async.h @@ -585,7 +585,7 @@ ExecutionPtr Executor::exec(const ExecutorBasePtr &self) // chainup execution->prevExecution = mPrev ? mPrev->exec(mPrev) : ExecutionPtr(); - execution->resultBase = this->createFuture(execution); + execution->resultBase = ExecutorBase::createFuture(execution); auto fw = new Async::FutureWatcher(); QObject::connect(fw, &Async::FutureWatcher::futureReady, [fw, execution, this]() { @@ -678,7 +678,7 @@ void ThenExecutor::run(const ExecutionPtr &execution) assert(prevFuture->isFinished()); } - this->mFunc(prevFuture ? prevFuture->value() : In() ..., *execution->result()); + ThenExecutor::mFunc(prevFuture ? prevFuture->value() : In() ..., *execution->result()); } template @@ -703,7 +703,7 @@ void EachExecutor::run(const ExecutionPtr &execution) for (auto arg : prevFuture->value()) { Async::Future future; - this->mFunc(arg, future); + EachExecutor::mFunc(arg, future); auto fw = new Async::FutureWatcher(); mFutureWatchers.append(fw); QObject::connect(fw, &Async::FutureWatcher::futureReady, @@ -756,7 +756,7 @@ void SyncThenExecutor::run(const ExecutionPtr &execution, std::fals : nullptr; (void) prevFuture; // silence 'set but not used' warning Async::Future *future = execution->result(); - future->setValue(this->mFunc(prevFuture ? prevFuture->value() : In() ...)); + future->setValue(SyncThenExecutor::mFunc(prevFuture ? prevFuture->value() : In() ...)); } template @@ -767,7 +767,7 @@ void SyncThenExecutor::run(const ExecutionPtr &execution, std::true ? execution->prevExecution->result::type>() : nullptr; (void) prevFuture; // silence 'set but not used' warning - this->mFunc(prevFuture ? prevFuture->value() : In() ...); + SyncThenExecutor::mFunc(prevFuture ? prevFuture->value() : In() ...); } template @@ -799,13 +799,13 @@ void SyncEachExecutor::run(const ExecutionPtr &execution) template void SyncEachExecutor::run(Async::Future *out, const typename PrevOut::value_type &arg, std::false_type) { - out->setValue(out->value() + this->mFunc(arg)); + out->setValue(out->value() + SyncEachExecutor::mFunc(arg)); } template void SyncEachExecutor::run(Async::Future * /* unused */, const typename PrevOut::value_type &arg, std::true_type) { - this->mFunc(arg); + SyncEachExecutor::mFunc(arg); } template -- cgit v1.2.3 From 73b8fe58c6fb985898d2bbf431926f0628e2b0a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Vr=C3=A1til?= Date: Sat, 11 Apr 2015 11:44:49 +0200 Subject: Async: add runtime executor tracing for easier debugging --- async/src/CMakeLists.txt | 1 + async/src/async.cpp | 6 +++- async/src/async.h | 20 ++++++++++++ async/src/debug.cpp | 73 +++++++++++++++++++++++++++++++++++++++++++ async/src/debug.h | 80 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 async/src/debug.cpp create mode 100644 async/src/debug.h (limited to 'async/src') diff --git a/async/src/CMakeLists.txt b/async/src/CMakeLists.txt index 6f8ab63..becc8ee 100644 --- a/async/src/CMakeLists.txt +++ b/async/src/CMakeLists.txt @@ -5,6 +5,7 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR}) set(async_SRCS async.cpp future.cpp + debug.cpp ) add_library(${PROJECT_NAME} SHARED ${async_SRCS}) diff --git a/async/src/async.cpp b/async/src/async.cpp index 6f28084..e92d333 100644 --- a/async/src/async.cpp +++ b/async/src/async.cpp @@ -21,7 +21,6 @@ #include #include - using namespace Async; Private::Execution::Execution(const Private::ExecutorBasePtr &executor) @@ -45,6 +44,11 @@ void Private::Execution::setFinished() { isFinished = true; //executor.clear(); +#ifndef QT_NO_DEBUG + if (tracer) { + delete tracer; + } +#endif } void Private::Execution::releaseFuture() diff --git a/async/src/async.h b/async/src/async.h index 2243046..73eeaa0 100644 --- a/async/src/async.h +++ b/async/src/async.h @@ -25,6 +25,7 @@ #include #include "future.h" +#include "debug.h" #include "async_impl.h" #include @@ -115,8 +116,13 @@ struct Execution { bool isFinished; ExecutionPtr prevExecution; + +#ifndef QT_NO_DEBUG + Tracer *tracer; +#endif }; + typedef QSharedPointer ExecutionPtr; class ExecutorBase @@ -128,6 +134,7 @@ class ExecutorBase friend class Async::Job; friend class Execution; + friend class Async::Tracer; public: virtual ~ExecutorBase(); @@ -143,6 +150,10 @@ protected: virtual bool handleError(const ExecutionPtr &execution) = 0; ExecutorBasePtr mPrev; + +#ifndef QT_NO_DEBUG + QString mExecutorName; +#endif }; template @@ -581,6 +592,9 @@ 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(); @@ -667,6 +681,7 @@ ThenExecutor::ThenExecutor(ThenTask then, ErrorHandler : Executor::type, Out, In ...>(error, parent) , mFunc(then) { + STORE_EXECUTOR_NAME("ThenExecutor", Out, In ...); } template @@ -686,6 +701,7 @@ EachExecutor::EachExecutor(EachTask each, ErrorHandle : Executor(error, parent) , mFunc(each) { + STORE_EXECUTOR_NAME("EachExecutor", PrevOut, Out, In); } template @@ -727,6 +743,7 @@ template ReduceExecutor::ReduceExecutor(ReduceTask reduce, ErrorHandler errorFunc, const ExecutorBasePtr &parent) : ThenExecutor(reduce, errorFunc, parent) { + STORE_EXECUTOR_NAME("ReduceExecutor", Out, In); } template @@ -734,6 +751,7 @@ SyncThenExecutor::SyncThenExecutor(SyncThenTask then, : Executor::type, Out, In ...>(errorFunc, parent) , mFunc(then) { + STORE_EXECUTOR_NAME("SyncThenExecutor", Out, In ...); } template @@ -775,6 +793,7 @@ SyncEachExecutor::SyncEachExecutor(SyncEachTask each, : Executor(errorFunc, parent) , mFunc(each) { + STORE_EXECUTOR_NAME("SyncEachExecutor", PrevOut, Out, In); } template @@ -812,6 +831,7 @@ template SyncReduceExecutor::SyncReduceExecutor(SyncReduceTask reduce, ErrorHandler errorFunc, const ExecutorBasePtr &parent) : SyncThenExecutor(reduce, errorFunc, parent) { + STORE_EXECUTOR_NAME("SyncReduceExecutor", Out, In); } diff --git a/async/src/debug.cpp b/async/src/debug.cpp new file mode 100644 index 0000000..9dfad1a --- /dev/null +++ b/async/src/debug.cpp @@ -0,0 +1,73 @@ +/* + * 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 Async +{ + +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(demangled.get()); + } +#endif + return QString(name); +} + +} + +using namespace Async; + +int Tracer::lastId = 0; + +Tracer::Tracer(Private::Execution *execution) + : mId(lastId++) + , mExecution(execution) +{ + msg(Async::Tracer::Start); +} + +Tracer::~Tracer() +{ + msg(Async::Tracer::End); + // FIXME: Does this work on parallel executions? + --lastId; + --mId; +} + +void Tracer::msg(Tracer::MsgType msgType) +{ + qCDebug(Trace).nospace() << (QString().fill(QLatin1Char(' '), mId * 2) % + (msgType == Async::Tracer::Start ? " START " : " END ") % + QString::number(mId) % " " % + mExecution->executor->mExecutorName); +} diff --git a/async/src/debug.h b/async/src/debug.h new file mode 100644 index 0000000..c453eb3 --- /dev/null +++ b/async/src/debug.h @@ -0,0 +1,80 @@ +/* + * 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 ASYNC_DEBUG_H +#define ASYNC_DEBUG_H + +#include +#include + +#ifndef QT_NO_DEBUG +#include +#endif + +namespace Async +{ + +Q_DECLARE_LOGGING_CATEGORY(Debug) +Q_DECLARE_LOGGING_CATEGORY(Trace) + +QString demangleName(const char *name); + +namespace Private +{ +class Execution; +} + +class 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 Async::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 // ASYNC_DEBUG_H \ No newline at end of file -- cgit v1.2.3