1 /**
2  * UGENE - Integrated Bioinformatics Tools.
3  * Copyright (C) 2008-2021 UniPro <ugene@unipro.ru>
4  * http://ugene.net
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  * MA 02110-1301, USA.
20  */
21 
22 #ifndef _U2_TASK_H_
23 #define _U2_TASK_H_
24 
25 #include <QDateTime>
26 #include <QPointer>
27 #include <QReadWriteLock>
28 #include <QStringList>
29 #include <QVarLengthArray>
30 
31 #include <U2Core/Log.h>
32 #include <U2Core/U2OpStatus.h>
33 #include <U2Core/global.h>
34 
35 namespace U2 {
36 
37 struct U2CORE_EXPORT TaskResourceUsage {
38     TaskResourceUsage(int id = 0, int use = 0, bool prepareStage = false)
resourceIdTaskResourceUsage39         : resourceId(id), resourceUse(use), prepareStageLock(prepareStage), locked(false) {
40     }
41 
42     enum UseType {
43         Read,
44         Write
45     };
46 
47     TaskResourceUsage(int id, UseType use, bool prepareStage = false)
resourceIdTaskResourceUsage48         : resourceId(id), resourceUse(use), prepareStageLock(prepareStage), locked(false) {
49     }
50 
51     int resourceId;
52     int resourceUse;
53     bool prepareStageLock;
54     bool locked;
55     /* Leave it empty for default message */
56     QString errorMessage;
57 };
58 
59 class TaskScheduler;
60 
61 /**
62     Holds task state info about current error and progress
63     Error variable is protected by RW lock to ensure safe multi-threaded updates
64 */
65 class U2CORE_EXPORT TaskStateInfo : public U2OpStatus {
66 public:
TaskStateInfo()67     TaskStateInfo()
68         : progress(-1), cancelFlag(false), hasErr(false), lock(QReadWriteLock::NonRecursive) {
69     }
70 
71     /* Percent values in range 0..100, negative if unknown. */
72     int progress;
73     int cancelFlag;
74 
hasError()75     virtual bool hasError() const {
76         return hasErr;
77     }
getError()78     virtual QString getError() const {
79         QReadLocker r(&lock);
80         return error;
81     }
setError(const QString & err)82     virtual void setError(const QString &err) {
83         QWriteLocker w(&lock);
84         error = err;
85         hasErr = !error.isEmpty();
86     }
87 
isCanceled()88     virtual bool isCanceled() const {
89         return cancelFlag;
90     }
setCanceled(bool v)91     virtual void setCanceled(bool v) {
92         cancelFlag = v;
93     }
94 
getProgress()95     virtual int getProgress() const {
96         return progress;
97     }
setProgress(int v)98     virtual void setProgress(int v) {
99         progress = v;
100     }
101 
getDescription()102     virtual QString getDescription() const {
103         QReadLocker r(&lock);
104         return desc;
105     }
setDescription(const QString & _desc)106     virtual void setDescription(const QString &_desc) {
107         QWriteLocker w(&lock);
108         desc = _desc;
109     }
110 
hasWarnings()111     virtual bool hasWarnings() const {
112         QReadLocker r(&lock);
113         return !warnings.isEmpty();
114     }
getWarnings()115     virtual QStringList getWarnings() const {
116         QReadLocker r(&lock);
117         return warnings;
118     }
119     virtual void addWarning(const QString &warning);
120     virtual void addWarnings(const QStringList &wList);
121 
122     /* The same as addWarnings() but it does not write to log. Used by TaskScheduler. */
123     void insertWarnings(const QStringList &wList);
124 
125 private:
126     bool hasErr;
127     QString desc;
128     QString error;
129     QStringList warnings;
130 
131 private:
132     mutable QReadWriteLock lock;  // the lock is used because error & stateDesc can be assigned from any thread
133 };
134 
135 class U2CORE_EXPORT TaskTimeInfo {
136 public:
TaskTimeInfo()137     TaskTimeInfo()
138         : startTime(0), finishTime(0), timeOut(-1) {
139     }
140 
141     // time in microseconds from Unix Epoch (UTC). See Timer.h
142     qint64 startTime;  // the time task is promoted to 'running' state
143     qint64 finishTime;  // the time task is promoted to 'finished' state
144 
145     int timeOut;  // number of seconds to be passed before tasks is timed out, -1 -> timeout function is disabled
146 };
147 
148 #define MAX_PARALLEL_SUBTASKS_AUTO 0
149 #define MAX_PARALLEL_SUBTASKS_SERIAL 1
150 
151 enum TaskFlag {
152 
153     /** The task has default behavior: all prepare/run/report methods are called. */
154     TaskFlag_None = 0,
155 
156     /** No thread will be allocated for the task and no 'run' method will be called. */
157     TaskFlag_NoRun = 1 << 1,
158 
159     /** The 'run' method of the task can be safely run before all subtasks are finished. */
160     TaskFlag_RunBeforeSubtasksFinished = 1 << 2,
161 
162     /** Top level task flag: task is not deleted by scheduler after task is finished. */
163     TaskFlag_NoAutoDelete = 1 << 3,
164 
165     /**
166      * Th task will communicate with subtasks from its run() method in a separate thread.
167      * The thread allocated for such task is not considered as computational thread and does not count towards thread limits.
168      */
169     TaskFlag_RunMessageLoopOnly = 1 << 4,
170 
171     /**
172      * Run method for the task is called from the main thread.
173      * Usually the flag is used for tasks which access GUI state.
174      */
175     TaskFlag_RunInMainThread = 1 << 5,
176 
177     /**
178      * The task will be marked as failed if any of subtasks fails.
179      * The task will inherit error message from the failed subtask.
180      */
181     TaskFlag_FailOnSubtaskError = 1 << 10,
182 
183     /** The task will be marked as failed if any of subtasks is cancelled. */
184     TaskFlag_FailOnSubtaskCancel = 1 << 11,
185 
186     /** Task scheduler will update description of the task with a description of the last changed (progress/state) subtask. */
187     TaskFlag_PropagateSubtaskDesc = 1 << 12,
188 
189     /** The task will be marked as canceled if any of subtasks is cancelled. */
190     TaskFlag_CancelOnSubtaskCancel = 1 << 13,
191 
192     /** The task supports generateReport() to produce a detailed visual report when it is finished. */
193     TaskFlag_ReportingIsSupported = 1 << 20,
194 
195     /** For tasks that support reporting (see TaskFlag_ReportingIsSupported) the flag indicates that the reporting is enabled and generateReport() will be called. */
196     TaskFlag_ReportingIsEnabled = 1 << 21,
197 
198     /**
199      * The task will log prepared/finished state into the 'info' logs category. Effective for top-level tasks only.
200      * Used to make important system/user tasks more visible/locatable in log.
201      */
202     TaskFlag_VerboseStateLog = 1 << 22,
203 
204     /**
205      * For tasks with TaskFlag_FailOnSubtaskError flag minimizes the final error text by excluding sub-task-names from the error message.
206      * The flag applies this for the current task and all children recursively.
207      */
208     TaskFlag_MinimizeSubtaskErrorText = 1 << 23,
209 
210     /** Do not show error notification on task failure. Effective for top-level tasks only. */
211     TaskFlag_SuppressErrorNotification = 1 << 24,
212 
213     /** When the task is cancelled, the state is dumped to the 'info' log category. */
214     TaskFlag_VerboseOnTaskCancel = 1 << 25,
215 
216     /** When the task is finished a user notification will appear. */
217     TaskFlag_OnlyNotificationReport = 1 << 26,
218 
219     /**
220      * The task will collect all warnings from all its children in TaskStateInfo.warnings list.
221      * When set the flag applies recursively for all children.
222      */
223     TaskFlag_CollectChildrenWarnings = 1 << 27,
224 
225     /** The task will collect errors from all children and unite them into a single report. */
226     TaskFlag_ConcatenateChildrenErrors = 1 << 28,
227 
228     /**
229      * When set the global shutdown task will not ask user if to cancel the task or not.
230      * The flag should be used for background service tasks.
231      */
232     TaskFlag_SilentCancelOnShutdown = 1 << 29
233 };
234 
235 #define TaskFlags_FOSCOE (U2::TaskFlags(U2::TaskFlag_FailOnSubtaskError) | U2::TaskFlag_FailOnSubtaskCancel)
236 #define TaskFlags_NR_FOSCOE (TaskFlags_FOSCOE | U2::TaskFlag_NoRun)
237 #define TaskFlags_RBSF_FOSCOE (TaskFlags_FOSCOE | TaskFlag_RunBeforeSubtasksFinished)
238 
239 // TODO: use this new alternative to FOSCOE, more logical: fail on error, cancel on cancel
240 #define TaskFlags_FOSE_COSC (TaskFlags(TaskFlag_FailOnSubtaskError) | TaskFlag_CancelOnSubtaskCancel)
241 #define TaskFlags_NR_FOSE_COSC (TaskFlags_FOSE_COSC | TaskFlag_NoRun)
242 #define TaskFlags_RBSF_FOSE_COSC (TaskFlags_FOSE_COSC | TaskFlag_RunBeforeSubtasksFinished)
243 
244 Q_DECLARE_FLAGS(TaskFlags, TaskFlag)
245 typedef QVarLengthArray<TaskResourceUsage, 1> TaskResources;
246 
247 class U2CORE_EXPORT Task : public QObject {
248     Q_OBJECT
249     friend class TaskScheduler;
250 
251 public:
252     enum State {
253         State_New,
254         State_Prepared,
255         State_Running,
256         State_Finished
257     };
258 
259     enum ProgressManagement {
260         Progress_Manual,
261         Progress_SubTasksBased
262     };
263 
264     enum ReportResult {
265         ReportResult_Finished,
266         ReportResult_CallMeAgain
267     };
268 
269     // Creates new task with State_New state
270     Task(const QString &_name, TaskFlags f);
271 
272     // Prepares Task to run
273     // Task must request/prepare all resources it needs, create subtasks and define progress management type
274     // This method called after Task is added to Scheduler from the main thread
275     // After calling this method task gets State_Prepared state
prepare()276     virtual void prepare() {
277     }
278 
279     // Called by Scheduler from the separate thread. No updates to Project/Document model can be done from this method
280     // Task gets State_Running state when its first of its subtasks is run
run()281     virtual void run() {
282         assert(0);
283     }  // assertion is added to find all tasks with RUN declared in flags but not implemented
284 
285     // Called from the main thread after run() is finished
286     // Task must report all of it results if needed.
287     // If task can't report right now (for example a model is state-locked)
288     // task can return ReportResult_CallMeAgain.
289     // If task locks some resources, it's a good place to release them
290     // After task reporting succeeds, it gets State_Finished state
report()291     virtual ReportResult report() {
292         return ReportResult_Finished;
293     }
294 
isCanceled()295     bool isCanceled() const {
296         return stateInfo.cancelFlag;
297     }
298 
299     // Returns subtasks of the task. Task must prepare it's subtask on prepare() call and can't change them latter.
300     const QList<QPointer<Task>> &getSubtasks() const;
301     QList<Task *> getPureSubtasks() const;
302 
getTaskName()303     QString getTaskName() const {
304         return taskName;
305     }
306 
getState()307     State getState() const {
308         return state;
309     }
310 
getStateInfo()311     const TaskStateInfo &getStateInfo() const {
312         return stateInfo;
313     }
314 
getTimeInfo()315     const TaskTimeInfo &getTimeInfo() const {
316         return timeInfo;
317     }
318 
getProgress()319     int getProgress() const {
320         return stateInfo.progress;
321     }
322 
getProgressManagementType()323     ProgressManagement getProgressManagementType() const {
324         return tpm;
325     }
326 
getFlags()327     TaskFlags getFlags() const {
328         return flags;
329     }
330 
hasFlags(TaskFlags f)331     bool hasFlags(TaskFlags f) const {
332         return flags & f;
333     }
334 
335     void addSubTask(Task *sub);
336 
hasError()337     bool hasError() const {
338         return stateInfo.hasError();
339     }
340 
getError()341     QString getError() const {
342         return stateInfo.getError();
343     }
344 
hasWarning()345     virtual bool hasWarning() const {
346         return stateInfo.hasWarnings();
347     }
348 
getWarnings()349     virtual QStringList getWarnings() const {
350         return stateInfo.getWarnings();
351     }
352 
isFinished()353     bool isFinished() const {
354         return state == Task::State_Finished;
355     }
356 
isRunning()357     bool isRunning() const {
358         return state == Task::State_Running;
359     }
360 
isPrepared()361     bool isPrepared() const {
362         return state == Task::State_Prepared;
363     }
364 
isNew()365     bool isNew() const {
366         return state == Task::State_New;
367     }
368 
369     // When called for a finished task it must deallocate all resources it keeps.
370     // ATTENTION: this method WILL NOT be called by Task Scheduler automatically.
371     // It is guaranteed that only tests run by TestRunnerTask will be cleaned up.
372     // So, if any task provides 'cleanup' method, it still MUST correctly clean up
373     // its resources in destructor.
374     virtual void cleanup();
375 
hasSubtasksWithErrors()376     virtual bool hasSubtasksWithErrors() const {
377         return getSubtaskWithErrors() != nullptr;
378     }
379 
380     virtual bool propagateSubtaskError();
381 
382     virtual Task *getSubtaskWithErrors() const;
383 
getTaskId()384     virtual qint64 getTaskId() const {
385         return taskId;
386     }
387 
isTopLevelTask()388     virtual bool isTopLevelTask() const {
389         return getParentTask() == 0;
390     }
391 
getParentTask()392     virtual Task *getParentTask() const {
393         return parentTask;
394     }
395 
getTopLevelParentTask()396     virtual Task *getTopLevelParentTask() {
397         return isTopLevelTask() ? this : parentTask->getTopLevelParentTask();
398     }
399 
isReportingSupported()400     virtual bool isReportingSupported() const {
401         return flags.testFlag(TaskFlag_ReportingIsSupported);
402     }
403 
isReportingEnabled()404     virtual bool isReportingEnabled() const {
405         return flags.testFlag(TaskFlag_ReportingIsEnabled);
406     }
407 
isNotificationReport()408     virtual bool isNotificationReport() const {
409         return flags.testFlag(TaskFlag_OnlyNotificationReport);
410     }
411 
setReportingSupported(bool v)412     virtual void setReportingSupported(bool v) {
413         setFlag(TaskFlag_ReportingIsSupported, v);
414     }
415 
setReportingEnabled(bool v)416     virtual void setReportingEnabled(bool v) {
417         assert(isReportingSupported());
418         setFlag(TaskFlag_ReportingIsEnabled, v);
419     }
420 
setNotificationReport(bool v)421     virtual void setNotificationReport(bool v) {
422         assert(isReportingSupported());
423         setFlag(TaskFlag_ReportingIsEnabled, v);
424     }
425 
426     virtual void setCollectChildrensWarningsFlag(bool v);
427 
setNoAutoDelete(bool v)428     virtual void setNoAutoDelete(bool v) {
429         setFlag(TaskFlag_NoAutoDelete, v);
430     }
431 
generateReport()432     virtual QString generateReport() const {
433         assert(0);
434         return QString();
435     }
436 
getSubtaskProgressWeight()437     float getSubtaskProgressWeight() const {
438         return progressWeightAsSubtask;
439     }
440 
setSubtaskProgressWeight(float v)441     void setSubtaskProgressWeight(float v) {
442         progressWeightAsSubtask = v;
443     }
444 
useDescriptionFromSubtask()445     bool useDescriptionFromSubtask() const {
446         return flags.testFlag(TaskFlag_PropagateSubtaskDesc);
447     }
448 
setUseDescriptionFromSubtask(bool v)449     void setUseDescriptionFromSubtask(bool v) {
450         setFlag(TaskFlag_PropagateSubtaskDesc, v);
451     }
452 
isVerboseLogMode()453     bool isVerboseLogMode() const {
454         return flags.testFlag(TaskFlag_VerboseStateLog);
455     }
456 
setVerboseLogMode(bool v)457     void setVerboseLogMode(bool v) {
458         setFlag(TaskFlag_VerboseStateLog, v);
459     }
460 
isErrorNotificationSuppressed()461     bool isErrorNotificationSuppressed() const {
462         return flags.testFlag(TaskFlag_SuppressErrorNotification);
463     }
464 
setErrorNotificationSuppression(bool v)465     void setErrorNotificationSuppression(bool v) {
466         setFlag(TaskFlag_SuppressErrorNotification, v);
467     }
468 
isVerboseOnTaskCancel()469     bool isVerboseOnTaskCancel() const {
470         return flags.testFlag(TaskFlag_VerboseOnTaskCancel);
471     }
472 
setVerboseOnTaskCancel(bool v)473     void setVerboseOnTaskCancel(bool v) {
474         setFlag(TaskFlag_VerboseOnTaskCancel, v);
475     }
476 
isConcatenateChildrenErrors()477     bool isConcatenateChildrenErrors() const {
478         return flags.testFlag(TaskFlag_ConcatenateChildrenErrors);
479     }
480 
setConcatenateChildrenErrors(bool v)481     void setConcatenateChildrenErrors(bool v) {
482         setFlag(TaskFlag_ConcatenateChildrenErrors, v);
483     }
484 
getTaskResources()485     const TaskResources &getTaskResources() {
486         return taskResources;
487     }
488 
489     // WARN: if set to MAX_PARALLEL_SUBTASKS_AUTO, returns unprocessed value (MAX_PARALLEL_SUBTASKS_AUTO = 0)
getMaxParallelSubtasks()490     int getMaxParallelSubtasks() const {
491         return maxParallelSubtasks;
492     }
493 
494     // the difference from getMaxParralelSubtasks is that this method
495     // will process MAX_PARALLEL_SUBTASKS_AUTO and will never return 0
496     virtual int getNumParallelSubtasks() const;
497 
498     void setMaxParallelSubtasks(int n);
499 
setError(const QString & err)500     void setError(const QString &err) {
501         stateInfo.setError(err);
502     }
503 
504     void setMinimizeSubtaskErrorText(bool v);
505 
506     /** Number of seconds to be passed to mark task as failed by timeout */
setTimeOut(int sec)507     void setTimeOut(int sec) {
508         timeInfo.timeOut = sec;
509     }
510 
511     /** Number of seconds to be passed to mark task as failed by timeout */
getTimeOut()512     int getTimeOut() const {
513         return timeInfo.timeOut;
514     }
515 
516     void addTaskResource(const TaskResourceUsage &r);
517 
518     bool isMinimizeSubtaskErrorText() const;
519 
520 public slots:
521     // Set's cancelFlag to true. Does not wait for task to be stopped
522     void cancel();
523 
524 signals:
525     void si_subtaskAdded(Task *sub);
526 
527     void si_progressChanged();
528     void si_descriptionChanged();
529     void si_stateChanged();
530 
531 protected:
532     /// Called by scheduler when subtask is finished.
533     virtual QList<Task *> onSubTaskFinished(Task *subTask);
setRunResources(const TaskResources & taskR)534     void setRunResources(const TaskResources &taskR) {
535         assert(state <= State_Prepared);
536         taskResources = taskR;
537     }
538 
539     void setTaskName(const QString &taskName);
540 
setFlag(TaskFlag f,bool v)541     void setFlag(TaskFlag f, bool v) {
542         flags = v ? (flags | f) : flags & (~f);
543     }
544 
545     TaskStateInfo stateInfo;
546     TaskTimeInfo timeInfo;
547     ProgressManagement tpm;
548 
549     float progressWeightAsSubtask;
550     int maxParallelSubtasks;
551 
552 private:
553     TaskFlags flags;
554     QString taskName;
555     State state;
556     Task *parentTask;
557     QList<QPointer<Task>> subtasks;
558     qint64 taskId;
559     TaskResources taskResources;
560     bool insidePrepare;
561 };
562 
563 class U2CORE_EXPORT TaskScheduler : public QObject {
564     Q_OBJECT
565 public:
566     virtual void registerTopLevelTask(Task *t) = 0;
567 
568     virtual void unregisterTopLevelTask(Task *t) = 0;
569 
570     virtual const QList<Task *> &getTopLevelTasks() const = 0;
571 
572     virtual Task *getTopLevelTaskById(qint64 id) const = 0;
573 
574     virtual QDateTime estimatedFinishTime(Task *) const = 0;
575 
576     virtual void cancelAllTasks() = 0;
577 
578     virtual QString getStateName(Task *t) const = 0;
579 
580     virtual void addThreadId(qint64 taskId, Qt::HANDLE id) = 0;
581 
582     virtual void removeThreadId(qint64 taskId) = 0;
583 
584     virtual qint64 getNameByThreadId(Qt::HANDLE id) const = 0;
585 
586     virtual void pauseThreadWithTask(const Task *task) = 0;
587 
588     virtual void resumeThreadWithTask(const Task *task) = 0;
589 
590 signals:
591     void si_noTasksInScheduler();
592     void si_ugeneIsReadyToWork();
593 
594 protected:
getTaskResources(Task * t)595     TaskResources &getTaskResources(Task *t) {
596         return t->taskResources;
597     }
598 
getTaskStateInfo(Task * t)599     TaskStateInfo &getTaskStateInfo(Task *t) {
600         return t->stateInfo;
601     }
602 
getTaskTimeInfo(Task * t)603     TaskTimeInfo &getTaskTimeInfo(Task *t) {
604         return t->timeInfo;
605     }
606 
emit_taskProgressChanged(Task * t)607     void emit_taskProgressChanged(Task *t) {
608         emit t->si_progressChanged();
609     }
610 
emit_taskDescriptionChanged(Task * t)611     void emit_taskDescriptionChanged(Task *t) {
612         emit t->si_descriptionChanged();
613     }
614 
onSubTaskFinished(Task * parentTask,Task * subTask)615     QList<Task *> onSubTaskFinished(Task *parentTask, Task *subTask) {
616         return parentTask->onSubTaskFinished(subTask);
617     }
618 
619     void addSubTask(Task *t, Task *sub);
620 
621     void setTaskState(Task *t, Task::State newState);
622 
623     void setTaskStateDesc(Task *t, const QString &desc);
624 
625     void setTaskInsidePrepare(Task *t, bool val);
626 
627 signals:
628     void si_topLevelTaskRegistered(Task *);
629 
630     void si_topLevelTaskUnregistered(Task *);
631 
632     void si_stateChanged(Task *task);
633 };
634 
635 }  // namespace U2
636 
637 Q_DECLARE_OPERATORS_FOR_FLAGS(U2::TaskFlags)
638 
639 #endif
640