22#include <QtConcurrentRun>
31 , mDescription( name )
32 , mNotStartedMutex( 1 )
34 mNotStartedMutex.acquire();
39 Q_ASSERT_X( mStatus !=
Running,
"delete", QStringLiteral(
"status was %1" ).arg( mStatus ).toLatin1() );
41 mNotFinishedMutex.lock();
42 const auto constMSubTasks = mSubTasks;
43 for (
const SubTask &subTask : constMSubTasks )
47 mNotFinishedMutex.unlock();
48 mNotStartedMutex.release();
58 return mElapsedTime.elapsed();
63 QMutexLocker locker( &mNotFinishedMutex );
64 mNotStartedMutex.release();
66 Q_ASSERT( mStartCount == 1 );
96 mShouldTerminateMutex.lock();
97 mShouldTerminate =
true;
98 mShouldTerminateMutex.unlock();
103 mNotStartedMutex.release();
108 processSubTasksForTermination();
111 const auto constMSubTasks = mSubTasks;
112 for (
const SubTask &subTask : constMSubTasks )
114 subTask.task->cancel();
120 QMutexLocker locker( &mShouldTerminateMutex );
121 return mShouldTerminate;
129 processSubTasksForHold();
132 const auto constMSubTasks = mSubTasks;
133 for (
const SubTask &subTask : constMSubTasks )
135 subTask.task->hold();
148 const auto constMSubTasks = mSubTasks;
149 for (
const SubTask &subTask : constMSubTasks )
151 subTask.task->unhold();
158 mSubTasks << SubTask( subTask, dependencies, subTaskDependency );
165 return _qgis_listQPointerToRaw( mDependentLayers );
171 mNotStartedMutex.acquire();
181 timeout = std::numeric_limits< int >::max();
182 if ( mNotFinishedMutex.tryLock( timeout ) )
184 mNotFinishedMutex.unlock();
185 QCoreApplication::sendPostedEvents(
this );
201void QgsTask::subTaskStatusChanged(
int status )
203 QgsTask *subTask = qobject_cast< QgsTask * >( sender() );
214 processSubTasksForCompletion();
219 processSubTasksForTermination();
223 processSubTasksForHold();
236 if ( !mSubTasks.isEmpty() )
240 double totalProgress = 0.0;
241 const auto constMSubTasks = mSubTasks;
242 for (
const SubTask &subTask : constMSubTasks )
246 totalProgress += 100.0;
250 totalProgress += subTask.task->
progress();
257 double prevProgress = mTotalProgress;
261 if (
static_cast< int >( prevProgress * 10 ) !=
static_cast< int >( mTotalProgress * 10 ) )
265void QgsTask::completed()
268 QMetaObject::invokeMethod(
this,
"processSubTasksForCompletion" );
271void QgsTask::processSubTasksForCompletion()
273 bool subTasksCompleted =
true;
274 const auto constMSubTasks = mSubTasks;
275 for (
const SubTask &subTask : constMSubTasks )
279 subTasksCompleted =
false;
284 if ( mStatus ==
Complete && subTasksCompleted )
299void QgsTask::processSubTasksForTermination()
301 bool subTasksTerminated =
true;
302 const auto constMSubTasks = mSubTasks;
303 for (
const SubTask &subTask : constMSubTasks )
307 subTasksTerminated =
false;
319 else if ( mStatus ==
Terminated && !subTasksTerminated )
326void QgsTask::processSubTasksForHold()
328 bool subTasksRunning =
false;
329 const auto constMSubTasks = mSubTasks;
330 for (
const SubTask &subTask : constMSubTasks )
334 subTasksRunning =
true;
339 if ( mStatus ==
OnHold && !subTasksRunning && mOverallStatus !=
OnHold )
344 else if ( mStatus ==
OnHold && subTasksRunning )
351void QgsTask::terminated()
354 QMetaObject::invokeMethod(
this,
"processSubTasksForTermination" );
360class QgsTaskRunnableWrapper :
public QRunnable
364 explicit QgsTaskRunnableWrapper(
QgsTask *task )
367 setAutoDelete(
true );
392 , mThreadPool( new QThreadPool( this ) )
393 , mTaskMutex( new QRecursiveMutex() )
405 QMap< long, TaskInfo >
tasks = mTasks;
407 mTaskMutex->unlock();
408 QMap< long, TaskInfo >::const_iterator it =
tasks.constBegin();
409 for ( ; it !=
tasks.constEnd(); ++it )
411 cleanupAndDeleteTask( it.value().task );
415 mThreadPool->waitForDone();
430 return addTaskPrivate( definition.
task,
437long QgsTaskManager::addTaskPrivate(
QgsTask *task,
QgsTaskList dependencies,
bool isSubTask,
int priority )
448 this, &QgsTaskManager::layersWillBeRemoved );
451 long taskId = mNextTaskId++;
454 mTasks.insert(
taskId, TaskInfo(
task, priority ) );
461 mParentTasks <<
task;
465 mTaskMutex->unlock();
474 for (
const QgsTask::SubTask &subTask : std::as_const(
task->mSubTasks ) )
476 switch ( subTask.dependency )
487 addTaskPrivate( subTask.task, subTask.dependencies,
true, priority );
495 if ( hasCircularDependencies(
taskId ) )
513 QMutexLocker ml( mTaskMutex );
515 if ( mTasks.contains(
id ) )
516 t = mTasks.value(
id ).task;
522 QMutexLocker ml( mTaskMutex );
523 return QList<QgsTask *>( mParentTasks.begin(), mParentTasks.end() );
528 QMutexLocker ml( mTaskMutex );
529 return mParentTasks.count();
537 QMutexLocker ml( mTaskMutex );
538 QMap< long, TaskInfo >::const_iterator it = mTasks.constBegin();
539 for ( ; it != mTasks.constEnd(); ++it )
541 if ( it.value().task ==
task )
552 QSet< QgsTask * > parents = mParentTasks;
554 mTaskMutex->unlock();
556 const auto constParents = parents;
566 QMap< long, QgsTaskList >
dependencies = mTaskDependencies;
568 mTaskMutex->unlock();
592bool QgsTaskManager::resolveDependencies(
long firstTaskId,
long currentTaskId, QSet<long> &results )
const
595 QMap< long, QgsTaskList >
dependencies = mTaskDependencies;
597 mTaskMutex->unlock();
602 const auto constValue =
dependencies.value( currentTaskId );
606 if ( dependentTaskId >= 0 )
608 if ( dependentTaskId == firstTaskId )
613 results.insert( dependentTaskId );
615 QSet< long > newTaskDeps;
616 if ( !resolveDependencies( firstTaskId, dependentTaskId, newTaskDeps ) )
619 if ( newTaskDeps.contains( firstTaskId ) )
625 results.unite( newTaskDeps );
632bool QgsTaskManager::hasCircularDependencies(
long taskId )
const
640 QMutexLocker ml( mTaskMutex );
646 QMutexLocker ml( mTaskMutex );
647 QList< QgsTask * >
tasks;
648 QMap< long, QgsWeakMapLayerPointerList >::const_iterator layerIt = mLayerDependencies.constBegin();
649 for ( ; layerIt != mLayerDependencies.constEnd(); ++layerIt )
651 if ( _qgis_listQPointerToRaw( layerIt.value() ).contains( layer ) )
663 QMutexLocker ml( mTaskMutex );
671 QMutexLocker ml( mTaskMutex );
672 QSet< QgsTask * >
tasks = mActiveTasks;
674 if ( !includeHidden )
676 QSet< QgsTask * > filteredTasks;
677 filteredTasks.reserve(
tasks.size() );
681 filteredTasks.insert(
task );
683 tasks = filteredTasks;
686 return tasks.intersect( mParentTasks ).count();
695void QgsTaskManager::taskProgressChanged(
double progress )
697 QgsTask *
task = qobject_cast< QgsTask * >( sender() );
714void QgsTaskManager::taskStatusChanged(
int status )
716 QgsTask *
task = qobject_cast< QgsTask * >( sender() );
725 QgsTaskRunnableWrapper *runnable = mTasks.value(
id ).runnable;
726 mTaskMutex->unlock();
727 if ( runnable && mThreadPool->tryTake( runnable ) )
730 mTasks[ id ].runnable =
nullptr;
742 cancelDependentTasks(
id );
746 bool isParent = mParentTasks.contains(
task );
747 mTaskMutex->unlock();
748 if ( isParent && !isHidden )
758 cleanupAndDeleteTask(
task );
763void QgsTaskManager::layersWillBeRemoved(
const QList< QgsMapLayer * > &layers )
767 QMap< long, QgsWeakMapLayerPointerList > layerDependencies = mLayerDependencies;
768 layerDependencies.detach();
769 mTaskMutex->unlock();
771 const auto constLayers = layers;
775 for ( QMap< long, QgsWeakMapLayerPointerList >::const_iterator it = layerDependencies.constBegin();
776 it != layerDependencies.constEnd(); ++it )
778 if ( !( _qgis_listQPointerToRaw( it.value() ).contains( layer ) ) )
795bool QgsTaskManager::cleanupAndDeleteTask(
QgsTask *task )
804 QgsTaskRunnableWrapper *runnable = mTasks.value(
id ).runnable;
806 task->disconnect(
this );
809 if ( mTaskDependencies.contains(
id ) )
810 mTaskDependencies.remove(
id );
811 mTaskMutex->unlock();
816 bool isParent = mParentTasks.contains(
task );
817 mParentTasks.remove(
task );
818 mSubTasks.remove(
task );
820 mLayerDependencies.remove(
id );
834 if ( runnable && mThreadPool->tryTake( runnable ) )
837 mTasks[ id ].runnable =
nullptr;
848 for ( QMap< long, QgsTaskList >::iterator it = mTaskDependencies.begin(); it != mTaskDependencies.end(); ++it )
850 if ( it.value().contains(
task ) )
852 it.value().removeAll(
task );
855 mTaskMutex->unlock();
860void QgsTaskManager::processQueue()
864 mActiveTasks.clear();
865 for ( QMap< long, TaskInfo >::iterator it = mTasks.begin(); it != mTasks.end(); ++it )
870 it.value().createRunnable();
871 mThreadPool->start( it.value().runnable, it.value().priority );
876 mActiveTasks <<
task;
880 bool allFinished = mActiveTasks.isEmpty();
881 mTaskMutex->unlock();
889 if ( prevActiveCount != newActiveCount )
895void QgsTaskManager::cancelDependentTasks(
long taskId )
901 QMap< long, QgsTaskList > taskDependencies = mTaskDependencies;
902 taskDependencies.detach();
903 mTaskMutex->unlock();
905 QMap< long, QgsTaskList >::const_iterator it = taskDependencies.constBegin();
906 for ( ; it != taskDependencies.constEnd(); ++it )
908 if ( it.value().contains( canceledTask ) )
921QgsTaskManager::TaskInfo::TaskInfo(
QgsTask *task,
int priority )
924 , priority( priority )
927void QgsTaskManager::TaskInfo::createRunnable()
929 Q_ASSERT( !runnable );
930 runnable =
new QgsTaskRunnableWrapper( task );
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.
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 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 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 (since QGIS 3.26)
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.