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.