28#include "moc_qgstaskmanager.cpp"
30using namespace Qt::StringLiterals;
38 , mDescription( name )
39 , mNotStartedMutex( 1 )
41 mNotStartedMutex.acquire();
46 Q_ASSERT_X( mStatus !=
Running,
"delete", u
"status was %1"_s.arg( mStatus ).toLatin1() );
48 mNotFinishedMutex.lock();
49 const auto constMSubTasks = mSubTasks;
50 for (
const SubTask &subTask : constMSubTasks )
54 mNotFinishedMutex.unlock();
55 mNotStartedMutex.release();
65 return mElapsedTime.elapsed();
70 QMutexLocker locker( &mNotFinishedMutex );
71 mNotStartedMutex.release();
73 Q_ASSERT( mStartCount == 1 );
103 mShouldTerminateMutex.lock();
104 mShouldTerminate =
true;
105 mShouldTerminateMutex.unlock();
110 mNotStartedMutex.release();
115 processSubTasksForTermination();
118 const auto constMSubTasks = mSubTasks;
119 for (
const SubTask &subTask : constMSubTasks )
121 subTask.task->cancel();
127 QMutexLocker locker( &mShouldTerminateMutex );
128 return mShouldTerminate;
136 processSubTasksForHold();
139 const auto constMSubTasks = mSubTasks;
140 for (
const SubTask &subTask : constMSubTasks )
142 subTask.task->hold();
155 const auto constMSubTasks = mSubTasks;
156 for (
const SubTask &subTask : constMSubTasks )
158 subTask.task->unhold();
165 mSubTasks << SubTask( subTask, dependencies, subTaskDependency );
172 return _qgis_listQPointerToRaw( mDependentLayers );
178 mNotStartedMutex.acquire();
179 mNotStartedMutex.release();
189 timeout = std::numeric_limits< int >::max();
190 if ( mNotFinishedMutex.tryLock( timeout ) )
192 mNotFinishedMutex.unlock();
193 QCoreApplication::sendPostedEvents(
this );
209void QgsTask::subTaskStatusChanged(
int status )
211 QgsTask *subTask = qobject_cast< QgsTask * >( sender() );
222 processSubTasksForCompletion();
227 processSubTasksForTermination();
231 processSubTasksForHold();
244 if ( !mSubTasks.isEmpty() )
248 double totalProgress = 0.0;
249 const auto constMSubTasks = mSubTasks;
250 for (
const SubTask &subTask : constMSubTasks )
254 totalProgress += 100.0;
258 totalProgress += subTask.task->
progress();
265 double prevProgress = mTotalProgress;
269 if (
static_cast< int >( prevProgress * 10 ) !=
static_cast< int >( mTotalProgress * 10 ) )
273void QgsTask::completed()
276 QMetaObject::invokeMethod(
this,
"processSubTasksForCompletion" );
279void QgsTask::processSubTasksForCompletion()
281 bool subTasksCompleted =
true;
282 const auto constMSubTasks = mSubTasks;
283 for (
const SubTask &subTask : constMSubTasks )
287 subTasksCompleted =
false;
292 if ( mStatus ==
Complete && subTasksCompleted )
307void QgsTask::processSubTasksForTermination()
309 bool subTasksTerminated =
true;
310 const auto constMSubTasks = mSubTasks;
311 for (
const SubTask &subTask : constMSubTasks )
315 subTasksTerminated =
false;
327 else if ( mStatus ==
Terminated && !subTasksTerminated )
334void QgsTask::processSubTasksForHold()
336 bool subTasksRunning =
false;
337 const auto constMSubTasks = mSubTasks;
338 for (
const SubTask &subTask : constMSubTasks )
342 subTasksRunning =
true;
347 if ( mStatus ==
OnHold && !subTasksRunning && mOverallStatus !=
OnHold )
352 else if ( mStatus ==
OnHold && subTasksRunning )
359void QgsTask::terminated()
362 QMetaObject::invokeMethod(
this,
"processSubTasksForTermination" );
368class QgsTaskRunnableWrapper :
public QRunnable
372 explicit QgsTaskRunnableWrapper( QgsTask *task )
375 setAutoDelete(
true );
386 QgsTask *mTask =
nullptr;
400 , mThreadPool( new QThreadPool( this ) )
401 , mTaskMutex( new QRecursiveMutex() )
413 QMap< long, TaskInfo >
tasks = mTasks;
415 mTaskMutex->unlock();
416 QMap< long, TaskInfo >::const_iterator it =
tasks.constBegin();
417 for ( ; it !=
tasks.constEnd(); ++it )
419 cleanupAndDeleteTask( it.value().task );
423 mThreadPool->waitForDone();
438 return addTaskPrivate( definition.
task,
445long QgsTaskManager::addTaskPrivate(
QgsTask *task,
QgsTaskList dependencies,
bool isSubTask,
int priority )
456 this, &QgsTaskManager::layersWillBeRemoved );
459 long taskId = mNextTaskId++;
462 mTasks.insert(
taskId, TaskInfo(
task, priority ) );
470 mParentTasks <<
task;
472 if ( !
task->dependentLayers().isEmpty() )
473 mLayerDependencies.insert(
taskId, _qgis_listRawToQPointer(
task->dependentLayers() ) );
474 mTaskMutex->unlock();
483 for (
const QgsTask::SubTask &subTask : std::as_const(
task->mSubTasks ) )
485 switch ( subTask.dependency )
496 addTaskPrivate( subTask.task, subTask.dependencies,
true, priority );
504 if ( hasCircularDependencies(
taskId ) )
522 QMutexLocker ml( mTaskMutex );
524 if ( mTasks.contains(
id ) )
525 t = mTasks.value(
id ).task;
531 QMutexLocker ml( mTaskMutex );
532 return QList<QgsTask *>( mParentTasks.begin(), mParentTasks.end() );
537 QMutexLocker ml( mTaskMutex );
538 return mParentTasks.count();
546 QMutexLocker ml( mTaskMutex );
547 const auto iter = mMapTaskPtrToId.constFind(
task );
548 if ( iter != mMapTaskPtrToId.constEnd() )
556 QSet< QgsTask * > parents = mParentTasks;
558 mTaskMutex->unlock();
560 const auto constParents = parents;
570 QMap< long, QgsTaskList >
dependencies = mTaskDependencies;
572 mTaskMutex->unlock();
590 if ( resolveDependencies(
taskId, results ) )
596bool QgsTaskManager::resolveDependencies(
long thisTaskId, QSet<long> &results )
const
599 QMap< long, QgsTaskList >
dependencies = mTaskDependencies;
601 mTaskMutex->unlock();
603 QSet<long> alreadyExploredTaskIds;
604 QStack<long> stackTaskIds;
605 stackTaskIds.push( thisTaskId );
606 while ( !stackTaskIds.isEmpty() )
608 const long currentTaskId = stackTaskIds.pop();
609 alreadyExploredTaskIds.insert( currentTaskId );
615 const auto &constValue = *iter;
619 if ( dependentTaskId >= 0 )
621 if ( thisTaskId == dependentTaskId )
628 results.insert( dependentTaskId );
631 if ( !alreadyExploredTaskIds.contains( dependentTaskId ) )
633 stackTaskIds.push( dependentTaskId );
642bool QgsTaskManager::hasCircularDependencies(
long taskId )
const
645 return !resolveDependencies(
taskId, d );
650 QMutexLocker ml( mTaskMutex );
656 QMutexLocker ml( mTaskMutex );
657 QList< QgsTask * >
tasks;
658 QMap< long, QgsWeakMapLayerPointerList >::const_iterator layerIt = mLayerDependencies.constBegin();
659 for ( ; layerIt != mLayerDependencies.constEnd(); ++layerIt )
661 if ( _qgis_listQPointerToRaw( layerIt.value() ).contains( layer ) )
673 QMutexLocker ml( mTaskMutex );
681 QMutexLocker ml( mTaskMutex );
682 QSet< QgsTask * >
tasks = mActiveTasks;
684 if ( !includeHidden )
686 QSet< QgsTask * > filteredTasks;
687 filteredTasks.reserve(
tasks.size() );
691 filteredTasks.insert(
task );
693 tasks = filteredTasks;
696 return tasks.intersect( mParentTasks ).count();
705void QgsTaskManager::taskProgressChanged(
double progress )
707 QgsTask *
task = qobject_cast< QgsTask * >( sender() );
724void QgsTaskManager::taskStatusChanged(
int status )
726 QgsTask *
task = qobject_cast< QgsTask * >( sender() );
735 QgsTaskRunnableWrapper *runnable = mTasks.value(
id ).runnable;
736 mTaskMutex->unlock();
737 if ( runnable && mThreadPool->tryTake( runnable ) )
740 mTasks[ id ].runnable =
nullptr;
746 task->finished( result );
752 cancelDependentTasks(
id );
756 bool isParent = mParentTasks.contains(
task );
757 mTaskMutex->unlock();
758 if ( isParent && !isHidden )
768 cleanupAndDeleteTask(
task );
773void QgsTaskManager::layersWillBeRemoved(
const QList< QgsMapLayer * > &layers )
777 QMap< long, QgsWeakMapLayerPointerList > layerDependencies = mLayerDependencies;
778 layerDependencies.detach();
779 mTaskMutex->unlock();
781 const auto constLayers = layers;
782 for ( QgsMapLayer *layer : constLayers )
785 for ( QMap< long, QgsWeakMapLayerPointerList >::const_iterator it = layerDependencies.constBegin();
786 it != layerDependencies.constEnd(); ++it )
788 if ( !( _qgis_listQPointerToRaw( it.value() ).contains( layer ) ) )
794 QgsTask *dependentTask =
task( it.key() );
805bool QgsTaskManager::cleanupAndDeleteTask(
QgsTask *task )
814 QgsTaskRunnableWrapper *runnable = mTasks.value(
id ).runnable;
816 task->disconnect(
this );
819 if ( mTaskDependencies.contains(
id ) )
820 mTaskDependencies.remove(
id );
821 mTaskMutex->unlock();
826 bool isParent = mParentTasks.contains(
task );
827 mParentTasks.remove(
task );
828 mSubTasks.remove(
task );
830 mMapTaskPtrToId.remove(
task );
831 mLayerDependencies.remove(
id );
845 if ( runnable && mThreadPool->tryTake( runnable ) )
848 mTasks[ id ].runnable =
nullptr;
859 for ( QMap< long, QgsTaskList >::iterator it = mTaskDependencies.begin(); it != mTaskDependencies.end(); ++it )
861 if ( it.value().contains(
task ) )
863 it.value().removeAll(
task );
866 mTaskMutex->unlock();
871void QgsTaskManager::processQueue()
875 mActiveTasks.clear();
876 for ( QMap< long, TaskInfo >::iterator it = mTasks.begin(); it != mTasks.end(); ++it )
878 QgsTask *
task = it.value().task;
881 it.value().createRunnable();
882 mThreadPool->start( it.value().runnable, it.value().priority );
887 mActiveTasks <<
task;
891 bool allFinished = mActiveTasks.isEmpty();
892 mTaskMutex->unlock();
900 if ( prevActiveCount != newActiveCount )
906void QgsTaskManager::cancelDependentTasks(
long taskId )
912 QMap< long, QgsTaskList > taskDependencies = mTaskDependencies;
913 taskDependencies.detach();
914 mTaskMutex->unlock();
916 QMap< long, QgsTaskList >::const_iterator it = taskDependencies.constBegin();
917 for ( ; it != taskDependencies.constEnd(); ++it )
919 if ( it.value().contains( canceledTask ) )
925 QgsTask *dependentTask =
task( it.key() );
932QgsTaskManager::TaskInfo::TaskInfo(
QgsTask *task,
int priority )
935 , priority( priority )
938void QgsTaskManager::TaskInfo::createRunnable()
940 Q_ASSERT( !runnable );
941 runnable =
new QgsTaskRunnableWrapper( task );
963 if ( mShouldTerminate )
966 [
this, i](
double subTaskProgress )
968 mProgress = 100.0 * ( double( i ) + subTaskProgress / 100.0 ) /
double(
mSubTasksSerial.size() );
971 if ( !subTask->
run() )
973 subTask->completed();
974 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.