19#include "moc_qgstaskmanager.cpp"
24#include <QtConcurrentRun>
33 , mDescription( name )
34 , mNotStartedMutex( 1 )
36 mNotStartedMutex.acquire();
41 Q_ASSERT_X( mStatus !=
Running,
"delete", QStringLiteral(
"status was %1" ).arg( mStatus ).toLatin1() );
43 mNotFinishedMutex.lock();
44 const auto constMSubTasks = mSubTasks;
45 for (
const SubTask &subTask : constMSubTasks )
49 mNotFinishedMutex.unlock();
50 mNotStartedMutex.release();
60 return mElapsedTime.elapsed();
65 QMutexLocker locker( &mNotFinishedMutex );
66 mNotStartedMutex.release();
68 Q_ASSERT( mStartCount == 1 );
98 mShouldTerminateMutex.lock();
99 mShouldTerminate =
true;
100 mShouldTerminateMutex.unlock();
105 mNotStartedMutex.release();
110 processSubTasksForTermination();
113 const auto constMSubTasks = mSubTasks;
114 for (
const SubTask &subTask : constMSubTasks )
116 subTask.task->cancel();
122 QMutexLocker locker( &mShouldTerminateMutex );
123 return mShouldTerminate;
131 processSubTasksForHold();
134 const auto constMSubTasks = mSubTasks;
135 for (
const SubTask &subTask : constMSubTasks )
137 subTask.task->hold();
150 const auto constMSubTasks = mSubTasks;
151 for (
const SubTask &subTask : constMSubTasks )
153 subTask.task->unhold();
160 mSubTasks << SubTask( subTask, dependencies, subTaskDependency );
167 return _qgis_listQPointerToRaw( mDependentLayers );
173 mNotStartedMutex.acquire();
174 mNotStartedMutex.release();
184 timeout = std::numeric_limits< int >::max();
185 if ( mNotFinishedMutex.tryLock( timeout ) )
187 mNotFinishedMutex.unlock();
188 QCoreApplication::sendPostedEvents(
this );
204void QgsTask::subTaskStatusChanged(
int status )
206 QgsTask *subTask = qobject_cast< QgsTask * >( sender() );
217 processSubTasksForCompletion();
222 processSubTasksForTermination();
226 processSubTasksForHold();
239 if ( !mSubTasks.isEmpty() )
243 double totalProgress = 0.0;
244 const auto constMSubTasks = mSubTasks;
245 for (
const SubTask &subTask : constMSubTasks )
249 totalProgress += 100.0;
253 totalProgress += subTask.task->
progress();
260 double prevProgress = mTotalProgress;
264 if (
static_cast< int >( prevProgress * 10 ) !=
static_cast< int >( mTotalProgress * 10 ) )
268void QgsTask::completed()
271 QMetaObject::invokeMethod(
this,
"processSubTasksForCompletion" );
274void QgsTask::processSubTasksForCompletion()
276 bool subTasksCompleted =
true;
277 const auto constMSubTasks = mSubTasks;
278 for (
const SubTask &subTask : constMSubTasks )
282 subTasksCompleted =
false;
287 if ( mStatus ==
Complete && subTasksCompleted )
302void QgsTask::processSubTasksForTermination()
304 bool subTasksTerminated =
true;
305 const auto constMSubTasks = mSubTasks;
306 for (
const SubTask &subTask : constMSubTasks )
310 subTasksTerminated =
false;
322 else if ( mStatus ==
Terminated && !subTasksTerminated )
329void QgsTask::processSubTasksForHold()
331 bool subTasksRunning =
false;
332 const auto constMSubTasks = mSubTasks;
333 for (
const SubTask &subTask : constMSubTasks )
337 subTasksRunning =
true;
342 if ( mStatus ==
OnHold && !subTasksRunning && mOverallStatus !=
OnHold )
347 else if ( mStatus ==
OnHold && subTasksRunning )
354void QgsTask::terminated()
357 QMetaObject::invokeMethod(
this,
"processSubTasksForTermination" );
363class QgsTaskRunnableWrapper :
public QRunnable
367 explicit QgsTaskRunnableWrapper(
QgsTask *task )
370 setAutoDelete(
true );
395 , mThreadPool( new QThreadPool( this ) )
396 , mTaskMutex( new QRecursiveMutex() )
408 QMap< long, TaskInfo >
tasks = mTasks;
410 mTaskMutex->unlock();
411 QMap< long, TaskInfo >::const_iterator it =
tasks.constBegin();
412 for ( ; it !=
tasks.constEnd(); ++it )
414 cleanupAndDeleteTask( it.value().task );
418 mThreadPool->waitForDone();
433 return addTaskPrivate( definition.
task,
440long QgsTaskManager::addTaskPrivate(
QgsTask *task,
QgsTaskList dependencies,
bool isSubTask,
int priority )
451 this, &QgsTaskManager::layersWillBeRemoved );
454 long taskId = mNextTaskId++;
457 mTasks.insert(
taskId, TaskInfo(
task, priority ) );
465 mParentTasks <<
task;
469 mTaskMutex->unlock();
478 for (
const QgsTask::SubTask &subTask : std::as_const(
task->mSubTasks ) )
480 switch ( subTask.dependency )
491 addTaskPrivate( subTask.task, subTask.dependencies,
true, priority );
499 if ( hasCircularDependencies(
taskId ) )
517 QMutexLocker ml( mTaskMutex );
519 if ( mTasks.contains(
id ) )
520 t = mTasks.value(
id ).task;
526 QMutexLocker ml( mTaskMutex );
527 return QList<QgsTask *>( mParentTasks.begin(), mParentTasks.end() );
532 QMutexLocker ml( mTaskMutex );
533 return mParentTasks.count();
541 QMutexLocker ml( mTaskMutex );
542 const auto iter = mMapTaskPtrToId.constFind(
task );
543 if ( iter != mMapTaskPtrToId.constEnd() )
551 QSet< QgsTask * > parents = mParentTasks;
553 mTaskMutex->unlock();
555 const auto constParents = parents;
565 QMap< long, QgsTaskList >
dependencies = mTaskDependencies;
567 mTaskMutex->unlock();
585 if ( resolveDependencies(
taskId, results ) )
591bool QgsTaskManager::resolveDependencies(
long thisTaskId, QSet<long> &results )
const
594 QMap< long, QgsTaskList >
dependencies = mTaskDependencies;
596 mTaskMutex->unlock();
598 QSet<long> alreadyExploredTaskIds;
599 QStack<long> stackTaskIds;
600 stackTaskIds.push( thisTaskId );
601 while ( !stackTaskIds.isEmpty() )
603 const long currentTaskId = stackTaskIds.pop();
604 alreadyExploredTaskIds.insert( currentTaskId );
610 const auto &constValue = *iter;
614 if ( dependentTaskId >= 0 )
616 if ( thisTaskId == dependentTaskId )
623 results.insert( dependentTaskId );
626 if ( !alreadyExploredTaskIds.contains( dependentTaskId ) )
628 stackTaskIds.push( dependentTaskId );
637bool QgsTaskManager::hasCircularDependencies(
long taskId )
const
640 return !resolveDependencies(
taskId, d );
645 QMutexLocker ml( mTaskMutex );
651 QMutexLocker ml( mTaskMutex );
652 QList< QgsTask * >
tasks;
653 QMap< long, QgsWeakMapLayerPointerList >::const_iterator layerIt = mLayerDependencies.constBegin();
654 for ( ; layerIt != mLayerDependencies.constEnd(); ++layerIt )
656 if ( _qgis_listQPointerToRaw( layerIt.value() ).contains( layer ) )
668 QMutexLocker ml( mTaskMutex );
676 QMutexLocker ml( mTaskMutex );
677 QSet< QgsTask * >
tasks = mActiveTasks;
679 if ( !includeHidden )
681 QSet< QgsTask * > filteredTasks;
682 filteredTasks.reserve(
tasks.size() );
686 filteredTasks.insert(
task );
688 tasks = filteredTasks;
691 return tasks.intersect( mParentTasks ).count();
700void QgsTaskManager::taskProgressChanged(
double progress )
702 QgsTask *
task = qobject_cast< QgsTask * >( sender() );
719void QgsTaskManager::taskStatusChanged(
int status )
721 QgsTask *
task = qobject_cast< QgsTask * >( sender() );
730 QgsTaskRunnableWrapper *runnable = mTasks.value(
id ).runnable;
731 mTaskMutex->unlock();
732 if ( runnable && mThreadPool->tryTake( runnable ) )
735 mTasks[ id ].runnable =
nullptr;
747 cancelDependentTasks(
id );
751 bool isParent = mParentTasks.contains(
task );
752 mTaskMutex->unlock();
753 if ( isParent && !isHidden )
763 cleanupAndDeleteTask(
task );
768void QgsTaskManager::layersWillBeRemoved(
const QList< QgsMapLayer * > &layers )
772 QMap< long, QgsWeakMapLayerPointerList > layerDependencies = mLayerDependencies;
773 layerDependencies.detach();
774 mTaskMutex->unlock();
776 const auto constLayers = layers;
780 for ( QMap< long, QgsWeakMapLayerPointerList >::const_iterator it = layerDependencies.constBegin();
781 it != layerDependencies.constEnd(); ++it )
783 if ( !( _qgis_listQPointerToRaw( it.value() ).contains( layer ) ) )
800bool QgsTaskManager::cleanupAndDeleteTask(
QgsTask *task )
809 QgsTaskRunnableWrapper *runnable = mTasks.value(
id ).runnable;
811 task->disconnect(
this );
814 if ( mTaskDependencies.contains(
id ) )
815 mTaskDependencies.remove(
id );
816 mTaskMutex->unlock();
821 bool isParent = mParentTasks.contains(
task );
822 mParentTasks.remove(
task );
823 mSubTasks.remove(
task );
825 mMapTaskPtrToId.remove(
task );
826 mLayerDependencies.remove(
id );
840 if ( runnable && mThreadPool->tryTake( runnable ) )
843 mTasks[ id ].runnable =
nullptr;
854 for ( QMap< long, QgsTaskList >::iterator it = mTaskDependencies.begin(); it != mTaskDependencies.end(); ++it )
856 if ( it.value().contains(
task ) )
858 it.value().removeAll(
task );
861 mTaskMutex->unlock();
866void QgsTaskManager::processQueue()
870 mActiveTasks.clear();
871 for ( QMap< long, TaskInfo >::iterator it = mTasks.begin(); it != mTasks.end(); ++it )
876 it.value().createRunnable();
877 mThreadPool->start( it.value().runnable, it.value().priority );
882 mActiveTasks <<
task;
886 bool allFinished = mActiveTasks.isEmpty();
887 mTaskMutex->unlock();
895 if ( prevActiveCount != newActiveCount )
901void QgsTaskManager::cancelDependentTasks(
long taskId )
907 QMap< long, QgsTaskList > taskDependencies = mTaskDependencies;
908 taskDependencies.detach();
909 mTaskMutex->unlock();
911 QMap< long, QgsTaskList >::const_iterator it = taskDependencies.constBegin();
912 for ( ; it != taskDependencies.constEnd(); ++it )
914 if ( it.value().contains( canceledTask ) )
927QgsTaskManager::TaskInfo::TaskInfo(
QgsTask *task,
int priority )
930 , priority( priority )
933void QgsTaskManager::TaskInfo::createRunnable()
935 Q_ASSERT( !runnable );
936 runnable =
new QgsTaskRunnableWrapper( task );
958 if ( mShouldTerminate )
961 [
this, i](
double subTaskProgress )
963 mProgress = 100.0 * ( double( i ) + subTaskProgress / 100.0 ) /
double(
mSubTasksSerial.size() );
966 if ( !subTask->
run() )
968 subTask->completed();
969 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.