21 #include <QtConcurrentRun> 30 , mDescription( name )
36 Q_ASSERT_X( mStatus !=
Running,
"delete", QStringLiteral(
"status was %1" ).arg( mStatus ).toLatin1() );
37 mNotFinishedMutex.tryLock();
38 const auto constMSubTasks = mSubTasks;
39 for (
const SubTask &subTask : constMSubTasks )
43 mNotFinishedMutex.unlock();
48 return mElapsedTime.elapsed();
53 mNotFinishedMutex.lock();
55 Q_ASSERT( mStartCount == 1 );
85 mShouldTerminate =
true;
94 processSubTasksForTermination();
97 const auto constMSubTasks = mSubTasks;
98 for (
const SubTask &subTask : constMSubTasks )
100 subTask.task->cancel();
109 processSubTasksForHold();
112 const auto constMSubTasks = mSubTasks;
113 for (
const SubTask &subTask : constMSubTasks )
115 subTask.task->hold();
128 const auto constMSubTasks = mSubTasks;
129 for (
const SubTask &subTask : constMSubTasks )
131 subTask.task->unhold();
138 mSubTasks << SubTask( subTask, dependencies, subTaskDependency );
145 return _qgis_listQPointerToRaw( mDependentLayers );
158 timeout = std::numeric_limits< int >::max();
159 if ( mNotFinishedMutex.tryLock( timeout ) )
161 mNotFinishedMutex.unlock();
174 mDependentLayers = _qgis_listRawToQPointer( dependentLayers );
177 void QgsTask::subTaskStatusChanged(
int status )
190 processSubTasksForCompletion();
195 processSubTasksForTermination();
199 processSubTasksForHold();
212 if ( !mSubTasks.isEmpty() )
216 double totalProgress = 0.0;
217 const auto constMSubTasks = mSubTasks;
218 for (
const SubTask &subTask : constMSubTasks )
222 totalProgress += 100.0;
226 totalProgress += subTask.task->progress();
229 progress = ( progress + totalProgress ) / ( mSubTasks.count() + 1 );
233 double prevProgress = mTotalProgress;
237 if ( static_cast< int >( prevProgress * 10 ) !=
static_cast< int >( mTotalProgress * 10 ) )
241 void QgsTask::completed()
244 processSubTasksForCompletion();
247 void QgsTask::processSubTasksForCompletion()
249 bool subTasksCompleted =
true;
250 const auto constMSubTasks = mSubTasks;
251 for (
const SubTask &subTask : constMSubTasks )
253 if ( subTask.task->status() !=
Complete )
255 subTasksCompleted =
false;
260 if ( mStatus ==
Complete && subTasksCompleted )
267 mNotFinishedMutex.tryLock();
268 mNotFinishedMutex.unlock();
277 void QgsTask::processSubTasksForTermination()
279 bool subTasksTerminated =
true;
280 const auto constMSubTasks = mSubTasks;
281 for (
const SubTask &subTask : constMSubTasks )
285 subTasksTerminated =
false;
296 mNotFinishedMutex.tryLock();
297 mNotFinishedMutex.unlock();
299 else if ( mStatus ==
Terminated && !subTasksTerminated )
306 void QgsTask::processSubTasksForHold()
308 bool subTasksRunning =
false;
309 const auto constMSubTasks = mSubTasks;
310 for (
const SubTask &subTask : constMSubTasks )
312 if ( subTask.task->status() ==
Running )
314 subTasksRunning =
true;
319 if ( mStatus ==
OnHold && !subTasksRunning && mOverallStatus !=
OnHold )
324 else if ( mStatus ==
OnHold && subTasksRunning )
331 void QgsTask::terminated()
334 processSubTasksForTermination();
347 setAutoDelete(
true );
372 , mTaskMutex( new QMutex( QMutex::Recursive ) )
375 this, &QgsTaskManager::layersWillBeRemoved );
385 QMap< long, TaskInfo >
tasks = mTasks;
387 mTaskMutex->unlock();
388 QMap< long, TaskInfo >::const_iterator it = tasks.constBegin();
389 for ( ; it != tasks.constEnd(); ++it )
391 cleanupAndDeleteTask( it.value().task );
399 return addTaskPrivate( task,
QgsTaskList(),
false, priority );
404 return addTaskPrivate( definition.
task,
416 long taskId = mNextTaskId++;
419 mTasks.insert( taskId, TaskInfo( task, priority ) );
426 mParentTasks <<
task;
429 mLayerDependencies.insert( taskId, _qgis_listRawToQPointer( task->
dependentLayers() ) );
430 mTaskMutex->unlock();
439 for (
const QgsTask::SubTask &subTask : qgis::as_const( task->mSubTasks ) )
441 switch ( subTask.dependency )
452 addTaskPrivate( subTask.task, subTask.dependencies,
true, priority );
460 if ( hasCircularDependencies( taskId ) )
476 QMutexLocker ml( mTaskMutex );
478 if ( mTasks.contains(
id ) )
479 t = mTasks.value(
id ).task;
485 QMutexLocker ml( mTaskMutex );
486 return mParentTasks.toList();
491 QMutexLocker ml( mTaskMutex );
492 return mParentTasks.count();
500 QMutexLocker ml( mTaskMutex );
501 QMap< long, TaskInfo >::const_iterator it = mTasks.constBegin();
502 for ( ; it != mTasks.constEnd(); ++it )
504 if ( it.value().task ==
task )
515 QSet< QgsTask * > parents = mParentTasks;
517 mTaskMutex->unlock();
519 const auto constParents = parents;
520 for (
QgsTask *task : constParents )
529 QMap< long, QgsTaskList >
dependencies = mTaskDependencies;
530 dependencies.detach();
531 mTaskMutex->unlock();
533 if ( !dependencies.contains( taskId ) )
536 const auto constValue = dependencies.value( taskId );
537 for (
QgsTask *task : constValue )
549 if ( resolveDependencies( taskId, taskId, results ) )
555 bool QgsTaskManager::resolveDependencies(
long firstTaskId,
long currentTaskId, QSet<long> &results )
const 558 QMap< long, QgsTaskList >
dependencies = mTaskDependencies;
559 dependencies.detach();
560 mTaskMutex->unlock();
562 if ( !dependencies.contains( currentTaskId ) )
565 const auto constValue = dependencies.value( currentTaskId );
566 for (
QgsTask *task : constValue )
568 long dependentTaskId =
taskId( task );
569 if ( dependentTaskId >= 0 )
571 if ( dependentTaskId == firstTaskId )
576 results.insert( dependentTaskId );
578 QSet< long > newTaskDeps;
579 if ( !resolveDependencies( firstTaskId, dependentTaskId, newTaskDeps ) )
582 if ( newTaskDeps.contains( firstTaskId ) )
588 results.unite( newTaskDeps );
595 bool QgsTaskManager::hasCircularDependencies(
long taskId )
const 598 return !resolveDependencies( taskId, taskId, d );
603 QMutexLocker ml( mTaskMutex );
609 QMutexLocker ml( mTaskMutex );
610 QList< QgsTask * >
tasks;
611 QMap< long, QgsWeakMapLayerPointerList >::const_iterator layerIt = mLayerDependencies.constBegin();
612 for ( ; layerIt != mLayerDependencies.constEnd(); ++layerIt )
614 if ( _qgis_listQPointerToRaw( layerIt.value() ).contains( layer ) )
626 QMutexLocker ml( mTaskMutex );
628 activeTasks.intersect( mParentTasks );
629 return activeTasks.toList();
634 QMutexLocker ml( mTaskMutex );
635 QSet< QgsTask * >
tasks = mActiveTasks;
636 return tasks.intersect( mParentTasks ).count();
645 void QgsTaskManager::taskProgressChanged(
double progress )
662 void QgsTaskManager::taskStatusChanged(
int status )
672 QgsTaskRunnableWrapper *runnable = mTasks.value(
id ).runnable;
673 mTaskMutex->unlock();
675 QThreadPool::globalInstance()->cancel( runnable );
686 cancelDependentTasks(
id );
690 bool isParent = mParentTasks.contains( task );
691 mTaskMutex->unlock();
702 cleanupAndDeleteTask( task );
707 void QgsTaskManager::layersWillBeRemoved(
const QList< QgsMapLayer * > &layers )
711 QMap< long, QgsWeakMapLayerPointerList > layerDependencies = mLayerDependencies;
712 layerDependencies.detach();
713 mTaskMutex->unlock();
715 const auto constLayers = layers;
719 for ( QMap< long, QgsWeakMapLayerPointerList >::const_iterator it = layerDependencies.constBegin();
720 it != layerDependencies.constEnd(); ++it )
722 if ( !( _qgis_listQPointerToRaw( it.value() ).contains( layer ) ) )
739 bool QgsTaskManager::cleanupAndDeleteTask(
QgsTask *task )
748 QgsTaskRunnableWrapper *runnable = mTasks.value(
id ).runnable;
750 task->disconnect(
this );
753 if ( mTaskDependencies.contains(
id ) )
754 mTaskDependencies.remove(
id );
755 mTaskMutex->unlock();
760 bool isParent = mParentTasks.contains( task );
761 mParentTasks.remove( task );
762 mSubTasks.remove( task );
764 mLayerDependencies.remove(
id );
779 QThreadPool::globalInstance()->cancel( runnable );
788 for ( QMap< long, QgsTaskList >::iterator it = mTaskDependencies.begin(); it != mTaskDependencies.end(); ++it )
790 if ( it.value().contains( task ) )
792 it.value().removeAll( task );
795 mTaskMutex->unlock();
800 void QgsTaskManager::processQueue()
804 mActiveTasks.clear();
805 for ( QMap< long, TaskInfo >::iterator it = mTasks.begin(); it != mTasks.end(); ++it )
807 QgsTask *task = it.value().task;
810 it.value().createRunnable();
811 QThreadPool::globalInstance()->start( it.value().runnable, it.value().priority );
816 mActiveTasks <<
task;
820 bool allFinished = mActiveTasks.isEmpty();
821 mTaskMutex->unlock();
829 if ( prevActiveCount != newActiveCount )
835 void QgsTaskManager::cancelDependentTasks(
long taskId )
841 QMap< long, QgsTaskList > taskDependencies = mTaskDependencies;
842 taskDependencies.detach();
843 mTaskMutex->unlock();
845 QMap< long, QgsTaskList >::const_iterator it = taskDependencies.constBegin();
846 for ( ; it != taskDependencies.constEnd(); ++it )
848 if ( it.value().contains( canceledTask ) )
861 QgsTaskManager::TaskInfo::TaskInfo(
QgsTask *task,
int priority )
864 , priority( priority )
867 void QgsTaskManager::TaskInfo::createRunnable()
869 Q_ASSERT( !runnable );
870 runnable =
new QgsTaskRunnableWrapper( task );
bool dependenciesSatisfied(long taskId) const
Returns true if all dependencies for the specified task are satisfied.
int countActiveTasks() const
Returns the number of active (queued or running) tasks.
void taskTerminated()
Will be emitted by task if it has terminated for any reason other then completion (e...
void setProgress(double progress)
Sets the task's current progress.
Base class for all map layer types.
void hold()
Places the task on hold.
void taskCompleted()
Will be emitted by task to indicate its successful completion.
void taskTriggered(QgsTask *task)
Emitted when a task is triggered.
QList< QgsMapLayer *> dependentLayers() const
Returns the list of layers on which the task depends.
QgsTask(const QString &description=QString(), QgsTask::Flags flags=AllFlags)
Constructor for QgsTask.
void statusChanged(int status)
Will be emitted by task when its status changes.
QList< QgsTask *> QgsTaskList
List of QgsTask objects.
int count() const
Returns the number of tasks tracked by the manager.
Subtask must complete before parent can begin.
qint64 elapsedTime() const
Returns the elapsed time since the task commenced, in milliseconds.
QgsTaskManager(QObject *parent=nullptr)
Constructor for QgsTaskManager.
void setDependentLayers(const QList< QgsMapLayer *> &dependentLayers)
Sets a list of layers on which the task depends.
bool waitForFinished(int timeout=30000)
Blocks the current thread until the task finishes or a maximum of timeout milliseconds.
QList< QgsTask * > tasks() const
Returns all tasks tracked by the manager.
void begun()
Will be emitted by task to indicate its commencement.
SubTaskDependency
Controls how subtasks relate to their parent task.
void cancelAll()
Instructs all tasks tracked by the manager to terminate.
QgsTaskList dependentTasks
List of dependent tasks which must be completed before task can run.
void progressChanged(double progress)
Will be emitted by task when its progress changes.
QSet< long > dependencies(long taskId) const
Returns the set of task IDs on which a task is dependent.
void taskAboutToBeDeleted(long taskId)
Emitted when a task is about to be deleted.
Task was terminated or errored.
void countActiveTasksChanged(int count)
Emitted when the number of active tasks changes.
void triggerTask(QgsTask *task)
Triggers a task, e.g.
QList< QgsTask *> tasksDependentOnLayer(QgsMapLayer *layer) const
Returns a list of tasks which depend on a layer.
QgsTask * task(long id) const
Returns the task with matching ID.
void progressChanged(long taskId, double progress)
Will be emitted when a task reports a progress change.
Definition of a task for inclusion in the manager.
Task is queued but on hold and will not be started.
long addTask(QgsTask *task, int priority=0)
Adds a task to the manager.
Abstract base class for long running background tasks.
Reads and writes project states.
friend class QgsTaskRunnableWrapper
void statusChanged(long taskId, int status)
Will be emitted when a task reports a status change.
Task successfully completed.
void taskAdded(long taskId)
Emitted when a new task has been added to the manager.
virtual void cancel()
Notifies the task that it should terminate.
void allTasksFinished()
Emitted when all tasks are complete.
Task is queued and has not begun.
double progress() const
Returns the task's progress (between 0.0 and 100.0)
void layersWillBeRemoved(const QStringList &layerIds)
Emitted when one or more layers are about to be removed from the registry.
QList< QgsTask *> activeTasks() const
Returns a list of the active (queued or running) tasks.
Task is currently running.
~QgsTaskManager() override
QList< QgsWeakMapLayerPointer > QgsWeakMapLayerPointerList
A list of weak pointers to QgsMapLayers.
static QgsProject * instance()
Returns the QgsProject singleton instance.
long taskId(QgsTask *task) const
Returns the unique task ID corresponding to a task managed by the class.
virtual void finished(bool result)
If the task is managed by a QgsTaskManager, this will be called after the task has finished (whether ...
virtual bool run()=0
Performs the task's operation.
void finalTaskProgressChanged(double progress)
Will be emitted when only a single task remains to complete and that task has reported a progress cha...
Subtask is independent of the parent, and can run before, after or at the same time as the parent...
void addSubTask(QgsTask *subTask, const QgsTaskList &dependencies=QgsTaskList(), SubTaskDependency subTaskDependency=SubTaskIndependent)
Adds a subtask to this task.
void unhold()
Releases the task from being held.
TaskStatus status() const
Returns the current task status.
QList< QgsMapLayer *> dependentLayers(long taskId) const
Returns a list of layers on which as task is dependent.