28#include "moc_qgstaskmanager.cpp"
30using namespace Qt::StringLiterals;
38 , mDescription( name )
39 , mNotStartedMutex( 1 )
41 mNotStartedMutex.acquire();
46 Q_ASSERT_X( mStatus !=
Running,
"delete", u
"status was %1"_s.arg( mStatus ).toLatin1() );
48 mNotFinishedMutex.lock();
49 const auto constMSubTasks = mSubTasks;
50 for (
const SubTask &subTask : constMSubTasks )
54 mNotFinishedMutex.unlock();
55 mNotStartedMutex.release();
65 return mElapsedTime.elapsed();
70 QMutexLocker locker( &mNotFinishedMutex );
71 mNotStartedMutex.release();
73 Q_ASSERT( mStartCount == 1 );
103 mShouldTerminateMutex.lock();
104 mShouldTerminate =
true;
105 mShouldTerminateMutex.unlock();
110 mNotStartedMutex.release();
115 processSubTasksForTermination();
118 const auto constMSubTasks = mSubTasks;
119 for (
const SubTask &subTask : constMSubTasks )
121 subTask.task->cancel();
127 QMutexLocker locker( &mShouldTerminateMutex );
128 return mShouldTerminate;
136 processSubTasksForHold();
139 const auto constMSubTasks = mSubTasks;
140 for (
const SubTask &subTask : constMSubTasks )
142 subTask.task->hold();
155 const auto constMSubTasks = mSubTasks;
156 for (
const SubTask &subTask : constMSubTasks )
158 subTask.task->unhold();
164 mSubTasks << SubTask( subTask, dependencies, subTaskDependency );
171 return _qgis_listQPointerToRaw( mDependentLayers );
177 mNotStartedMutex.acquire();
178 mNotStartedMutex.release();
188 timeout = std::numeric_limits< int >::max();
189 if ( mNotFinishedMutex.tryLock( timeout ) )
191 mNotFinishedMutex.unlock();
192 QCoreApplication::sendPostedEvents(
this );
208void QgsTask::subTaskStatusChanged(
int status )
210 QgsTask *subTask = qobject_cast< QgsTask * >( sender() );
221 processSubTasksForCompletion();
226 processSubTasksForTermination();
230 processSubTasksForHold();
243 if ( !mSubTasks.isEmpty() )
247 double totalProgress = 0.0;
248 const auto constMSubTasks = mSubTasks;
249 for (
const SubTask &subTask : constMSubTasks )
253 totalProgress += 100.0;
257 totalProgress += subTask.task->
progress();
264 double prevProgress = mTotalProgress;
268 if (
static_cast< int >( prevProgress * 10 ) !=
static_cast< int >( mTotalProgress * 10 ) )
272void QgsTask::completed()
275 QMetaObject::invokeMethod(
this, &QgsTask::processSubTasksForCompletion );
278void QgsTask::processSubTasksForCompletion()
280 bool subTasksCompleted =
true;
281 const auto constMSubTasks = mSubTasks;
282 for (
const SubTask &subTask : constMSubTasks )
286 subTasksCompleted =
false;
291 if ( mStatus ==
Complete && subTasksCompleted )
306void QgsTask::processSubTasksForTermination()
308 bool subTasksTerminated =
true;
309 const auto constMSubTasks = mSubTasks;
310 for (
const SubTask &subTask : constMSubTasks )
314 subTasksTerminated =
false;
326 else if ( mStatus ==
Terminated && !subTasksTerminated )
333void QgsTask::processSubTasksForHold()
335 bool subTasksRunning =
false;
336 const auto constMSubTasks = mSubTasks;
337 for (
const SubTask &subTask : constMSubTasks )
341 subTasksRunning =
true;
346 if ( mStatus ==
OnHold && !subTasksRunning && mOverallStatus !=
OnHold )
351 else if ( mStatus ==
OnHold && subTasksRunning )
358void QgsTask::terminated()
361 QMetaObject::invokeMethod(
this, &QgsTask::processSubTasksForTermination );
367class QgsTaskRunnableWrapper :
public QRunnable
370 explicit QgsTaskRunnableWrapper( QgsTask *task )
373 setAutoDelete(
true );
383 QgsTask *mTask =
nullptr;
395 , mThreadPool( new QThreadPool( this ) )
396 , 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();
435long QgsTaskManager::addTaskPrivate(
QgsTask *task,
QgsTaskList dependencies,
bool isSubTask,
int priority )
443 if (
task->thread() != this->thread() )
446#if QT_VERSION >= QT_VERSION_CHECK( 6, 7, 0 )
447 if ( !
task->moveToThread( this->thread() ) )
452 task->moveToThread( this->thread() );
465 &QgsTaskManager::layersWillBeRemoved
469 long taskId = mNextTaskId++;
472 mTasks.insert(
taskId, TaskInfo(
task, priority ) );
480 mParentTasks <<
task;
482 if ( !
task->dependentLayers().isEmpty() )
483 mLayerDependencies.insert(
taskId, _qgis_listRawToQPointer(
task->dependentLayers() ) );
484 mTaskMutex->unlock();
493 for (
const QgsTask::SubTask &subTask : std::as_const(
task->mSubTasks ) )
495 switch ( subTask.dependency )
506 addTaskPrivate( subTask.task, subTask.dependencies,
true, priority );
514 if ( hasCircularDependencies(
taskId ) )
532 QMutexLocker ml( mTaskMutex );
534 if ( mTasks.contains(
id ) )
535 t = mTasks.value(
id ).task;
541 QMutexLocker ml( mTaskMutex );
542 return QList<QgsTask *>( mParentTasks.begin(), mParentTasks.end() );
547 QMutexLocker ml( mTaskMutex );
548 return mParentTasks.count();
556 QMutexLocker ml( mTaskMutex );
557 const auto iter = mMapTaskPtrToId.constFind(
task );
558 if ( iter != mMapTaskPtrToId.constEnd() )
566 QSet< QgsTask * > parents = mParentTasks;
568 mTaskMutex->unlock();
570 const auto constParents = parents;
580 QMap< long, QgsTaskList >
dependencies = mTaskDependencies;
582 mTaskMutex->unlock();
600 if ( resolveDependencies(
taskId, results ) )
606bool QgsTaskManager::resolveDependencies(
long thisTaskId, QSet<long> &results )
const
609 QMap< long, QgsTaskList >
dependencies = mTaskDependencies;
611 mTaskMutex->unlock();
613 QSet<long> alreadyExploredTaskIds;
614 QStack<long> stackTaskIds;
615 stackTaskIds.push( thisTaskId );
616 while ( !stackTaskIds.isEmpty() )
618 const long currentTaskId = stackTaskIds.pop();
619 alreadyExploredTaskIds.insert( currentTaskId );
625 const auto &constValue = *iter;
629 if ( dependentTaskId >= 0 )
631 if ( thisTaskId == dependentTaskId )
638 results.insert( dependentTaskId );
641 if ( !alreadyExploredTaskIds.contains( dependentTaskId ) )
643 stackTaskIds.push( dependentTaskId );
652bool QgsTaskManager::hasCircularDependencies(
long taskId )
const
655 return !resolveDependencies(
taskId, d );
660 QMutexLocker ml( mTaskMutex );
666 QMutexLocker ml( mTaskMutex );
667 QList< QgsTask * >
tasks;
668 QMap< long, QgsWeakMapLayerPointerList >::const_iterator layerIt = mLayerDependencies.constBegin();
669 for ( ; layerIt != mLayerDependencies.constEnd(); ++layerIt )
671 if ( _qgis_listQPointerToRaw( layerIt.value() ).contains( layer ) )
683 QMutexLocker ml( mTaskMutex );
691 QMutexLocker ml( mTaskMutex );
692 QSet< QgsTask * >
tasks = mActiveTasks;
694 if ( !includeHidden )
696 QSet< QgsTask * > filteredTasks;
697 filteredTasks.reserve(
tasks.size() );
701 filteredTasks.insert(
task );
703 tasks = filteredTasks;
706 return tasks.intersect( mParentTasks ).count();
715void QgsTaskManager::taskProgressChanged(
double progress )
717 QgsTask *
task = qobject_cast< QgsTask * >( sender() );
734void QgsTaskManager::taskStatusChanged(
int status )
736 QgsTask *
task = qobject_cast< QgsTask * >( sender() );
745 QgsTaskRunnableWrapper *runnable = mTasks.value(
id ).runnable;
746 mTaskMutex->unlock();
747 if ( runnable && mThreadPool->tryTake( runnable ) )
750 mTasks[id].runnable =
nullptr;
756 task->finished( result );
762 cancelDependentTasks(
id );
766 bool isParent = mParentTasks.contains(
task );
767 mTaskMutex->unlock();
768 if ( isParent && !isHidden )
778 cleanupAndDeleteTask(
task );
782void QgsTaskManager::layersWillBeRemoved(
const QList< QgsMapLayer * > &layers )
786 QMap< long, QgsWeakMapLayerPointerList > layerDependencies = mLayerDependencies;
787 layerDependencies.detach();
788 mTaskMutex->unlock();
790 const auto constLayers = layers;
791 for ( QgsMapLayer *layer : constLayers )
794 for ( QMap< long, QgsWeakMapLayerPointerList >::const_iterator it = layerDependencies.constBegin(); it != layerDependencies.constEnd(); ++it )
796 if ( !( _qgis_listQPointerToRaw( it.value() ).contains( layer ) ) )
802 QgsTask *dependentTask =
task( it.key() );
813bool QgsTaskManager::cleanupAndDeleteTask(
QgsTask *task )
822 QgsTaskRunnableWrapper *runnable = mTasks.value(
id ).runnable;
824 task->disconnect(
this );
827 if ( mTaskDependencies.contains(
id ) )
828 mTaskDependencies.remove(
id );
829 mTaskMutex->unlock();
834 bool isParent = mParentTasks.contains(
task );
835 mParentTasks.remove(
task );
836 mSubTasks.remove(
task );
838 mMapTaskPtrToId.remove(
task );
839 mLayerDependencies.remove(
id );
853 if ( runnable && mThreadPool->tryTake( runnable ) )
856 mTasks[id].runnable =
nullptr;
867 for ( QMap< long, QgsTaskList >::iterator it = mTaskDependencies.begin(); it != mTaskDependencies.end(); ++it )
869 if ( it.value().contains(
task ) )
871 it.value().removeAll(
task );
874 mTaskMutex->unlock();
879void QgsTaskManager::processQueue()
883 mActiveTasks.clear();
884 for ( QMap< long, TaskInfo >::iterator it = mTasks.begin(); it != mTasks.end(); ++it )
886 QgsTask *
task = it.value().task;
889 it.value().createRunnable();
890 mThreadPool->start( it.value().runnable, it.value().priority );
895 mActiveTasks <<
task;
899 bool allFinished = mActiveTasks.isEmpty();
900 mTaskMutex->unlock();
908 if ( prevActiveCount != newActiveCount )
914void QgsTaskManager::cancelDependentTasks(
long taskId )
920 QMap< long, QgsTaskList > taskDependencies = mTaskDependencies;
921 taskDependencies.detach();
922 mTaskMutex->unlock();
924 QMap< long, QgsTaskList >::const_iterator it = taskDependencies.constBegin();
925 for ( ; it != taskDependencies.constEnd(); ++it )
927 if ( it.value().contains( canceledTask ) )
933 QgsTask *dependentTask =
task( it.key() );
940QgsTaskManager::TaskInfo::TaskInfo(
QgsTask *task,
int priority )
943 , priority( priority )
946void QgsTaskManager::TaskInfo::createRunnable()
948 Q_ASSERT( !runnable );
949 runnable =
new QgsTaskRunnableWrapper( task );
971 if ( mShouldTerminate )
974 mProgress = 100.0 * ( double( i ) + subTaskProgress / 100.0 ) /
double(
mSubTasksSerial.size() );
977 if ( !subTask->
run() )
979 subTask->completed();
980 mProgress = 100.0 * double( i + 1 ) / double(
mSubTasksSerial.size() );
Base class for all map layer types.
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.
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)
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.