22 #include <QtConcurrentRun> 
   31   , mDescription( name )
 
   32   , mNotStartedMutex( 1 )
 
   34   mNotStartedMutex.acquire();
 
   39   Q_ASSERT_X( mStatus != 
Running, 
"delete", QStringLiteral( 
"status was %1" ).arg( mStatus ).toLatin1() );
 
   41   mNotFinishedMutex.lock();
 
   42   const auto constMSubTasks = mSubTasks;
 
   43   for ( 
const SubTask &subTask : constMSubTasks )
 
   47   mNotFinishedMutex.unlock();
 
   48   mNotStartedMutex.release();
 
   58   return mElapsedTime.elapsed();
 
   63   QMutexLocker locker( &mNotFinishedMutex );
 
   64   mNotStartedMutex.release();
 
   66   Q_ASSERT( mStartCount == 1 );
 
   96   mShouldTerminateMutex.lock();
 
   97   mShouldTerminate = 
true;
 
   98   mShouldTerminateMutex.unlock();
 
  103     mNotStartedMutex.release();
 
  108     processSubTasksForTermination();
 
  111   const auto constMSubTasks = mSubTasks;
 
  112   for ( 
const SubTask &subTask : constMSubTasks )
 
  114     subTask.task->cancel();
 
  120   QMutexLocker locker( &mShouldTerminateMutex );
 
  121   return mShouldTerminate;
 
  129     processSubTasksForHold();
 
  132   const auto constMSubTasks = mSubTasks;
 
  133   for ( 
const SubTask &subTask : constMSubTasks )
 
  135     subTask.task->hold();
 
  148   const auto constMSubTasks = mSubTasks;
 
  149   for ( 
const SubTask &subTask : constMSubTasks )
 
  151     subTask.task->unhold();
 
  158   mSubTasks << SubTask( subTask, dependencies, subTaskDependency );
 
  165   return _qgis_listQPointerToRaw( mDependentLayers );
 
  171   mNotStartedMutex.acquire();
 
  181       timeout = std::numeric_limits< int >::max();
 
  182     if ( mNotFinishedMutex.tryLock( timeout ) )
 
  184       mNotFinishedMutex.unlock();
 
  185       QCoreApplication::sendPostedEvents( 
this );
 
  201 void QgsTask::subTaskStatusChanged( 
int status )
 
  203   QgsTask *subTask = qobject_cast< QgsTask * >( sender() );
 
  214     processSubTasksForCompletion();
 
  219     processSubTasksForTermination();
 
  223     processSubTasksForHold();
 
  236   if ( !mSubTasks.isEmpty() )
 
  240     double totalProgress = 0.0;
 
  241     const auto constMSubTasks = mSubTasks;
 
  242     for ( 
const SubTask &subTask : constMSubTasks )
 
  246         totalProgress += 100.0;
 
  250         totalProgress += subTask.task->
progress();
 
  257   double prevProgress = mTotalProgress;
 
  261   if ( 
static_cast< int >( prevProgress * 10 ) != 
static_cast< int >( mTotalProgress * 10 ) )
 
  265 void QgsTask::completed()
 
  268   QMetaObject::invokeMethod( 
this, 
"processSubTasksForCompletion" );
 
  271 void QgsTask::processSubTasksForCompletion()
 
  273   bool subTasksCompleted = 
true;
 
  274   const auto constMSubTasks = mSubTasks;
 
  275   for ( 
const SubTask &subTask : constMSubTasks )
 
  279       subTasksCompleted = 
false;
 
  284   if ( mStatus == 
Complete && subTasksCompleted )
 
  299 void QgsTask::processSubTasksForTermination()
 
  301   bool subTasksTerminated = 
true;
 
  302   const auto constMSubTasks = mSubTasks;
 
  303   for ( 
const SubTask &subTask : constMSubTasks )
 
  307       subTasksTerminated = 
false;
 
  319   else if ( mStatus == 
Terminated && !subTasksTerminated )
 
  326 void QgsTask::processSubTasksForHold()
 
  328   bool subTasksRunning = 
false;
 
  329   const auto constMSubTasks = mSubTasks;
 
  330   for ( 
const SubTask &subTask : constMSubTasks )
 
  334       subTasksRunning = 
