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