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();
173 mNotStartedMutex.release();
183 timeout = std::numeric_limits< int >::max();
184 if ( mNotFinishedMutex.tryLock( timeout ) )
186 mNotFinishedMutex.unlock();
187 QCoreApplication::sendPostedEvents(
this );
203void QgsTask::subTaskStatusChanged(
int status )
205 QgsTask *subTask = qobject_cast< QgsTask * >( sender() );
216 processSubTasksForCompletion();
221 processSubTasksForTermination();
225 processSubTasksForHold();
238 if ( !mSubTasks.isEmpty() )
242 double totalProgress = 0.0;
243 const auto constMSubTasks = mSubTasks;
244 for (
const SubTask &subTask : constMSubTasks )
248 totalProgress += 100.0;
252 totalProgress += subTask.task->
progress();
259 double prevProgress = mTotalProgress;
263 if (
static_cast< int >( prevProgress * 10 ) !=
static_cast< int >( mTotalProgress * 10 ) )
267void QgsTask::completed()
270 QMetaObject::invokeMethod(
this,
"processSubTasksForCompletion" );
273void QgsTask::processSubTasksForCompletion()
275 bool subTasksCompleted =
true;
276 const auto constMSubTasks = mSubTasks;
277 for (
const SubTask &subTask : constMSubTasks )
281 subTasksCompleted =
false;
286 if ( mStatus ==
Complete && subTasksCompleted )
301void QgsTask::processSubTasksForTermination()
303 bool subTasksTerminated =
true;
304 const auto constMSubTasks = mSubTasks;
305 for (
const SubTask &subTask : constMSubTasks )
309 subTasksTerminated =
false;
321 else if ( mStatus ==
Terminated && !subTasksTerminated )
328void QgsTask::processSubTasksForHold()
330 bool subTasksRunning =
false;
331 const auto constMSubTasks = mSubTasks;
332 for (
const SubTask &subTask : constMSubTasks )
336 subTasksRunning =
true;
341 if ( mStatus ==
OnHold && !subTasksRunning && mOverallStatus !=
OnHold )
346 else if ( mStatus ==
OnHold && subTasksRunning )
353void QgsTask::terminated()
356 QMetaObject::invokeMethod(
this,
"processSubTasksForTermination" );
362class QgsTaskRunnableWrapper :
public QRunnable
366 explicit QgsTaskRunnableWrapper(
QgsTask *task )
369 setAutoDelete(
true );
394 , mThreadPool( new QThreadPool( this ) )
395 , mTaskMutex( new QRecursiveMutex() )
407 QMap< long, TaskInfo >
tasks = mTasks;
409 mTaskMutex->unlock();
410 QMap< long, TaskInfo >::const_iterator it =
tasks.constBegin();
411 for ( ; it !=
tasks.constEnd(); ++it )
413 cleanupAndDeleteTask( it.value().task );
417 mThreadPool->waitForDone();
432 return addTaskPrivate( definition.
task,
439long QgsTaskManager::addTaskPrivate(
QgsTask *task,
QgsTaskList dependencies,
bool isSubTask,
int priority )
450 this, &QgsTaskManager::layersWillBeRemoved );
453 long taskId = mNextTaskId++;
456 mTasks.insert(
taskId, TaskInfo(
task, priority ) );
464 mParentTasks <<
task;
468 mTaskMutex->unlock();
477 for (
const QgsTask::SubTask &subTask : std::as_const(
task->mSubTasks ) )
479 switch ( subTask.dependency )
490 addTaskPrivate( subTask.task, subTask.dependencies,
true, priority );
498 if ( hasCircularDependencies(
taskId ) )
516 QMutexLocker ml( mTaskMutex );
518 if ( mTasks.contains(
id ) )
519 t = mTasks.value(
id ).task;
525 QMutexLocker ml( mTaskMutex );
526 return QList<QgsTask *>( mParentTasks.begin(), mParentTasks.end() );
531 QMutexLocker ml( mTaskMutex );
532 return mParentTasks.count();
540 QMutexLocker ml( mTaskMutex );
541 const auto iter = mMapTaskPtrToId.constFind(
task );
542 if ( iter != mMapTaskPtrToId.constEnd() )
550 QSet< QgsTask * > parents = mParentTasks;
552 mTaskMutex->unlock();
554 const auto constParents = parents;
564 QMap< long, QgsTaskList >
dependencies = mTaskDependencies;
566 mTaskMutex->unlock();
584 if ( resolveDependencies(
taskId, results ) )
590bool QgsTaskManager::resolveDependencies(
long thisTaskId, QSet<long> &results )
const
593 QMap< long, QgsTaskList >
dependencies = mTaskDependencies;
595 mTaskMutex->unlock();
597 QSet<long> alreadyExploredTaskIds;
598 QStack<long> stackTaskIds;
599 stackTaskIds.push( thisTaskId );
600 while ( !stackTaskIds.isEmpty() )
602 const long currentTaskId = stackTaskIds.pop();
603 alreadyExploredTaskIds.insert( currentTaskId );
609 const auto &constValue = *iter;
613 if ( dependentTaskId >= 0 )
615 if ( thisTaskId == dependentTaskId )
622 results.insert( dependentTaskId );
625 if ( !alreadyExploredTaskIds.contains( dependentTaskId ) )
627 stackTaskIds.push( dependentTaskId );
636bool QgsTaskManager::hasCircularDependencies(
long taskId )
const
639 return !resolveDependencies(
taskId, d );
644 QMutexLocker ml( mTaskMutex );
650 QMutexLocker ml( mTaskMutex );
651 QList< QgsTask * >
tasks;
652 QMap< long, QgsWeakMapLayerPointerList >::const_iterator layerIt = mLayerDependencies.constBegin();
653 for ( ; layerIt != mLayerDependencies.constEnd(); ++layerIt )
655 if ( _qgis_listQPointerToRaw( layerIt.value() ).contains( layer ) )
667 QMutexLocker ml( mTaskMutex );
675 QMutexLocker ml( mTaskMutex );
676 QSet< QgsTask * >
tasks = mActiveTasks;
678 if ( !includeHidden )
680 QSet< QgsTask * > filteredTasks;
681 filteredTasks.reserve(
tasks.size() );
685 filteredTasks.insert(
task );
687 tasks = filteredTasks;
690 return tasks.intersect( mParentTasks ).count();
699void QgsTaskManager::taskProgressChanged(
double progress )
701 QgsTask *
task = qobject_cast< QgsTask * >( sender() );
718void QgsTaskManager::taskStatusChanged(
int status )
720 QgsTask *
task = qobject_cast< QgsTask * >( sender() );
729 QgsTaskRunnableWrapper *runnable = mTasks.value(
id ).runnable;
730 mTaskMutex->unlock();
731 if ( runnable && mThreadPool->tryTake( runnable ) )
734 mTasks[ id ].runnable =
nullptr;
746 cancelDependentTasks(
id );
750 bool isParent = mParentTasks.contains(
task );
751 mTaskMutex->unlock();
752 if ( isParent && !isHidden )
762 cleanupAndDeleteTask(
task );
767void QgsTaskManager::layersWillBeRemoved(
const QList< QgsMapLayer * > &layers )
771 QMap< long, QgsWeakMapLayerPointerList > layerDependencies = mLayerDependencies;
772 layerDependencies.detach();
773 mTaskMutex->unlock();
775 const auto constLayers = layers;
779 for ( QMap< long, QgsWeakMapLayerPointerList >::const_iterator it = layerDependencies.constBegin();
780 it != layerDependencies.constEnd(); ++it )
782 if ( !( _qgis_listQPointerToRaw( it.value() ).contains( layer ) ) )
799bool QgsTaskManager::cleanupAndDeleteTask(
QgsTask *task )
808 QgsTaskRunnableWrapper *runnable = mTasks.value(
id ).runnable;
810 task->disconnect(
this );
813 if ( mTaskDependencies.contains(
id ) )
814 mTaskDependencies.remove(
id );
815 mTaskMutex->unlock();
820 bool isParent = mParentTasks.contains(
task );
821 mParentTasks.remove(
task );
822 mSubTasks.remove(
task );
824 mMapTaskPtrToId.remove(
task );
825 mLayerDependencies.remove(
id );
839 if ( runnable && mThreadPool->tryTake( runnable ) )
842 mTasks[ id ].runnable =
nullptr;
853 for ( QMap< long, QgsTaskList >::iterator it = mTaskDependencies.begin(); it != mTaskDependencies.end(); ++it )
855 if ( it.value().contains(
task ) )
857 it.value().removeAll(
task );
860 mTaskMutex->unlock();
865void QgsTaskManager::processQueue()
869 mActiveTasks.clear();
870 for ( QMap< long, TaskInfo >::iterator it = mTasks.begin(); it != mTasks.end(); ++it )
875 it.value().createRunnable();
876 mThreadPool->start( it.value().runnable, it.value().priority );
881 mActiveTasks <<
task;
885 bool allFinished = mActiveTasks.isEmpty();
886 mTaskMutex->unlock();
894 if ( prevActiveCount != newActiveCount )
900void QgsTaskManager::cancelDependentTasks(
long taskId )
906 QMap< long, QgsTaskList > taskDependencies = mTaskDependencies;
907 taskDependencies.detach();
908 mTaskMutex->unlock();
910 QMap< long, QgsTaskList >::const_iterator it = taskDependencies.constBegin();
911 for ( ; it != taskDependencies.constEnd(); ++it )
913 if ( it.value().contains( canceledTask ) )
926QgsTaskManager::TaskInfo::TaskInfo(
QgsTask *task,
int priority )
929 , priority( priority )
932void QgsTaskManager::TaskInfo::createRunnable()
934 Q_ASSERT( !runnable );
935 runnable =
new QgsTaskRunnableWrapper( task );
957 if ( mShouldTerminate )
960 [
this, i](
double subTaskProgress )
962 mProgress = 100.0 * ( double( i ) + subTaskProgress / 100.0 ) /
double(
mSubTasksSerial.size() );
965 if ( !subTask->
run() )
967 subTask->completed();
968 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.