diff --git a/core/ControllableTask.h b/core/ControllableTask.h new file mode 100644 index 0000000..f9ac030 --- /dev/null +++ b/core/ControllableTask.h @@ -0,0 +1,18 @@ +#ifndef CONTROLLABLETASK_H +#define CONTROLLABLETASK_H +/** From answer by Hatter + * + * https://stackoverflow.com/questions/5423058/qfuture-that-can-be-cancelled-and-report-progress + */ + +#include "TaskControl.h" + +template +class ControllableTask +{ +public: + virtual ~ControllableTask() {} + virtual T run(TaskControl& control) = 0; +}; + +#endif // CONTROLLABLETASK_H diff --git a/core/RunControllableTask.h b/core/RunControllableTask.h new file mode 100644 index 0000000..c93e19c --- /dev/null +++ b/core/RunControllableTask.h @@ -0,0 +1,45 @@ +#ifndef RUNCONTROLLABLETASK_H +#define RUNCONTROLLABLETASK_H +/** From answer by Hatter + * + * https://stackoverflow.com/questions/5423058/qfuture-that-can-be-cancelled-and-report-progress + */ + +#include +#include +#include "ControllableTask.h" + +template +class RunControllableTask : public QFutureInterface , public QRunnable +{ +public: + RunControllableTask(ControllableTask* tsk) : task(tsk) { } + virtial ~RunControllableTask() { delete task; } + + QFuture start() + { + this->setRunnable(this); + this->reportStarted(); + QFuture future = this->future(); + QThreadPool::globalInstance()->start(this, /*m_priority*/ 0); + return future; + } + + void run() + { + if (this->isCanceled()) { + this->reportFinished(); + return; + } + TaskControl control(this); + result = this->task->run(control); + if (!this->isCanceled()) { + this->reportResult(result); + } + this->reportFinished(); + } + + T result; + ControllableTask *task; +}; +#endif // RUNCONTROLLABLETASK_H diff --git a/core/TaskControl.h b/core/TaskControl.h new file mode 100644 index 0000000..7984900 --- /dev/null +++ b/core/TaskControl.h @@ -0,0 +1,19 @@ +#ifndef TASKCONTROL_H +#define TASKCONTROL_H +/** From answer by Hatter + * + * https://stackoverflow.com/questions/5423058/qfuture-that-can-be-cancelled-and-report-progress + */ + +#include + +class TaskControl +{ +public: + TaskControl(QFutureInterfaceBase *f) : fu(f) { } + bool shouldRun() const { return !fu->isCanceled(); } +private: + QFutureInterfaceBase *fu; +}; + +#endif // TASKCONTROL_H diff --git a/core/TaskExecutor.h b/core/TaskExecutor.h new file mode 100644 index 0000000..d44cf46 --- /dev/null +++ b/core/TaskExecutor.h @@ -0,0 +1,32 @@ +#ifndef TASKEXECUTOR_H +#define TASKEXECUTOR_H +/* From answer by Hatter + * + * https://stackoverflow.com/questions/5423058/qfuture-that-can-be-cancelled-and-report-progress + */ +#include "ControllableTask.h" +#include "RunControllableTask.h" + +/** + * @brief The TaskExecutor class + * + * The user should sublass ControllableTask, implement background routine which checks sometimes + * method shouldRun() of TaskControl instance passed to run(TaskControl&) and then use it like: + * + * QFututre futureValue = TaskExecutor::run(new SomeControllableTask(inputForThatTask)); + * + * Then she may cancel it by calling futureValue.cancel(), bearing in mind that cancellation is + * graceful and not immediate. + * + */ +class TaskExecutor { +public: + template + static QFuture run(ControllableTask* task) { + return (new RunControllableTask(task))->start(); + } +}; + + + +#endif // TASKEXECUTOR_H diff --git a/core/core.pro b/core/core.pro index fcf9077..d716f57 100644 --- a/core/core.pro +++ b/core/core.pro @@ -50,7 +50,11 @@ HEADERS += PasswordManager.h \ Expected.h \ ExplainTreeModelItem.h \ json/json.h \ - WorkManager.h + WorkManager.h \ + TaskControl.h \ + ControllableTask.h \ + RunControllableTask.h \ + TaskExecutor.h unix { target.path = /usr/lib