27#include <QtConcurrentRun>
29#include "moc_qgstaskmanager.cpp"
31using namespace Qt::StringLiterals;
39 , mDescription( name )
40 , mNotStartedMutex( 1 )
42 mNotStartedMutex.acquire();
47 Q_ASSERT_X( mStatus !=
Running,
"delete", u
"status was %1"_s.arg( mStatus ).toLatin1() );
49 mNotFinishedMutex.lock();
50 const auto constMSubTasks = mSubTasks;
51 for (
const SubTask &subTask : constMSubTasks )
55 mNotFinishedMutex.unlock();
56 mNotStartedMutex.release();
66 return mElapsedTime.elapsed();
71 QMutexLocker locker( &mNotFinishedMutex );
72 mNotStartedMutex.release();
74 Q_ASSERT( mStartCount == 1 );
104 mShouldTerminateMutex.lock();
105 mShouldTerminate =
true;
106 mShouldTerminateMutex.unlock();
111 mNotStartedMutex.release();
116 processSubTasksForTermination();
119 const auto constMSubTasks = mSubTasks;
120 for (
const SubTask &subTask : constMSubTasks )
122 subTask.task->cancel();
128 QMutexLocker locker( &mShouldTerminateMutex );
129 return mShouldTerminate;
137 processSubTasksForHold();
140 const auto constMSubTasks = mSubTasks;
141 for (
const SubTask &subTask : constMSubTasks )
143 subTask.task->hold();
156 const auto constMSubTasks = mSubTasks;
157 for (
const SubTask &subTask : constMSubTasks )
159 subTask.task->unhold();
166 mSubTasks << SubTask( subTask, dependencies, subTaskDependency );
173 return _qgis_listQPointerToRaw( mDependentLayers );
179 mNotStartedMutex.acquire();
180 mNotStartedMutex.release();
190 timeout = std::numeric_limits< int >::max();
191 if ( mNotFinishedMutex.tryLock( timeout ) )
193 mNotFinishedMutex.unlock();
194 QCoreApplication::sendPostedEvents(
this );
210void QgsTask::subTaskStatusChanged(
int status )
212 QgsTask *subTask = qobject_cast< QgsTask * >( sender() );
223 processSubTasksForCompletion();
228 processSubTasksForTermination();
232 processSubTasksForHold();
245 if ( !mSubTasks.isEmpty() )
249 double totalProgress = 0.0;
250 const auto constMSubTasks = mSubTasks;
251 for (
const SubTask &subTask : constMSubTasks )
255 totalProgress += 100.0;
259 totalProgress += subTask.task->
progress();
266 double prevProgress = mTotalProgress;
270 if (
static_cast< int >( prevProgress * 10 ) !=
static_cast< int >( mTotalProgress * 10 ) )
274void QgsTask::completed()
277 QMetaObject::invokeMethod(
this,
"processSubTasksForCompletion" );
280void QgsTask::processSubTasksForCompletion()
282 bool subTasksCompleted =
true;
283 const auto constMSubTasks = mSubTasks;
284 for (
const SubTask &subTask : constMSubTasks )
288 subTasksCompleted =
false;
293 if ( mStatus ==
Complete && subTasksCompleted )
308void QgsTask::processSubTasksForTermination()
310 bool subTasksTerminated =
true;
311 const auto constMSubTasks = mSubTasks;
312 for (
const SubTask &subTask : constMSubTasks )
316 subTasksTerminated =
false;
328 else if ( mStatus ==
Terminated && !subTasksTerminated )
335void QgsTask::processSubTasksForHold()
337 bool subTasksRunning =
false;
338 const auto constMSubTasks = mSubTasks;
339 for (
const SubTask &subTask : constMSubTasks )
343 subTasksRunning =
true;
348 if ( mStatus ==
OnHold && !subTasksRunning && mOverallStatus !=
OnHold )
353 else if ( mStatus ==
OnHold && subTasksRunning )
360void QgsTask::terminated()
363 QMetaObject::invokeMethod(
this,
"processSubTasksForTermination" );
369class QgsTaskRunnableWrapper :
public QRunnable
373 explicit QgsTaskRunnableWrapper( QgsTask *task )
376 setAutoDelete(
true );
387 QgsTask *mTask =
nullptr;
401 , mThreadPool( new QThreadPool( this ) )
402 , mTaskMutex( new QRecursiveMutex() )
414 QMap< long, TaskInfo >
tasks = mTasks;
416 mTaskMutex->unlock();
417 QMap< long, TaskInfo >::const_iterator it =
tasks.constBegin();
418 for ( ; it !=
tasks.constEnd(); ++it )
420 cleanupAndDeleteTask( it.value().task );
424 mThreadPool->waitForDone();
439 return addTaskPrivate( definition.
task,
446long QgsTaskManager::addTaskPrivate(
QgsTask *task,
QgsTaskList dependencies,
bool isSubTask,
int priority )
457 this, &QgsTaskManager::layersWillBeRemoved );
460 long taskId = mNextTaskId++;
463 mTasks.insert(
taskId, TaskInfo(
task, priority ) );
471 mParentTasks <<
task;
473 if ( !
task->dependentLayers().isEmpty() )
474 mLayerDependencies.insert(
taskId, _qgis_listRawToQPointer(
task->dependentLayers() ) );
475 mTaskMutex->unlock();
484 for (
const QgsTask::SubTask &subTask : std::as_const(
task->mSubTasks ) )
486 switch ( subTask.dependency )
497 addTaskPrivate( subTask.task, subTask.dependencies,
true, priority );
505 if ( hasCircularDependencies(
taskId ) )
523 QMutexLocker ml( mTaskMutex );
525 if ( mTasks.contains(
id ) )
526 t = mTasks.value(
id ).task;
532 QMutexLocker ml( mTaskMutex );
533 return QList<QgsTask *>( mParentTasks.begin(), mParentTasks.end() );
538 QMutexLocker ml( mTaskMutex );
539 return mParentTasks.count();
547 QMutexLocker ml( mTaskMutex );
548 const auto iter = mMapTaskPtrToId.constFind(
task );
549 if ( iter != mMapTaskPtrToId.constEnd() )
557 QSet< QgsTask * > parents = mParentTasks;
559 mTaskMutex->unlock();
561 const auto constParents = parents;
571 QMap< long, QgsTaskList >
dependencies = mTaskDependencies;
573 mTaskMutex->unlock();
591 if ( resolveDependencies(
taskId, results ) )
597bool QgsTaskManager::resolveDependencies(
long thisTaskId, QSet<long> &results )
const
600 QMap< long, QgsTaskList >
dependencies = mTaskDependencies;
602 mTaskMutex->unlock();
604 QSet<long> alreadyExploredTaskIds;
605 QStack<long> stackTaskIds;
606 stackTaskIds.push( thisTaskId );
607 while ( !stackTaskIds.isEmpty() )
609 const long currentTaskId = stackTaskIds.pop();
610 alreadyExploredTaskIds.insert( currentTaskId );
616 const auto &constValue = *iter;
620 if ( dependentTaskId >= 0 )
622 if ( thisTaskId == dependentTaskId )
629 results.insert( dependentTaskId );
632 if ( !alreadyExploredTaskIds.contains( dependentTaskId ) )
634 stackTaskIds.push( dependentTaskId );
643bool QgsTaskManager::hasCircularDependencies(
long taskId )
const
646 return !resolveDependencies(
taskId, d );
651 QMutexLocker ml( mTaskMutex );
657 QMutexLocker ml( mTaskMutex );
658 QList< QgsTask * >
tasks;
659 QMap< long, QgsWeakMapLayerPointerList >::const_iterator layerIt = mLayerDependencies.constBegin();
660 for ( ; layerIt != mLayerDependencies.constEnd(); ++layerIt )
662 if ( _qgis_listQPointerToRaw( layerIt.value() ).contains( layer ) )
674 QMutexLocker ml( mTaskMutex );
682 QMutexLocker ml( mTaskMutex );
683 QSet< QgsTask * >
tasks = mActiveTasks;
685 if ( !includeHidden )
687 QSet< QgsTask * > filteredTasks;
688 filteredTasks.reserve(
tasks.size() );
692 filteredTasks.insert(
task );
694 tasks = filteredTasks;
697 return tasks.intersect( mParentTasks ).count();
706void QgsTaskManager::taskProgressChanged(
double progress )
708 QgsTask *
task = qobject_cast< QgsTask * >( sender() );
725void QgsTaskManager::taskStatusChanged(
int status )
727 QgsTask *
task = qobject_cast< QgsTask * >( sender() );
736 QgsTaskRunnableWrapper *runnable = mTasks.value(
id ).runnable;
737 mTaskMutex->unlock();
738 if ( runnable && mThreadPool->tryTake( runnable ) )
741 mTasks[ id ].runnable =
nullptr;
747 task->finished( result );
753 cancelDependentTasks(
id );
757 bool isParent = mParentTasks.contains(
task );
758 mTaskMutex->unlock();
759 if ( isParent && !isHidden )
769 cleanupAndDeleteTask(
task );
774void QgsTaskManager::layersWillBeRemoved(
const QList< QgsMapLayer * > &layers )
778 QMap< long, QgsWeakMapLayerPointerList > layerDependencies = mLayerDependencies;
779 layerDependencies.detach();
780 mTaskMutex->unlock();
782 const auto constLayers = layers;
783 for ( QgsMapLayer *layer : constLayers )
786 for ( QMap< long, QgsWeakMapLayerPointerList >::const_iterator it = layerDependencies.constBegin();
787 it != layerDependencies.constEnd(); ++it )
789 if ( !( _qgis_listQPointerToRaw( it.value() ).contains( layer ) ) )
795 QgsTask *dependentTask =
task( it.key() );
806bool QgsTaskManager::cleanupAndDeleteTask(
QgsTask *task )
815 QgsTaskRunnableWrapper *runnable = mTasks.value(
id ).runnable;
817 task->disconnect(
this );
820 if ( mTaskDependencies.contains(
id ) )
821 mTaskDependencies.remove(
id );
822 mTaskMutex->unlock();
827 bool isParent = mParentTasks.contains(
task );
828 mParentTasks.remove(
task );
829 mSubTasks.remove(
task );
831 mMapTaskPtrToId.remove(
task );
832 mLayerDependencies.remove(
id );
846 if ( runnable && mThreadPool->tryTake( runnable ) )
849 mTasks[ id ].runnable =
nullptr;
860 for ( QMap< long, QgsTaskList >::iterator it = mTaskDependencies.begin(); it != mTaskDependencies.end(); ++it )
862 if ( it.value().contains(
task ) )
864 it.value().removeAll(
task );
867 mTaskMutex->unlock();
872void QgsTaskManager::processQueue()
876 mActiveTasks.clear();
877 for ( QMap< long, TaskInfo >::iterator it = mTasks.begin(); it != mTasks.end(); ++it )
879 QgsTask *
task = it.value().task;
882 it.value().createRunnable();
883 mThreadPool->start( it.value().runnable, it.value().priority );
888 mActiveTasks <<
task;
892 bool allFinished = mActiveTasks.isEmpty();
893 mTaskMutex->unlock();
901 if ( prevActiveCount != newActiveCount )
907void QgsTaskManager::cancelDependentTasks(
long taskId )
913 QMap< long, QgsTaskList > taskDependencies = mTaskDependencies;
914 taskDependencies.detach();
915 mTaskMutex->unlock();
917 QMap< long, QgsTaskList >::const_iterator it = taskDependencies.constBegin();
918 for ( ; it != taskDependencies.constEnd(); ++it )
920 if ( it.value().contains( canceledTask ) )
926 QgsTask *dependentTask =
task( it.key() );
933QgsTaskManager::TaskInfo::TaskInfo(
QgsTask *task,
int priority )
936 , priority( priority )
939void QgsTaskManager::TaskInfo::createRunnable()
941 Q_ASSERT( !runnable );
942 runnable =
new QgsTaskRunnableWrapper( task );
964 if ( mShouldTerminate )
967 [
this, i](
double subTaskProgress )
969 mProgress = 100.0 * ( double( i ) + subTaskProgress / 100.0 ) /
double(
mSubTasksSerial.size() );
972 if ( !subTask->
run() )
974 subTask->completed();
975 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.