21 #include <QtConcurrentRun> 30 , mDescription( name )
31 , mNotStartedMutex( 1 )
33 mNotStartedMutex.acquire();
38 Q_ASSERT_X( mStatus !=
Running,
"delete", QStringLiteral(
"status was %1" ).arg( mStatus ).toLatin1() );
39 mNotFinishedMutex.tryLock();
40 const auto constMSubTasks = mSubTasks;
41 for (
const SubTask &subTask : constMSubTasks )
45 mNotFinishedMutex.unlock();
46 mNotStartedMutex.release();
56 return mElapsedTime.elapsed();
61 mNotFinishedMutex.lock();
62 mNotStartedMutex.release();
64 Q_ASSERT( mStartCount == 1 );
94 mShouldTerminate =
true;
99 mNotStartedMutex.release();
104 processSubTasksForTermination();
107 const auto constMSubTasks = mSubTasks;
108 for (
const SubTask &subTask : constMSubTasks )
110 subTask.task->cancel();
119 processSubTasksForHold();
122 const auto constMSubTasks = mSubTasks;
123 for (
const SubTask &subTask : constMSubTasks )
125 subTask.task->hold();
138 const auto constMSubTasks = mSubTasks;
139 for (
const SubTask &subTask : constMSubTasks )
141 subTask.task->unhold();
148 mSubTasks << SubTask( subTask, dependencies, subTaskDependency );
155 return _qgis_listQPointerToRaw( mDependentLayers );
161 mNotStartedMutex.acquire();
171 timeout = std::numeric_limits< int >::max();
172 if ( mNotFinishedMutex.tryLock( timeout ) )
174 mNotFinishedMutex.unlock();
187 mDependentLayers = _qgis_listRawToQPointer( dependentLayers );
190 void QgsTask::subTaskStatusChanged(
int status )
203 processSubTasksForCompletion();
208 processSubTasksForTermination();
212 processSubTasksForHold();
225 if ( !mSubTasks.isEmpty() )
229 double totalProgress = 0.0;
230 const auto constMSubTasks = mSubTasks;
231 for (
const SubTask &subTask : constMSubTasks )
235 totalProgress += 100.0;
239 totalProgress += subTask.task->progress();
242 progress = ( progress + totalProgress ) / ( mSubTasks.count() + 1 );
246 double prevProgress = mTotalProgress;
250 if ( static_cast< int >( prevProgress * 10 ) !=
static_cast< int >( mTotalProgress * 10 ) )
254 void QgsTask::completed()
257 processSubTasksForCompletion();
260 void QgsTask::processSubTasksForCompletion()
262 bool subTasksCompleted =
true;
263 const auto constMSubTasks = mSubTasks;
264 for (
const SubTask &subTask : constMSubTasks )
266 if ( subTask.task->status() !=
Complete )
268 subTasksCompleted =
false;
273 if ( mStatus ==
Complete && subTasksCompleted )
280 mNotFinishedMutex.tryLock();
281 mNotFinishedMutex.unlock();
290 void QgsTask::processSubTasksForTermination()
292 bool subTasksTerminated =
true;
293 const auto constMSubTasks = mSubTasks;
294 for (
const SubTask &subTask : constMSubTasks )
298 subTasksTerminated =
false;
309 mNotFinishedMutex.tryLock();
310 mNotFinishedMutex.unlock();
312 else if ( mStatus ==
Terminated && !subTasksTerminated )
319 void QgsTask::processSubTasksForHold()
321 bool subTasksRunning =
false;
322 const auto constMSubTasks = mSubTasks;
323 for (
const SubTask &subTask : constMSubTasks )
325 if ( subTask.task->status() ==
Running )
327 subTasksRunning =
true;
332 if ( mStatus ==
OnHold && !subTasksRunning && mOverallStatus !=
OnHold )
337 else if ( mStatus ==
OnHold && subTasksRunning )
344 void QgsTask::terminated()
347 processSubTasksForTermination();
360 setAutoDelete(
true );
385 , mTaskMutex( new QMutex( QMutex::Recursive ) )
388 this, &QgsTaskManager::layersWillBeRemoved );
398 QMap< long, TaskInfo >
tasks = mTasks;
400 mTaskMutex->unlock();
401 QMap< long, TaskInfo >::const_iterator it = tasks.constBegin();
402 for ( ; it != tasks.constEnd(); ++it )
404 cleanupAndDeleteTask( it.value().task );
412 return addTaskPrivate( task,
QgsTaskList(),
false, priority );
417 return addTaskPrivate( definition.
task,
429 long taskId = mNextTaskId++;
432 mTasks.insert( taskId, TaskInfo( task, priority ) );
439 mParentTasks <<
task;
442 mLayerDependencies.insert( taskId, _qgis_listRawToQPointer( task->
dependentLayers() ) );
443 mTaskMutex->unlock();
452 for (
const QgsTask::SubTask &subTask : qgis::as_const( task->mSubTasks ) )
454 switch ( subTask.dependency )
465 addTaskPrivate( subTask.task, subTask.dependencies,
true, priority );
473 if ( hasCircularDependencies( taskId ) )
489 QMutexLocker ml( mTaskMutex );
491 if ( mTasks.contains(
id ) )
492 t = mTasks.value(
id ).task;
498 QMutexLocker ml( mTaskMutex );
499 return mParentTasks.toList();
504 QMutexLocker ml( mTaskMutex );
505 return mParentTasks.count();
513 QMutexLocker ml( mTaskMutex );
514 QMap< long, TaskInfo >::const_iterator it = mTasks.constBegin();
515 for ( ; it != mTasks.constEnd(); ++it )
517 if ( it.value().task ==
task )
528 QSet< QgsTask * > parents = mParentTasks;
530 mTaskMutex->unlock();
532 const auto constParents = parents;
533 for (
QgsTask *task : constParents )
542 QMap< long, QgsTaskList >
dependencies = mTaskDependencies;
543 dependencies.detach();
544 mTaskMutex->unlock();
546 if ( !dependencies.contains( taskId ) )
549 const auto constValue = dependencies.value( taskId );
550 for (
QgsTask *task : constValue )
562 if ( resolveDependencies( taskId, taskId, results ) )
568 bool QgsTaskManager::resolveDependencies(
long firstTaskId,
long currentTaskId, QSet<long> &results )
const 571 QMap< long, QgsTaskList >
dependencies = mTaskDependencies;
572 dependencies.detach();
573 mTaskMutex->unlock();
575 if ( !dependencies.contains( currentTaskId ) )
578 const auto constValue = dependencies.value( currentTaskId );
579 for (
QgsTask *task : constValue )
581 long dependentTaskId =
taskId( task );
582 if ( dependentTaskId >= 0 )
584 if ( dependentTaskId == firstTaskId )
589 results.insert( dependentTaskId );
591 QSet< long > newTaskDeps;
592 if ( !resolveDependencies( firstTaskId, dependentTaskId, newTaskDeps ) )
595 if ( newTaskDeps.contains( firstTaskId ) )
601 results.unite( newTaskDeps );
608 bool QgsTaskManager::hasCircularDependencies(
long taskId )
const 611 return !resolveDependencies( taskId, taskId, d );
616 QMutexLocker ml( mTaskMutex );
622 QMutexLocker ml( mTaskMutex );
623 QList< QgsTask * >
tasks;
624 QMap< long, QgsWeakMapLayerPointerList >::const_iterator layerIt = mLayerDependencies.constBegin();
625 for ( ; layerIt != mLayerDependencies.constEnd(); ++layerIt )
627 if ( _qgis_listQPointerToRaw( layerIt.value() ).contains( layer ) )
639 QMutexLocker ml( mTaskMutex );
641 activeTasks.intersect( mParentTasks );
642 return activeTasks.toList();
647 QMutexLocker ml( mTaskMutex );
648 QSet< QgsTask * >
tasks = mActiveTasks;
649 return tasks.intersect( mParentTasks ).count();
658 void QgsTaskManager::taskProgressChanged(
double progress )
675 void QgsTaskManager::taskStatusChanged(
int status )
685 QgsTaskRunnableWrapper *runnable = mTasks.value(
id ).runnable;
686 mTaskMutex->unlock();
687 if ( runnable && QThreadPool::globalInstance()->tryTake( runnable ) )
690 mTasks[ id ].runnable =
nullptr;
702 cancelDependentTasks(
id );
706 bool isParent = mParentTasks.contains( task );
707 mTaskMutex->unlock();
718 cleanupAndDeleteTask( task );
723 void QgsTaskManager::layersWillBeRemoved(
const QList< QgsMapLayer * > &layers )
727 QMap< long, QgsWeakMapLayerPointerList > layerDependencies = mLayerDependencies;
728 layerDependencies.detach();
729 mTaskMutex->unlock();
731 const auto constLayers = layers;
735 for ( QMap< long, QgsWeakMapLayerPointerList >::const_iterator it = layerDependencies.constBegin();
736 it != layerDependencies.constEnd(); ++it )
738 if ( !( _qgis_listQPointerToRaw( it.value() ).contains( layer ) ) )
755 bool QgsTaskManager::cleanupAndDeleteTask(
QgsTask *task )
764 QgsTaskRunnableWrapper *runnable = mTasks.value(
id ).runnable;
766 task->disconnect(
this );
769 if ( mTaskDependencies.contains(
id ) )
770 mTaskDependencies.remove(
id );
771 mTaskMutex->unlock();
776 bool isParent = mParentTasks.contains( task );
777 mParentTasks.remove( task );
778 mSubTasks.remove( task );
780 mLayerDependencies.remove(
id );
794 if ( runnable && QThreadPool::globalInstance()->tryTake( runnable ) )
797 mTasks[ id ].runnable =
nullptr;
808 for ( QMap< long, QgsTaskList >::iterator it = mTaskDependencies.begin(); it != mTaskDependencies.end(); ++it )
810 if ( it.value().contains( task ) )
812 it.value().removeAll( task );
815 mTaskMutex->unlock();
820 void QgsTaskManager::processQueue()
824 mActiveTasks.clear();
825 for ( QMap< long, TaskInfo >::iterator it = mTasks.begin(); it != mTasks.end(); ++it )
827 QgsTask *task = it.value().task;
830 it.value().createRunnable();
831 QThreadPool::globalInstance()->start( it.value().runnable, it.value().priority );
836 mActiveTasks <<
task;
840 bool allFinished = mActiveTasks.isEmpty();
841 mTaskMutex->unlock();
849 if ( prevActiveCount != newActiveCount )
855 void QgsTaskManager::cancelDependentTasks(
long taskId )
861 QMap< long, QgsTaskList > taskDependencies = mTaskDependencies;
862 taskDependencies.detach();
863 mTaskMutex->unlock();
865 QMap< long, QgsTaskList >::const_iterator it = taskDependencies.constBegin();
866 for ( ; it != taskDependencies.constEnd(); ++it )
868 if ( it.value().contains( canceledTask ) )
881 QgsTaskManager::TaskInfo::TaskInfo(
QgsTask *task,
int priority )
884 , priority( priority )
887 void QgsTaskManager::TaskInfo::createRunnable()
889 Q_ASSERT( !runnable );
890 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 setDescription(const QString &description)
Sets the task's description.
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.
QString description() const
Returns the task's description.
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.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts, annotations, canvases, etc.
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.