true;
 
  339   if ( mStatus == 
OnHold && !subTasksRunning && mOverallStatus != 
OnHold )
 
  344   else if ( mStatus == 
OnHold && subTasksRunning )
 
  351 void QgsTask::terminated()
 
  354   QMetaObject::invokeMethod( 
this, 
"processSubTasksForTermination" );
 
  360 class QgsTaskRunnableWrapper : 
public QRunnable
 
  364     explicit QgsTaskRunnableWrapper( 
QgsTask *task )
 
  367       setAutoDelete( 
true );
 
  392 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
 
  393   , mTaskMutex( new QMutex( QMutex::Recursive ) )
 
  395   , 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 );
 
  427   return addTaskPrivate( definition.
task,
 
  434 long QgsTaskManager::addTaskPrivate( 
QgsTask *task, 
QgsTaskList dependencies, 
bool isSubTask, 
int priority )
 
  445              this, &QgsTaskManager::layersWillBeRemoved );
 
  448   long taskId = mNextTaskId++;
 
  451   mTasks.insert( 
taskId, TaskInfo( 
task, priority ) );
 
  458     mParentTasks << 
task;
 
  462   mTaskMutex->unlock();
 
  471   for ( 
const QgsTask::SubTask &subTask : std::as_const( 
task->mSubTasks ) )
 
  473     switch ( subTask.dependency )
 
  484     addTaskPrivate( subTask.task, subTask.dependencies, 
true, priority );
 
  492   if ( hasCircularDependencies( 
taskId ) )
 
  508   QMutexLocker ml( mTaskMutex );
 
  510   if ( mTasks.contains( 
id ) )
 
  511     t = mTasks.value( 
id ).task;
 
  517   QMutexLocker ml( mTaskMutex );
 
  518   return qgis::setToList( mParentTasks );
 
  523   QMutexLocker ml( mTaskMutex );
 
  524   return mParentTasks.count();
 
  532   QMutexLocker ml( mTaskMutex );
 
  533   QMap< long, TaskInfo >::const_iterator it = mTasks.constBegin();
 
  534   for ( ; it != mTasks.constEnd(); ++it )
 
  536     if ( it.value().task == 
task )
 
  547   QSet< QgsTask * > parents = mParentTasks;
 
  549   mTaskMutex->unlock();
 
  551   const auto constParents = parents;
 
  561   QMap< long, QgsTaskList > 
dependencies = mTaskDependencies;
 
  563   mTaskMutex->unlock();
 
  587 bool QgsTaskManager::resolveDependencies( 
long firstTaskId, 
long currentTaskId, QSet<long> &results )
 const 
  590   QMap< long, QgsTaskList > 
dependencies = mTaskDependencies;
 
  592   mTaskMutex->unlock();
 
  597   const auto constValue = 
dependencies.value( currentTaskId );
 
  601     if ( dependentTaskId >= 0 )
 
  603       if ( dependentTaskId == firstTaskId )
 
  608       results.insert( dependentTaskId );
 
  610       QSet< long > newTaskDeps;
 
  611       if ( !resolveDependencies( firstTaskId, dependentTaskId, newTaskDeps ) )
 
  614       if ( newTaskDeps.contains( firstTaskId ) )
 
  620       results.unite( newTaskDeps );
 
  627 bool QgsTaskManager::hasCircularDependencies( 
long taskId )
 const 
  635   QMutexLocker ml( mTaskMutex );
 
  641   QMutexLocker ml( mTaskMutex );
 
  642   QList< QgsTask * > 
tasks;
 
  643   QMap< long, QgsWeakMapLayerPointerList >::const_iterator layerIt = mLayerDependencies.constBegin();
 
  644   for ( ; layerIt != mLayerDependencies.constEnd(); ++layerIt )
 
  646     if ( _qgis_listQPointerToRaw( layerIt.value() ).contains( layer ) )
 
  658   QMutexLocker ml( mTaskMutex );
 
  666   QMutexLocker ml( mTaskMutex );
 
  667   QSet< QgsTask * > 
tasks = mActiveTasks;
 
  668   return tasks.intersect( mParentTasks ).count();
 
  677 void QgsTaskManager::taskProgressChanged( 
double progress )
 
  679   QgsTask *
