23#include <QtConcurrentRun>
32 , mDescription( name )
33 , mNotStartedMutex( 1 )
35 mNotStartedMutex.acquire();
40 Q_ASSERT_X( mStatus !=
Running,
"delete", QStringLiteral(
"status was %1" ).arg( mStatus ).toLatin1() );
42 mNotFinishedMutex.lock();
43 const auto constMSubTasks = mSubTasks;
44 for (
const SubTask &subTask : constMSubTasks )
48 mNotFinishedMutex.unlock();
49 mNotStartedMutex.release();
59 return mElapsedTime.elapsed();
64 QMutexLocker locker( &mNotFinishedMutex );
65 mNotStartedMutex.release();
67 Q_ASSERT( mStartCount == 1 );
97 mShouldTerminateMutex.lock();
98 mShouldTerminate =
true;
99 mShouldTerminateMutex.unlock();
104 mNotStartedMutex.release();
109 processSubTasksForTermination();
112 const auto constMSubTasks = mSubTasks;
113 for (
const SubTask &subTask : constMSubTasks )
115 subTask.task->cancel();
121 QMutexLocker locker( &mShouldTerminateMutex );
122 return mShouldTerminate;
130 processSubTasksForHold();
133 const auto constMSubTasks = mSubTasks;
134 for (
const SubTask &subTask : constMSubTasks )
136 subTask.task->hold();
149 const auto constMSubTasks = mSubTasks;
150 for (
const SubTask &subTask : constMSubTasks )
152 subTask.task->unhold();
159 mSubTasks << SubTask( subTask, dependencies, subTaskDependency );
166 return _qgis_listQPointerToRaw( mDependentLayers );
172 mNotStartedMutex.acquire();
182 timeout = std::numeric_limits< int >::max();
183 if ( mNotFinishedMutex.tryLock( timeout ) )
185 mNotFinishedMutex.unlock();
186 QCoreApplication::sendPostedEvents(
this );
202void QgsTask::subTaskStatusChanged(
int status )
204 QgsTask *subTask = qobject_cast< QgsTask * >( sender() );
215 processSubTasksForCompletion();
220 processSubTasksForTermination();
224 processSubTasksForHold();
237 if ( !mSubTasks.isEmpty() )
241 double totalProgress = 0.0;
242 const auto constMSubTasks = mSubTasks;
243 for (
const SubTask &subTask : constMSubTasks )
247 totalProgress += 100.0;
251 totalProgress += subTask.task->
progress();
258 double prevProgress = mTotalProgress;
262 if (
static_cast< int >( prevProgress * 10 ) !=
static_cast< int >( mTotalProgress * 10 ) )
266void QgsTask::completed()
269 QMetaObject::invokeMethod(
this,
"processSubTasksForCompletion" );
272void QgsTask::processSubTasksForCompletion()
274 bool subTasksCompleted =
true;
275 const auto constMSubTasks = mSubTasks;
276 for (
const SubTask &subTask : constMSubTasks )
280 subTasksCompleted =
false;
285 if ( mStatus ==
Complete && subTasksCompleted )
300void QgsTask::processSubTasksForTermination()
302 bool subTasksTerminated =
true;
303 const auto constMSubTasks = mSubTasks;
304 for (
const SubTask &subTask : constMSubTasks )
308 subTasksTerminated =
false;
320 else if ( mStatus ==
Terminated && !subTasksTerminated )
327void QgsTask::processSubTasksForHold()
329 bool subTasksRunning =
false;
330 const auto constMSubTasks = mSubTasks;
331 for (
const SubTask &subTask : constMSubTasks )
335 subTasksRunning =
true;
340 if ( mStatus ==
OnHold && !subTasksRunning && mOverallStatus !=
OnHold )
345 else if ( mStatus ==
OnHold && subTasksRunning )
352void QgsTask::terminated()
355 QMetaObject::invokeMethod(
this,
"processSubTasksForTermination" );
361class QgsTaskRunnableWrapper :
public QRunnable
365 explicit QgsTaskRunnableWrapper(
QgsTask *task )
368 setAutoDelete(
true );
393 , mThreadPool( new QThreadPool( this ) )
394 , mTaskMutex( new QRecursiveMutex() )
406 QMap< long, TaskInfo >
tasks = mTasks;
408 mTaskMutex->unlock();
409 QMap< long, TaskInfo >::const_iterator it =
tasks.constBegin();
410 for ( ; it !=
tasks.constEnd(); ++it )
412 cleanupAndDeleteTask( it.value().task );
416 mThreadPool->waitForDone();
431 return addTaskPrivate( definition.
task,
438long QgsTaskManager::addTaskPrivate(
QgsTask *task,
QgsTaskList dependencies,
bool isSubTask,
int priority )
449 this, &QgsTaskManager::layersWillBeRemoved );
452 long taskId = mNextTaskId++;
455 mTasks.insert(
taskId, TaskInfo(
task, priority ) );
463 mParentTasks <<
task;
467 mTaskMutex->unlock();
476 for (
const QgsTask::SubTask &subTask : std::as_const(
task->mSubTasks ) )
478 switch ( subTask.dependency )
489 addTaskPrivate( subTask.task, subTask.dependencies,
true, priority );
497 if ( hasCircularDependencies(
taskId ) )
515 QMutexLocker ml( mTaskMutex );
517 if ( mTasks.contains(
id ) )
518 t = mTasks.value(
id ).task;
524 QMutexLocker ml( mTaskMutex );
525 return QList<QgsTask *>( mParentTasks.begin(), mParentTasks.end() );
530 QMutexLocker ml( mTaskMutex );
531 return mParentTasks.count();
539 QMutexLocker ml( mTaskMutex );
540 const auto iter = mMapTaskPtrToId.constFind(
task );
541 if ( iter != mMapTaskPtrToId.constEnd() )
549 QSet< QgsTask * > parents = mParentTasks;
551 mTaskMutex->unlock();
553 const auto constParents = parents;
563 QMap< long, QgsTaskList >
dependencies = mTaskDependencies;
565 mTaskMutex->unlock();
583 if ( resolveDependencies(
taskId, results ) )
589bool QgsTaskManager::resolveDependencies(
long thisTaskId, QSet<long> &results )
const
592 QMap< long, QgsTaskList >
dependencies = mTaskDependencies;
594 mTaskMutex->unlock();
596 QSet<long> alreadyExploredTaskIds;
597 QStack<long> stackTaskIds;
598 stackTaskIds.push( thisTaskId );
599 while ( !stackTaskIds.isEmpty() )
601 const long currentTaskId = stackTaskIds.pop();
602 alreadyExploredTaskIds.insert( currentTaskId );
608 const auto &constValue = *iter;
612 if ( dependentTaskId >= 0 )
614 if ( thisTaskId == dependentTaskId )
621 results.insert( dependentTaskId );
624 if ( !alreadyExploredTaskIds.contains( dependentTaskId ) )
626 stackTaskIds.push( dependentTaskId );
635bool QgsTaskManager::hasCircularDependencies(
long taskId )
const
638 return !resolveDependencies(
taskId, d );
643 QMutexLocker ml( mTaskMutex );
649 QMutexLocker ml( mTaskMutex );
650 QList< QgsTask * >
tasks;
651 QMap< long, QgsWeakMapLayerPointerList >::const_iterator layerIt = mLayerDependencies.constBegin();
652 for ( ; layerIt != mLayerDependencies.constEnd(); ++layerIt )
654 if ( _qgis_listQPointerToRaw( layerIt.value() ).contains( layer ) )
666 QMutexLocker ml( mTaskMutex );
674 QMutexLocker ml( mTaskMutex );
675 QSet< QgsTask * >
tasks = mActiveTasks;
677 if ( !includeHidden )
679 QSet< QgsTask * > filteredTasks;
680 filteredTasks.reserve(
tasks.size() );
684 filteredTasks.insert(
task );
686 tasks = filteredTasks;
689 return tasks.intersect( mParentTasks ).count();
698void QgsTaskManager::taskProgressChanged(
double progress )
700 QgsTask *
task = qobject_cast< QgsTask * >( sender() );
717void QgsTaskManager::taskStatusChanged(
int status )
719 QgsTask *
task = qobject_cast< QgsTask * >( sender() );
728 QgsTaskRunnableWrapper *runnable = mTasks.value(
id ).runnable;
729 mTaskMutex->unlock();
730 if ( runnable && mThreadPool->tryTake( runnable ) )
733 mTasks[ id ].runnable =
nullptr;
745 cancelDependentTasks(
id );
749 bool isParent = mParentTasks.contains(
task );
750 mTaskMutex->unlock();
751 if ( isParent && !isHidden )
761 cleanupAndDeleteTask(
task );
766void QgsTaskManager::layersWillBeRemoved(
const QList< QgsMapLayer * > &layers )
770 QMap< long, QgsWeakMapLayerPointerList > layerDependencies = mLayerDependencies;
771 layerDependencies.detach();
772 mTaskMutex->unlock();
774 const auto constLayers = layers;
778 for ( QMap< long, QgsWeakMapLayerPointerList >::const_iterator it = layerDependencies.constBegin();
779 it != layerDependencies.constEnd(); ++it )
781 if ( !( _qgis_listQPointerToRaw( it.value() ).contains( layer ) ) )
798bool QgsTaskManager::cleanupAndDeleteTask(
QgsTask *task )
807 QgsTaskRunnableWrapper *runnable = mTasks.value(
id ).runnable;
809 task->disconnect(
this );
812 if ( mTaskDependencies.contains(
id ) )
813 mTaskDependencies.remove(
id );
814 mTaskMutex->unlock();
819 bool isParent = mParentTasks.contains(
task );
820 mParentTasks.remove(
task );
821 mSubTasks.remove(
task );
823 mMapTaskPtrToId.remove(
task );
824 mLayerDependencies.remove(
id );
838 if ( runnable && mThreadPool->tryTake( runnable ) )
841 mTasks[ id ].runnable =
nullptr;
852 for ( QMap< long, QgsTaskList >::iterator it = mTaskDependencies.begin(); it != mTaskDependencies.end(); ++it )
854 if ( it.value().contains(
task ) )
856 it.value().removeAll(
task );
859 mTaskMutex->unlock();
864void QgsTaskManager::processQueue()
868 mActiveTasks.clear();
869 for ( QMap< long, TaskInfo >::iterator it = mTasks.begin(); it != mTasks.end(); ++it )
874 it.value().createRunnable();
875 mThreadPool->start( it.value().runnable, it.value().priority );
880 mActiveTasks <<
task;
884 bool allFinished = mActiveTasks.isEmpty();
885 mTaskMutex->unlock();
893 if ( prevActiveCount != newActiveCount )
899void QgsTaskManager::cancelDependentTasks(
long taskId )
905 QMap< long, QgsTaskList > taskDependencies = mTaskDependencies;
906 taskDependencies.detach();
907 mTaskMutex->unlock();
909 QMap< long, QgsTaskList >::const_iterator it = taskDependencies.constBegin();
910 for ( ; it != taskDependencies.constEnd(); ++it )
912 if ( it.value().contains( canceledTask ) )
925QgsTaskManager::TaskInfo::TaskInfo(
QgsTask *task,
int priority )
928 , priority( priority )
931void QgsTaskManager::TaskInfo::createRunnable()
933 Q_ASSERT( !runnable );
934 runnable =
new QgsTaskRunnableWrapper( task );
956 if ( mShouldTerminate )
959 [
this, i](
double subTaskProgress )
961 mProgress = 100.0 * ( double( i ) + subTaskProgress / 100.0 ) /
double(
mSubTasksSerial.size() );
964 if ( !subTask->
run() )
966 subTask->completed();
967 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 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.
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.