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();
53 return mElapsedTime.elapsed();
58 mNotFinishedMutex.lock();
60 Q_ASSERT( mStartCount == 1 );
90 mShouldTerminate =
true;
99 processSubTasksForTermination();
102 const auto constMSubTasks = mSubTasks;
103 for (
const SubTask &subTask : constMSubTasks )
105 subTask.task->cancel();
114 processSubTasksForHold();
117 const auto constMSubTasks = mSubTasks;
118 for (
const SubTask &subTask : constMSubTasks )
120 subTask.task->hold();
133 const auto constMSubTasks = mSubTasks;
134 for (
const SubTask &subTask : constMSubTasks )
136 subTask.task->unhold();
143 mSubTasks << SubTask( subTask, dependencies, subTaskDependency );
150 return _qgis_listQPointerToRaw( mDependentLayers );
163 timeout = std::numeric_limits< int >::max();
164 if ( mNotFinishedMutex.tryLock( timeout ) )
166 mNotFinishedMutex.unlock();
179 mDependentLayers = _qgis_listRawToQPointer( dependentLayers );
182 void QgsTask::subTaskStatusChanged(
int status )
195 processSubTasksForCompletion();
200 processSubTasksForTermination();
204 processSubTasksForHold();
217 if ( !mSubTasks.isEmpty() )
221 double totalProgress = 0.0;
222 const auto constMSubTasks = mSubTasks;
223 for (
const SubTask &subTask : constMSubTasks )
227 totalProgress += 100.0;
231 totalProgress += subTask.task->progress();
234 progress = ( progress + totalProgress ) / ( mSubTasks.count() + 1 );
238 double prevProgress = mTotalProgress;
242 if ( static_cast< int >( prevProgress * 10 ) !=
static_cast< int >( mTotalProgress * 10 ) )
246 void QgsTask::completed()
249 processSubTasksForCompletion();
252 void QgsTask::processSubTasksForCompletion()
254 bool subTasksCompleted =
true;
255 const auto constMSubTasks = mSubTasks;
256 for (
const SubTask &subTask : constMSubTasks )
258 if ( subTask.task->status() !=
Complete )
260 subTasksCompleted =
false;
265 if ( mStatus ==
Complete && subTasksCompleted )
272 mNotFinishedMutex.tryLock();
273 mNotFinishedMutex.unlock();
282 void QgsTask::processSubTasksForTermination()
284 bool subTasksTerminated =
true;
285 const auto constMSubTasks = mSubTasks;
286 for (
const SubTask &subTask : constMSubTasks )
290 subTasksTerminated =
false;
301 mNotFinishedMutex.tryLock();
302 mNotFinishedMutex.unlock();
304 else if ( mStatus ==
Terminated && !subTasksTerminated )
311 void QgsTask::processSubTasksForHold()
313 bool subTasksRunning =
false;
314 const auto constMSubTasks = mSubTasks;
315 for (
const SubTask &subTask : constMSubTasks )
317 if ( subTask.task->status() ==
Running )
319 subTasksRunning =
true;
324 if ( mStatus ==
OnHold && !subTasksRunning && mOverallStatus !=
OnHold )
329 else if ( mStatus ==
OnHold && subTasksRunning )
336 void QgsTask::terminated()
339 processSubTasksForTermination();
352 setAutoDelete(
true );
377 , mTaskMutex( new QMutex( QMutex::Recursive ) )
380 this, &QgsTaskManager::layersWillBeRemoved );
390 QMap< long, TaskInfo >
tasks = mTasks;
392 mTaskMutex->unlock();
393 QMap< long, TaskInfo >::const_iterator it = tasks.constBegin();
394 for ( ; it != tasks.constEnd(); ++it )
396 cleanupAndDeleteTask( it.value().task );
404 return addTaskPrivate( task,
QgsTaskList(),
false, priority );
409 return addTaskPrivate( definition.
task,
421 long taskId = mNextTaskId++;
424 mTasks.insert( taskId, TaskInfo( task, priority ) );
431 mParentTasks <<
task;
434 mLayerDependencies.insert( taskId, _qgis_listRawToQPointer( task->
dependentLayers() ) );
435 mTaskMutex->unlock();
444 for (
const QgsTask::SubTask &subTask : qgis::as_const( task->mSubTasks ) )
446 switch ( subTask.dependency )
457 addTaskPrivate( subTask.task, subTask.dependencies,
true, priority );
465 if ( hasCircularDependencies( taskId ) )
481 QMutexLocker ml( mTaskMutex );
483 if ( mTasks.contains(
id ) )
484 t = mTasks.value(
id ).task;
490 QMutexLocker ml( mTaskMutex );
491 return mParentTasks.toList();
496 QMutexLocker ml( mTaskMutex );
497 return mParentTasks.count();
505 QMutexLocker ml( mTaskMutex );
506 QMap< long, TaskInfo >::const_iterator it = mTasks.constBegin();
507 for ( ; it != mTasks.constEnd(); ++it )
509 if ( it.value().task ==
task )
520 QSet< QgsTask * > parents = mParentTasks;
522 mTaskMutex->unlock();
524 const auto constParents = parents;
525 for (
QgsTask *task : constParents )
534 QMap< long, QgsTaskList >
dependencies = mTaskDependencies;
535 dependencies.detach();
536 mTaskMutex->unlock();
538 if ( !dependencies.contains( taskId ) )
541 const auto constValue = dependencies.value( taskId );
542 for (
QgsTask *task : constValue )
554 if ( resolveDependencies( taskId, taskId, results ) )
560 bool QgsTaskManager::resolveDependencies(
long firstTaskId,
long currentTaskId, QSet<long> &results )
const 563 QMap< long, QgsTaskList >
dependencies = mTaskDependencies;
564 dependencies.detach();
565 mTaskMutex->unlock();
567 if ( !dependencies.contains( currentTaskId ) )
570 const auto constValue = dependencies.value( currentTaskId );
571 for (
QgsTask *task : constValue )
573 long dependentTaskId =
taskId( task );
574 if ( dependentTaskId >= 0 )
576 if ( dependentTaskId == firstTaskId )
581 results.insert( dependentTaskId );
583 QSet< long > newTaskDeps;
584 if ( !resolveDependencies( firstTaskId, dependentTaskId, newTaskDeps ) )
587 if ( newTaskDeps.contains( firstTaskId ) )
593 results.unite( newTaskDeps );
600 bool QgsTaskManager::hasCircularDependencies(
long taskId )
const 603 return !resolveDependencies( taskId, taskId, d );
608 QMutexLocker ml( mTaskMutex );
614 QMutexLocker ml( mTaskMutex );
615 QList< QgsTask * >
tasks;
616 QMap< long, QgsWeakMapLayerPointerList >::const_iterator layerIt = mLayerDependencies.constBegin();
617 for ( ; layerIt != mLayerDependencies.constEnd(); ++layerIt )
619 if ( _qgis_listQPointerToRaw( layerIt.value() ).contains( layer ) )
631 QMutexLocker ml( mTaskMutex );
633 activeTasks.intersect( mParentTasks );
634 return activeTasks.toList();
639 QMutexLocker ml( mTaskMutex );
640 QSet< QgsTask * >
tasks = mActiveTasks;
641 return tasks.intersect( mParentTasks ).count();
650 void QgsTaskManager::taskProgressChanged(
double progress )
667 void QgsTaskManager::taskStatusChanged(
int status )
677 QgsTaskRunnableWrapper *runnable = mTasks.value(
id ).runnable;
678 mTaskMutex->unlock();
680 QThreadPool::globalInstance()->cancel( runnable );
691 cancelDependentTasks(
id );
695 bool isParent = mParentTasks.contains( task );
696 mTaskMutex->unlock();
707 cleanupAndDeleteTask( task );
712 void QgsTaskManager::layersWillBeRemoved(
const QList< QgsMapLayer * > &layers )
716 QMap< long, QgsWeakMapLayerPointerList > layerDependencies = mLayerDependencies;
717 layerDependencies.detach();
718 mTaskMutex->unlock();
720 const auto constLayers = layers;
724 for ( QMap< long, QgsWeakMapLayerPointerList >::const_iterator it = layerDependencies.constBegin();
725 it != layerDependencies.constEnd(); ++it )
727 if ( !( _qgis_listQPointerToRaw( it.value() ).contains( layer ) ) )
744 bool QgsTaskManager::cleanupAndDeleteTask(
QgsTask *task )
753 QgsTaskRunnableWrapper *runnable = mTasks.value(
id ).runnable;
755 task->disconnect(
this );
758 if ( mTaskDependencies.contains(
id ) )
759 mTaskDependencies.remove(
id );
760 mTaskMutex->unlock();
765 bool isParent = mParentTasks.contains( task );
766 mParentTasks.remove( task );
767 mSubTasks.remove( task );
769 mLayerDependencies.remove(
id );
784 QThreadPool::globalInstance()->cancel( runnable );
793 for ( QMap< long, QgsTaskList >::iterator it = mTaskDependencies.begin(); it != mTaskDependencies.end(); ++it )
795 if ( it.value().contains( task ) )
797 it.value().removeAll( task );
800 mTaskMutex->unlock();
805 void QgsTaskManager::processQueue()
809 mActiveTasks.clear();
810 for ( QMap< long, TaskInfo >::iterator it = mTasks.begin(); it != mTasks.end(); ++it )
812 QgsTask *task = it.value().task;
815 it.value().createRunnable();
816 QThreadPool::globalInstance()->start( it.value().runnable, it.value().priority );
821 mActiveTasks <<
task;
825 bool allFinished = mActiveTasks.isEmpty();
826 mTaskMutex->unlock();
834 if ( prevActiveCount != newActiveCount )
840 void QgsTaskManager::cancelDependentTasks(
long taskId )
846 QMap< long, QgsTaskList > taskDependencies = mTaskDependencies;
847 taskDependencies.detach();
848 mTaskMutex->unlock();
850 QMap< long, QgsTaskList >::const_iterator it = taskDependencies.constBegin();
851 for ( ; it != taskDependencies.constEnd(); ++it )
853 if ( it.value().contains( canceledTask ) )
866 QgsTaskManager::TaskInfo::TaskInfo(
QgsTask *task,
int priority )
869 , priority( priority )
872 void QgsTaskManager::TaskInfo::createRunnable()
874 Q_ASSERT( !runnable );
875 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.