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 );
 
  201void 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 ) )
 
  265void QgsTask::completed()
 
  268  QMetaObject::invokeMethod( 
this, 
"processSubTasksForCompletion" );
 
  271void 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 )
 
  299void 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 )
 
  326void 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 )
 
  351void QgsTask::terminated()
 
  354  QMetaObject::invokeMethod( 
this, 
"processSubTasksForTermination" );
 
  360class QgsTaskRunnableWrapper : 
public QRunnable
 
  364    explicit QgsTaskRunnableWrapper( 
QgsTask *task )
 
  367      setAutoDelete( 
true );
 
  392  , mTaskMutex( new QRecursiveMutex() )
 
  404  QMap< long, TaskInfo > 
tasks = mTasks;
 
  406  mTaskMutex->unlock();
 
  407  QMap< long, TaskInfo >::const_iterator it = 
tasks.constBegin();
 
  408  for ( ; it != 
tasks.constEnd(); ++it )
 
  410    cleanupAndDeleteTask( it.value().task );
 
  423  return addTaskPrivate( definition.
task,
 
  430long QgsTaskManager::addTaskPrivate( 
QgsTask *task, 
QgsTaskList dependencies, 
bool isSubTask, 
int priority )
 
  441             this, &QgsTaskManager::layersWillBeRemoved );
 
  444  long taskId = mNextTaskId++;
 
  447  mTasks.insert( 
taskId, TaskInfo( 
task, priority ) );
 
  454    mParentTasks << 
task;
 
  458  mTaskMutex->unlock();
 
  467  for ( 
const QgsTask::SubTask &subTask : std::as_const( 
task->mSubTasks ) )
 
  469    switch ( subTask.dependency )
 
  480    addTaskPrivate( subTask.task, subTask.dependencies, 
true, priority );
 
  488  if ( hasCircularDependencies( 
taskId ) )
 
  506  QMutexLocker ml( mTaskMutex );
 
  508  if ( mTasks.contains( 
id ) )
 
  509    t = mTasks.value( 
id ).task;
 
  515  QMutexLocker ml( mTaskMutex );
 
  516  return QList<QgsTask *>( mParentTasks.begin(), mParentTasks.end() );
 
  521  QMutexLocker ml( mTaskMutex );
 
  522  return mParentTasks.count();
 
  530  QMutexLocker ml( mTaskMutex );
 
  531  QMap< long, TaskInfo >::const_iterator it = mTasks.constBegin();
 
  532  for ( ; it != mTasks.constEnd(); ++it )
 
  534    if ( it.value().task == 
task )
 
  545  QSet< QgsTask * > parents = mParentTasks;
 
  547  mTaskMutex->unlock();
 
  549  const auto constParents = parents;
 
  559  QMap< long, QgsTaskList > 
dependencies = mTaskDependencies;
 
  561  mTaskMutex->unlock();
 
  585bool QgsTaskManager::resolveDependencies( 
long firstTaskId, 
long currentTaskId, QSet<long> &results )
 const 
  588  QMap< long, QgsTaskList > 
dependencies = mTaskDependencies;
 
  590  mTaskMutex->unlock();
 
  595  const auto constValue = 
dependencies.value( currentTaskId );
 
  599    if ( dependentTaskId >= 0 )
 
  601      if ( dependentTaskId == firstTaskId )
 
  606      results.insert( dependentTaskId );
 
  608      QSet< long > newTaskDeps;
 
  609      if ( !resolveDependencies( firstTaskId, dependentTaskId, newTaskDeps ) )
 
  612      if ( newTaskDeps.contains( firstTaskId ) )
 
  618      results.unite( newTaskDeps );
 
  625bool QgsTaskManager::hasCircularDependencies( 
long taskId )
 const 
  633  QMutexLocker ml( mTaskMutex );
 
  639  QMutexLocker ml( mTaskMutex );
 
  640  QList< QgsTask * > 
tasks;
 
  641  QMap< long, QgsWeakMapLayerPointerList >::const_iterator layerIt = mLayerDependencies.constBegin();
 
  642  for ( ; layerIt != mLayerDependencies.constEnd(); ++layerIt )
 
  644    if ( _qgis_listQPointerToRaw( layerIt.value() ).contains( layer ) )
 
  656  QMutexLocker ml( mTaskMutex );
 
  664  QMutexLocker ml( mTaskMutex );
 
  665  QSet< QgsTask * > 
