26#include <QtConcurrentRun>
28#include "moc_qgstaskmanager.cpp"
36 , mDescription( name )
37 , mNotStartedMutex( 1 )
39 mNotStartedMutex.acquire();
44 Q_ASSERT_X( mStatus !=
Running,
"delete", QStringLiteral(
"status was %1" ).arg( mStatus ).toLatin1() );
46 mNotFinishedMutex.lock();
47 const auto constMSubTasks = mSubTasks;
48 for (
const SubTask &subTask : constMSubTasks )
52 mNotFinishedMutex.unlock();
53 mNotStartedMutex.release();
63 return mElapsedTime.elapsed();
68 QMutexLocker locker( &mNotFinishedMutex );
69 mNotStartedMutex.release();
71 Q_ASSERT( mStartCount == 1 );
101 mShouldTerminateMutex.lock();
102 mShouldTerminate =
true;
103 mShouldTerminateMutex.unlock();
108 mNotStartedMutex.release();
113 processSubTasksForTermination();
116 const auto constMSubTasks = mSubTasks;
117 for (
const SubTask &subTask : constMSubTasks )
119 subTask.task->cancel();
125 QMutexLocker locker( &mShouldTerminateMutex );
126 return mShouldTerminate;
134 processSubTasksForHold();
137 const auto constMSubTasks = mSubTasks;
138 for (
const SubTask &subTask : constMSubTasks )
140 subTask.task->hold();
153 const auto constMSubTasks = mSubTasks;
154 for (
const SubTask &subTask : constMSubTasks )
156 subTask.task->unhold();
163 mSubTasks << SubTask( subTask, dependencies, subTaskDependency );
170 return _qgis_listQPointerToRaw( mDependentLayers );
176 mNotStartedMutex.acquire();
177 mNotStartedMutex.release();
187 timeout = std::numeric_limits< int >::max();
188 if ( mNotFinishedMutex.tryLock( timeout ) )
190 mNotFinishedMutex.unlock();
191 QCoreApplication::sendPostedEvents(
this );
207void QgsTask::subTaskStatusChanged(
int status )
209 QgsTask *subTask = qobject_cast< QgsTask * >( sender() );
220 processSubTasksForCompletion();
225 processSubTasksForTermination();
229 processSubTasksForHold();
242 if ( !mSubTasks.isEmpty() )
246 double totalProgress = 0.0;
247 const auto constMSubTasks = mSubTasks;
248 for (
const SubTask &subTask : constMSubTasks )
252 totalProgress += 100.0;
256 totalProgress += subTask.task->
progress();
263 double prevProgress = mTotalProgress;
267 if (
static_cast< int >( prevProgress * 10 ) !=
static_cast< int >( mTotalProgress * 10 ) )
271void QgsTask::completed()
274 QMetaObject::invokeMethod(
this,
"processSubTasksForCompletion" );
277void QgsTask::processSubTasksForCompletion()
279 bool subTasksCompleted =
true;
280 const auto constMSubTasks = mSubTasks;
281 for (
const SubTask &subTask : constMSubTasks )
285 subTasksCompleted =
false;
290 if ( mStatus ==
Complete && subTasksCompleted )
305void QgsTask::processSubTasksForTermination()
307 bool subTasksTerminated =
true;
308 const auto constMSubTasks = mSubTasks;
309 for (
const SubTask &subTask : constMSubTasks )
313 subTasksTerminated =
false;
325 else if ( mStatus ==
Terminated && !subTasksTerminated )
332void QgsTask::processSubTasksForHold()
334 bool subTasksRunning =
false;
335 const auto constMSubTasks = mSubTasks;
336 for (
const SubTask &subTask : constMSubTasks )
340 subTasksRunning =
true;
345 if ( mStatus ==
OnHold && !subTasksRunning && mOverallStatus !=
OnHold )
350 else if ( mStatus ==
OnHold && subTasksRunning )
357void QgsTask::terminated()
360 QMetaObject::invokeMethod(
this,
"processSubTasksForTermination" );
366class QgsTaskRunnableWrapper :
public QRunnable
370 explicit QgsTaskRunnableWrapper( QgsTask *task )
373 setAutoDelete(
true );
384 QgsTask *mTask =
nullptr;
398 , mThreadPool( new QThreadPool( this ) )
399 , mTaskMutex( new QRecursiveMutex() )
411 QMap< long, TaskInfo >
tasks = mTasks;
413 mTaskMutex->unlock();
414 QMap< long, TaskInfo >::const_iterator it =
tasks.constBegin();
415 for ( ; it !=
tasks.constEnd(); ++it )
417 cleanupAndDeleteTask( it.value().task );
421 mThreadPool->waitForDone();
436 return addTaskPrivate( definition.
task,
443long QgsTaskManager::addTaskPrivate(
QgsTask *task,
QgsTaskList dependencies,
bool isSubTask,
int priority )
454 this, &QgsTaskManager::layersWillBeRemoved );
457 long taskId = mNextTaskId++;
460 mTasks.insert(
taskId, TaskInfo(
task, priority ) );
468 mParentTasks <<
task;
470 if ( !
task->dependentLayers().isEmpty() )
471 mLayerDependencies.insert(
taskId, _qgis_listRawToQPointer(
task->dependentLayers() ) );
472 mTaskMutex->unlock();
481 for (
const QgsTask::SubTask &subTask : std::as_const(
task->mSubTasks ) )
483 switch ( subTask.dependency )
494 addTaskPrivate( subTask.task, subTask.dependencies,
true, priority );
502 if ( hasCircularDependencies(
taskId ) )
520 QMutexLocker ml( mTaskMutex );
522 if ( mTasks.contains(
id ) )
523 t = mTasks.value(
id ).task;
529 QMutexLocker ml( mTaskMutex );
530 return QList<QgsTask *>( mParentTasks.begin(), mParentTasks.end() );
535 QMutexLocker ml( mTaskMutex );
536 return mParentTasks.count();
544 QMutexLocker ml( mTaskMutex );
545 const auto iter = mMapTaskPtrToId.constFind(
task );
546 if ( iter != mMapTaskPtrToId.constEnd() )
554 QSet< QgsTask * > parents = mParentTasks;
556 mTaskMutex->unlock();
558 const auto constParents = parents;
568 QMap< long, QgsTaskList >
dependencies = mTaskDependencies;
570 mTaskMutex->unlock();
588 if ( resolveDependencies(
taskId, results ) )
594bool QgsTaskManager::resolveDependencies(
long thisTaskId, QSet<long> &results )
const
597 QMap< long, QgsTaskList >
dependencies = mTaskDependencies;
599 mTaskMutex->unlock();
601 QSet<long> alreadyExploredTaskIds;
602 QStack<long> stackTaskIds;
603 stackTaskIds.push( thisTaskId );
604 while ( !stackTaskIds.isEmpty() )
606 const long currentTaskId = stackTaskIds.pop();
607 alreadyExploredTaskIds.insert( currentTaskId );
613 const auto &constValue = *iter;
617 if ( dependentTaskId >= 0 )
619 if ( thisTaskId == dependentTaskId )
626 results.insert( dependentTaskId );
629 if ( !alreadyExploredTaskIds.contains( dependentTaskId ) )
631 stackTaskIds.push( dependentTaskId );
640bool QgsTaskManager::hasCircularDependencies(
long taskId )
const
643 return !resolveDependencies(
taskId, d );
648 QMutexLocker ml( mTaskMutex );
654 QMutexLocker ml( mTaskMutex );
655 QList< QgsTask * >
tasks;
656 QMap< long, QgsWeakMapLayerPointerList >::const_iterator layerIt = mLayerDependencies.constBegin();
657 for ( ; layerIt != mLayerDependencies.constEnd(); ++layerIt )
659 if ( _qgis_listQPointerToRaw( layerIt.value() ).contains( layer ) )
671 QMutexLocker ml( mTaskMutex );
679 QMutexLocker ml( mTaskMutex );
680 QSet< QgsTask * >
tasks = mActiveTasks;
682 if ( !includeHidden )
684 QSet< QgsTask * > filteredTasks;
685 filteredTasks.reserve(
tasks.size() );
689 filteredTasks.insert(
task );
691 tasks = filteredTasks;
694 return tasks.intersect( mParentTasks ).count();
703void QgsTaskManager::taskProgressChanged(
double progress )
705 QgsTask *
task = qobject_cast< QgsTask * >( sender() );
722void QgsTaskManager::taskStatusChanged(
int status )
724 QgsTask *
task = qobject_cast< QgsTask * >( sender() );
733 QgsTaskRunnableWrapper *runnable = mTasks.value(
id ).runnable;
734 mTaskMutex->unlock();
735 if ( runnable && mThreadPool->tryTake( runnable ) )
738 mTasks[ id ].runnable =
nullptr;
744 task->finished( result );
750 cancelDependentTasks(
id );
754 bool isParent = mParentTasks.contains(
task );
755 mTaskMutex->unlock();
756 if ( isParent && !isHidden )
766 cleanupAndDeleteTask(
task );
771void QgsTaskManager::layersWillBeRemoved(
const QList< QgsMapLayer * > &layers )
775 QMap< long, QgsWeakMapLayerPointerList > layerDependencies = mLayerDependencies;
776 layerDependencies.detach();
777 mTaskMutex->unlock();
779 const auto constLayers = layers;
780 for ( QgsMapLayer *layer : constLayers )
783 for ( QMap< long, QgsWeakMapLayerPointerList >::const_iterator it = layerDependencies.constBegin();
784 it != layerDependencies.constEnd(); ++it )
786 if ( !( _qgis_listQPointerToRaw( it.value() ).contains( layer ) ) )
792 QgsTask *dependentTask =
task( it.key() );
803bool QgsTaskManager::cleanupAndDeleteTask(
QgsTask *task )
812 QgsTaskRunnableWrapper *runnable = mTasks.value(
id ).runnable;
814 task->disconnect(
this );
817 if ( mTaskDependencies.contains(
id ) )
818 mTaskDependencies.remove(
id );
819 mTaskMutex->unlock();
824 bool isParent = mParentTasks.contains(
task );
825 mParentTasks.remove(
task );
826 mSubTasks.remove(
task );
828 mMapTaskPtrToId.remove(
task );
829 mLayerDependencies.remove(
id );
843 if ( runnable && mThreadPool->tryTake( runnable ) )
846 mTasks[ id ].runnable =
nullptr;
857 for ( QMap< long, QgsTaskList >::iterator it = mTaskDependencies.begin(); it != mTaskDependencies.end(); ++it )
859 if ( it.value().contains(
task ) )
861 it.value().removeAll(
task );
864 mTaskMutex->unlock();
869void QgsTaskManager::processQueue()
873 mActiveTasks.clear();
874 for ( QMap< long, TaskInfo >::iterator it = mTasks.begin(); it != mTasks.end(); ++it )
876 QgsTask *
task = it.value().task;
879 it.value().createRunnable();
880 mThreadPool->start( it.value().runnable, it.value().priority );
885 mActiveTasks <<
task;
889 bool allFinished = mActiveTasks.isEmpty();
890 mTaskMutex->unlock();
898 if ( prevActiveCount != newActiveCount )
904void QgsTaskManager::cancelDependentTasks(
long taskId )
910 QMap< long, QgsTaskList > taskDependencies = mTaskDependencies;
911 taskDependencies.detach();
912 mTaskMutex->unlock();
914 QMap< long, QgsTaskList >::const_iterator it = taskDependencies.constBegin();
915 for ( ; it != taskDependencies.constEnd(); ++it )
917 if ( it.value().contains( canceledTask ) )
923 QgsTask *dependentTask =
task( it.key() );
930QgsTaskManager::TaskInfo::TaskInfo(
QgsTask *task,
int priority )
933 , priority( priority )
936void QgsTaskManager::TaskInfo::createRunnable()
938 Q_ASSERT( !runnable );
939 runnable =
new QgsTaskRunnableWrapper( task );
961 if ( mShouldTerminate )
964 [
this, i](
double subTaskProgress )
966 mProgress = 100.0 * ( double( i ) + subTaskProgress / 100.0 ) /
double(
mSubTasksSerial.size() );
969 if ( !subTask->
run() )
971 subTask->completed();
972 mProgress = 100.0 * double( i + 1 ) / double(
mSubTasksSerial.size() );
Base class for all map layer types.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
static QgsProject * instance()
Returns the QgsProject singleton instance.
void layersWillBeRemoved(const QStringList &layerIds)
Emitted when one or more layers are about to be removed from the registry.
QList< QgsTask * > tasks() const
Returns all tasks tracked by the manager.
void finalTaskProgressChanged(double progress)
Will be emitted when only a single task remains to complete and that task has reported a progress cha...
void statusChanged(long taskId, int status)
Will be emitted when a task reports a status change.
void taskAdded(long taskId)
Emitted when a new task has been added to the manager.
QList< QgsTask * > activeTasks() const
Returns a list of the active (queued or running) tasks.
QgsTaskManager(QObject *parent=nullptr)
Constructor for QgsTaskManager.
void taskAboutToBeDeleted(long taskId)
Emitted when a task is about to be deleted.
long taskId(QgsTask *task) const
Returns the unique task ID corresponding to a task managed by the class.
int count() const
Returns the number of tasks tracked by the manager.
QList< QgsTask * > tasksDependentOnLayer(QgsMapLayer *layer) const
Returns a list of tasks which depend on a layer.
void allTasksFinished()
Emitted when all tasks are complete.
~QgsTaskManager() override
bool dependenciesSatisfied(long taskId) const
Returns true if all dependencies for the specified task are satisfied.
QThreadPool * threadPool()
Returns the threadpool utilized by the task manager.
void cancelAll()
Instructs all tasks tracked by the manager to terminate.
QSet< long > dependencies(long taskId) const
Returns the set of task IDs on which a task is dependent.
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.
int countActiveTasks(bool includeHidden=true) const
Returns the number of active (queued or running) tasks.
void triggerTask(QgsTask *task)
Triggers a task, e.g.
void countActiveTasksChanged(int count)
Emitted when the number of active tasks changes.
QList< QgsMapLayer * > dependentLayers(long taskId) const
Returns a list of layers on which as task is dependent.
long addTask(QgsTask *task, int priority=0)
Adds a task to the manager.
void taskTriggered(QgsTask *task)
Emitted when a task is triggered.
QList< QgsTask * > mSubTasksSerial
void addSubTask(QgsTask *subTask)
Add a subtask and transfer its ownership.
void cancel() override
Notifies the task that it should terminate.
~QgsTaskWithSerialSubTasks() override
bool run() override
Performs the task's operation.
Abstract base class for long running background tasks.
TaskStatus status() const
Returns the current task status.
Flags flags() const
Returns the flags associated with the task.
void taskCompleted()
Will be emitted by task to indicate its successful completion.
double progress() const
Returns the task's progress (between 0.0 and 100.0).
virtual bool run()=0
Performs the task's operation.
void progressChanged(double progress)
Will be emitted by task when its progress changes.
QList< QgsMapLayer * > dependentLayers() const
Returns the list of layers on which the task depends.
void begun()
Will be emitted by task to indicate its commencement.
virtual void cancel()
Notifies the task that it should terminate.
QgsTask(const QString &description=QString(), QgsTask::Flags flags=AllFlags)
Constructor for QgsTask.
@ Hidden
Hide task from GUI.
void taskTerminated()
Will be emitted by task if it has terminated for any reason other then completion (e....
void statusChanged(int status)
Will be emitted by task when its status changes.
qint64 elapsedTime() const
Returns the elapsed time since the task commenced, in milliseconds.
void unhold()
Releases the task from being held.
void setDependentLayers(const QList< QgsMapLayer * > &dependentLayers)
Sets a list of layers on which the task depends.
@ Terminated
Task was terminated or errored.
@ Queued
Task is queued and has not begun.
@ OnHold
Task is queued but on hold and will not be started.
@ Running
Task is currently running.
@ Complete
Task successfully completed.
void hold()
Places the task on hold.
QString description() const
Returns the task's description.
void addSubTask(QgsTask *subTask, const QgsTaskList &dependencies=QgsTaskList(), SubTaskDependency subTaskDependency=SubTaskIndependent)
Adds a subtask to this task.
void setDescription(const QString &description)
Sets the task's description.
SubTaskDependency
Controls how subtasks relate to their parent task.
@ SubTaskIndependent
Subtask is independent of the parent, and can run before, after or at the same time as the parent.
@ ParentDependsOnSubTask
Subtask must complete before parent can begin.
bool isCanceled() const
Will return true if task should terminate ASAP.
void setProgress(double progress)
Sets the task's current progress.
bool waitForFinished(int timeout=30000)
Blocks the current thread until the task finishes or a maximum of timeout milliseconds.
QList< QgsWeakMapLayerPointer > QgsWeakMapLayerPointerList
A list of weak pointers to QgsMapLayers.
QList< QgsTask * > QgsTaskList
List of QgsTask objects.
Definition of a task for inclusion in the manager.
QgsTaskList dependentTasks
List of dependent tasks which must be completed before task can run.