task = qobject_cast< QgsTask * >( sender() );
 
  694 void QgsTaskManager::taskStatusChanged( 
int status )
 
  696   QgsTask *
task = qobject_cast< QgsTask * >( sender() );
 
  704   QgsTaskRunnableWrapper *runnable = mTasks.value( 
id ).runnable;
 
  705   mTaskMutex->unlock();
 
  706   if ( runnable && QThreadPool::globalInstance()->tryTake( runnable ) )
 
  709     mTasks[ id ].runnable = 
nullptr;
 
  721     cancelDependentTasks( 
id );
 
  725   bool isParent = mParentTasks.contains( 
task );
 
  726   mTaskMutex->unlock();
 
  737     cleanupAndDeleteTask( 
task );
 
  742 void QgsTaskManager::layersWillBeRemoved( 
const QList< QgsMapLayer * > &layers )
 
  746   QMap< long, QgsWeakMapLayerPointerList > layerDependencies = mLayerDependencies;
 
  747   layerDependencies.detach();
 
  748   mTaskMutex->unlock();
 
  750   const auto constLayers = layers;
 
  754     for ( QMap< long, QgsWeakMapLayerPointerList >::const_iterator it = layerDependencies.constBegin();
 
  755           it != layerDependencies.constEnd(); ++it )
 
  757       if ( !( _qgis_listQPointerToRaw( it.value() ).contains( layer ) ) )
 
  774 bool QgsTaskManager::cleanupAndDeleteTask( 
QgsTask *task )
 
  783   QgsTaskRunnableWrapper *runnable = mTasks.value( 
id ).runnable;
 
  785   task->disconnect( 
this );
 
  788   if ( mTaskDependencies.contains( 
id ) )
 
  789     mTaskDependencies.remove( 
id );
 
  790   mTaskMutex->unlock();
 
  795   bool isParent = mParentTasks.contains( 
task );
 
  796   mParentTasks.remove( 
task );
 
  797   mSubTasks.remove( 
task );
 
  799   mLayerDependencies.remove( 
id );
 
  813     if ( runnable && QThreadPool::globalInstance()->tryTake( runnable ) )
 
  816       mTasks[ id ].runnable = 
nullptr;
 
  827   for ( QMap< long, QgsTaskList >::iterator it = mTaskDependencies.begin(); it != mTaskDependencies.end(); ++it )
 
  829     if ( it.value().contains( 
task ) )
 
  831       it.value().removeAll( 
task );
 
  834   mTaskMutex->unlock();
 
  839 void QgsTaskManager::processQueue()
 
  843   mActiveTasks.clear();
 
  844   for ( QMap< long, TaskInfo >::iterator it = mTasks.begin(); it != mTasks.end(); ++it )
 
  849       it.value().createRunnable();
 
  850       QThreadPool::globalInstance()->start( it.value().runnable, it.value().priority );
 
  855       mActiveTasks << 
task;
 
  859   bool allFinished = mActiveTasks.isEmpty();
 
  860   mTaskMutex->unlock();
 
  868   if ( prevActiveCount != newActiveCount )
 
  874 void QgsTaskManager::cancelDependentTasks( 
long taskId )
 
  880   QMap< long, QgsTaskList > taskDependencies = mTaskDependencies;
 
  881   taskDependencies.detach();
 
  882   mTaskMutex->unlock();
 
  884   QMap< long, QgsTaskList >::const_iterator it = taskDependencies.constBegin();
 
  885   for ( ; it != taskDependencies.constEnd(); ++it )
 
  887     if ( it.value().contains( canceledTask ) )
 
  900 QgsTaskManager::TaskInfo::TaskInfo( 
QgsTask *task, 
int priority )
 
  903   , priority( priority )
 
  906 void QgsTaskManager::TaskInfo::createRunnable()
 
  908   Q_ASSERT( !runnable );
 
  909   runnable = 
new QgsTaskRunnableWrapper( task ); 
 
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.
int countActiveTasks() const
Returns the number of active (queued or running) tasks.
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.
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.
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.
Abstract base class for long running background tasks.
TaskStatus status() const
Returns the current task status.
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.
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.