tasks = mActiveTasks;
 
  667  if ( !includeHidden )
 
  669    QSet< QgsTask * > filteredTasks;
 
  670    filteredTasks.reserve( 
tasks.size() );
 
  674        filteredTasks.insert( 
task );
 
  676    tasks = filteredTasks;
 
  679  return tasks.intersect( mParentTasks ).count();
 
  688void QgsTaskManager::taskProgressChanged( 
double progress )
 
  690  QgsTask *
task = qobject_cast< QgsTask * >( sender() );
 
  707void QgsTaskManager::taskStatusChanged( 
int status )
 
  709  QgsTask *
task = qobject_cast< QgsTask * >( sender() );
 
  718  QgsTaskRunnableWrapper *runnable = mTasks.value( 
id ).runnable;
 
  719  mTaskMutex->unlock();
 
  720  if ( runnable && QThreadPool::globalInstance()->tryTake( runnable ) )
 
  723    mTasks[ id ].runnable = 
nullptr;
 
  735    cancelDependentTasks( 
id );
 
  739  bool isParent = mParentTasks.contains( 
task );
 
  740  mTaskMutex->unlock();
 
  741  if ( isParent && !isHidden )
 
  751    cleanupAndDeleteTask( 
task );
 
  756void QgsTaskManager::layersWillBeRemoved( 
const QList< QgsMapLayer * > &layers )
 
  760  QMap< long, QgsWeakMapLayerPointerList > layerDependencies = mLayerDependencies;
 
  761  layerDependencies.detach();
 
  762  mTaskMutex->unlock();
 
  764  const auto constLayers = layers;
 
  768    for ( QMap< long, QgsWeakMapLayerPointerList >::const_iterator it = layerDependencies.constBegin();
 
  769          it != layerDependencies.constEnd(); ++it )
 
  771      if ( !( _qgis_listQPointerToRaw( it.value() ).contains( layer ) ) )
 
  788bool QgsTaskManager::cleanupAndDeleteTask( 
QgsTask *task )
 
  797  QgsTaskRunnableWrapper *runnable = mTasks.value( 
id ).runnable;
 
  799  task->disconnect( 
this );
 
  802  if ( mTaskDependencies.contains( 
id ) )
 
  803    mTaskDependencies.remove( 
id );
 
  804  mTaskMutex->unlock();
 
  809  bool isParent = mParentTasks.contains( 
task );
 
  810  mParentTasks.remove( 
task );
 
  811  mSubTasks.remove( 
task );
 
  813  mLayerDependencies.remove( 
id );
 
  827    if ( runnable && QThreadPool::globalInstance()->tryTake( runnable ) )
 
  830      mTasks[ id ].runnable = 
nullptr;
 
  841  for ( QMap< long, QgsTaskList >::iterator it = mTaskDependencies.begin(); it != mTaskDependencies.end(); ++it )
 
  843    if ( it.value().contains( 
task ) )
 
  845      it.value().removeAll( 
task );
 
  848  mTaskMutex->unlock();
 
  853void QgsTaskManager::processQueue()
 
  857  mActiveTasks.clear();
 
  858  for ( QMap< long, TaskInfo >::iterator it = mTasks.begin(); it != mTasks.end(); ++it )
 
  863      it.value().createRunnable();
 
  864      QThreadPool::globalInstance()->start( it.value().runnable, it.value().priority );
 
  869      mActiveTasks << 
task;
 
  873  bool allFinished = mActiveTasks.isEmpty();
 
  874  mTaskMutex->unlock();
 
  882  if ( prevActiveCount != newActiveCount )
 
  888void QgsTaskManager::cancelDependentTasks( 
long taskId )
 
  894  QMap< long, QgsTaskList > taskDependencies = mTaskDependencies;
 
  895  taskDependencies.detach();
 
  896  mTaskMutex->unlock();
 
  898  QMap< long, QgsTaskList >::const_iterator it = taskDependencies.constBegin();
 
  899  for ( ; it != taskDependencies.constEnd(); ++it )
 
  901    if ( it.value().contains( canceledTask ) )
 
  914QgsTaskManager::TaskInfo::TaskInfo( 
QgsTask *task, 
int priority )
 
  917  , priority( priority )
 
  920void QgsTaskManager::TaskInfo::createRunnable()
 
  922  Q_ASSERT( !runnable );
 
  923  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.
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.
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.
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 (since QGIS 3.26)
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.