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_SCHEDULER_IMPL_H_
23 #define _U2_TASK_SCHEDULER_IMPL_H_
24 
25 #include <QMap>
26 #include <QMutex>
27 #include <QThread>
28 #include <QTimer>
29 #include <QWaitCondition>
30 
31 #include <U2Core/Task.h>
32 #include <U2Core/global.h>
33 
34 namespace U2 {
35 
36 class TaskInfo;
37 class AppResourcePool;
38 class AppResource;
39 
40 /**
41  * Some systems can go to hibernation during UGENE work.
42  * This class prevents this behavior if top-level tasks exist.
43  */
44 class SleepPreventer {
45 public:
~SleepPreventer()46     virtual ~SleepPreventer() {
47     }
capture()48     virtual void capture() {
49     }
release()50     virtual void release() {
51     }
52 };
53 
54 class TaskThread : public QThread {
55     Q_OBJECT
56 public:
57     TaskThread(TaskInfo *_ti);
58     void run();
59     void resume();
60     QList<Task *> getProcessedSubtasks() const;
61     void appendProcessedSubtask(Task *);
62 
63     TaskInfo *ti;
64     QObject *finishEventListener;
65     QMutex subtasksLocker;
66     QList<Task *> unconsideredNewSubtasks;
67     volatile bool newSubtasksObtained;
68 
69     QWaitCondition pauser;
70     volatile bool isPaused;
71     QMutex pauseLocker;
72 signals:
73     void si_processMySubtasks();
74 
75 protected:
76     bool event(QEvent *event);
77 
78 private:
79     void getNewSubtasks();
80     void terminateMessageLoop();
81     void pause();
82 
83     QList<Task *> processedSubtasks;
84 };
85 
86 class TaskInfo {
87 public:
TaskInfo(Task * t,TaskInfo * p)88     TaskInfo(Task *t, TaskInfo *p)
89         : task(t), parentTaskInfo(p), wasPrepared(false), subtasksWereCanceled(false), selfRunFinished(false),
90           hasLockedPrepareResources(false), hasLockedRunResources(false),
91           prevProgress(0), numPreparedSubtasks(0), numRunningSubtasks(0), numFinishedSubtasks(0), thread(nullptr) {
92     }
93 
94     virtual ~TaskInfo();
95 
96     // true if task state >= RUN && thread is finished or not used at all
97 
98     Task *task;
99     TaskInfo *parentTaskInfo;
100     QList<Task *> newSubtasks;
101 
102     bool wasPrepared;  // 'true' if prepare() was called for the task
103     bool subtasksWereCanceled;  // 'true' if canceled task has called cancel() on its subtasks
104     bool selfRunFinished;  // indicates that the 'run' method of this task was finished
105     bool hasLockedPrepareResources;  // true if there were resource locks for 'prepare' stage
106     bool hasLockedRunResources;  // true if there were resource locks for 'run' stage
107 
108     int prevProgress;  // used for TaskProgress_Manual
109     QString prevDesc;
110 
111     int numPreparedSubtasks;
112     int numRunningSubtasks;
113     int numFinishedSubtasks;
114 
115     TaskThread *thread;
116 
numActiveSubtasks()117     inline int numActiveSubtasks() const {
118         return numPreparedSubtasks + numRunningSubtasks;
119     }
120 };
121 
122 class U2PRIVATE_EXPORT TaskSchedulerImpl : public TaskScheduler {
123     Q_OBJECT
124 public:
125     using TaskScheduler::onSubTaskFinished;
126 
127     TaskSchedulerImpl(AppResourcePool *rp);
128     ~TaskSchedulerImpl();
129 
130     virtual void registerTopLevelTask(Task *t);
131 
132     virtual void unregisterTopLevelTask(Task *t);
133 
getTopLevelTasks()134     const QList<Task *> &getTopLevelTasks() const {
135         return topLevelTasks;
136     }
137 
138     Task *getTopLevelTaskById(qint64 id) const;
139 
140     QDateTime estimatedFinishTime(Task *) const;
141 
142     virtual void cancelTask(Task *t);
143 
144     virtual void cancelAllTasks();
145 
146     virtual QString getStateName(Task *t) const;
147 
addThreadId(qint64 taskId,Qt::HANDLE id)148     void addThreadId(qint64 taskId, Qt::HANDLE id) { /*threadIds.insert(taskId, id);*/
149         threadIds[taskId] = id;
150     }
removeThreadId(qint64 taskId)151     void removeThreadId(qint64 taskId) {
152         threadIds.remove(taskId);
153     }
getNameByThreadId(Qt::HANDLE id)154     qint64 getNameByThreadId(Qt::HANDLE id) const {
155         return threadIds.key(id);
156     }
157     void pauseThreadWithTask(const Task *task);
158     void resumeThreadWithTask(const Task *task);
159     void onSubTaskFinished(TaskThread *thread, Task *subtask);
160 
161 private slots:
162     void update();
163     void sl_threadFinished();
164     void sl_processSubtasks();
165 
166 private:
167     bool processFinishedTasks();
168     void unregisterFinishedTopLevelTasks();
169     void processNewSubtasks();
170     void prepareNewTasks();
171     void runReady();
172 
173     bool readyToFinish(TaskInfo *ti);
174     bool addToPriorityQueue(Task *t, TaskInfo *parentInfo);  // return true if added. Failure can be caused if a task requires resources
175     void runThread(TaskInfo *pi);
176     void stopTask(Task *t);
177     void updateTaskProgressAndDesc(TaskInfo *ti);
178     void promoteTask(TaskInfo *ti, Task::State newState);
179     void deleteTask(Task *t);
180     void finishSubtasks(TaskInfo *pti);
181 
182     QString tryLockResources(Task *task, bool prepareStage, bool &hasLockedResourcesAfterCall);  // returns error message
183     void releaseResources(TaskInfo *ti, bool prepareStage);
184 
185     void propagateStateToParent(Task *t);
186     void updateOldTasksPriority();
187     void checkSerialPromotion(TaskInfo *pti, Task *subtask);
188     void createSleepPreventer();
189 
190 private:
191     QTimer timer;
192     QList<Task *> topLevelTasks;
193     QList<TaskInfo *> priorityQueue;
194     QList<TaskInfo *> tasksWithNewSubtasks;
195     QList<Task *> newTasks;
196     QList<Task *> loadingTasks;
197     QStringList stateNames;
198     QMap<quint64, Qt::HANDLE> threadIds;
199 
200     AppResourcePool *resourcePool;
201     AppResource *threadsResource;
202     bool stateChangesObserved;
203     bool stateIsLoaded;
204     SleepPreventer *sleepPreventer;
205 };
206 
207 }  // namespace U2
208 #endif
209