QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
qgstaskmanager.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgstaskmanager.cpp
3  ------------------
4  begin : April 2016
5  copyright : (C) 2016 by Nyall Dawson
6  email : nyall dot dawson at gmail dot com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgstaskmanager.h"
19 #include "qgsproject.h"
20 #include "qgsmaplayerlistutils_p.h"
21 #include <mutex>
22 #include <QtConcurrentRun>
23 
24 
25 //
26 // QgsTask
27 //
28 
29 QgsTask::QgsTask( const QString &name, Flags flags )
30  : mFlags( flags )
31  , mDescription( name )
32  , mNotStartedMutex( 1 )
33 {
34  mNotStartedMutex.acquire();
35 }
36 
38 {
39  Q_ASSERT_X( mStatus != Running, "delete", QStringLiteral( "status was %1" ).arg( mStatus ).toLatin1() );
40  // even here we are not sure that task start method has ended
41  mNotFinishedMutex.lock();
42  const auto constMSubTasks = mSubTasks;
43  for ( const SubTask &subTask : constMSubTasks )
44  {
45  delete subTask.task;
46  }
47  mNotFinishedMutex.unlock();
48  mNotStartedMutex.release();
49 }
50 
51 void QgsTask::setDescription( const QString &description )
52 {
53  mDescription = description;
54 }
55 
56 qint64 QgsTask::elapsedTime() const
57 {
58  return mElapsedTime.elapsed();
59 }
60 
61 void QgsTask::start()
62 {
63  QMutexLocker locker( &mNotFinishedMutex );
64  mNotStartedMutex.release();
65  mStartCount++;
66  Q_ASSERT( mStartCount == 1 );
67 
68  if ( mStatus != Queued )
69  return;
70 
71  mStatus = Running;
72  mOverallStatus = Running;
73  mElapsedTime.start();
74 
75  emit statusChanged( Running );
76  emit begun();
77 
78  // force initial emission of progressChanged, but respect if task has had initial progress manually set
79  setProgress( mProgress );
80 
81  if ( run() )
82  {
83  completed();
84  }
85  else
86  {
87  terminated();
88  }
89 }
90 
92 {
93  if ( mOverallStatus == Complete || mOverallStatus == Terminated )
94  return;
95 
96  mShouldTerminateMutex.lock();
97  mShouldTerminate = true;
98  mShouldTerminateMutex.unlock();
99  if ( mStatus == Queued || mStatus == OnHold )
100  {
101  // immediately terminate unstarted jobs
102  terminated();
103  mNotStartedMutex.release();
104  }
105 
106  if ( mStatus == Terminated )
107  {
108  processSubTasksForTermination();
109  }
110 
111  const auto constMSubTasks = mSubTasks;
112  for ( const SubTask &subTask : constMSubTasks )
113  {
114  subTask.task->cancel();
115  }
116 }
117 
119 {
120  QMutexLocker locker( &mShouldTerminateMutex );
121  return mShouldTerminate;
122 }
123 
125 {
126  if ( mStatus == Queued )
127  {
128  mStatus = OnHold;
129  processSubTasksForHold();
130  }
131 
132  const auto constMSubTasks = mSubTasks;
133  for ( const SubTask &subTask : constMSubTasks )
134  {
135  subTask.task->hold();
136  }
137 }
138 
140 {
141  if ( mStatus == OnHold )
142  {
143  mStatus = Queued;
144  mOverallStatus = Queued;
145  emit statusChanged( Queued );
146  }
147 
148  const auto constMSubTasks = mSubTasks;
149  for ( const SubTask &subTask : constMSubTasks )
150  {
151  subTask.task->unhold();
152  }
153 }
154 
155 void QgsTask::addSubTask( QgsTask *subTask, const QgsTaskList &dependencies,
156  SubTaskDependency subTaskDependency )
157 {
158  mSubTasks << SubTask( subTask, dependencies, subTaskDependency );
159  connect( subTask, &QgsTask::progressChanged, this, [ = ] { setProgress( mProgress ); } );
160  connect( subTask, &QgsTask::statusChanged, this, &QgsTask::subTaskStatusChanged );
161 }
162 
163 QList<QgsMapLayer *> QgsTask::dependentLayers() const
164 {
165  return _qgis_listQPointerToRaw( mDependentLayers );
166 }
167 
168 bool QgsTask::waitForFinished( int timeout )
169 {
170  // We wait the task to be started
171  mNotStartedMutex.acquire();
172 
173  bool rv = true;
174  if ( mOverallStatus == Complete || mOverallStatus == Terminated )
175  {
176  rv = true;
177  }
178  else
179  {
180  if ( timeout == 0 )
181  timeout = std::numeric_limits< int >::max();
182  if ( mNotFinishedMutex.tryLock( timeout ) )
183  {
184  mNotFinishedMutex.unlock();
185  QCoreApplication::sendPostedEvents( this );
186  rv = true;
187  }
188  else
189  {
190  rv = false;
191  }
192  }
193  return rv;
194 }
195 
196 void QgsTask::setDependentLayers( const QList< QgsMapLayer * > &dependentLayers )
197 {
198  mDependentLayers = _qgis_listRawToQPointer( dependentLayers );
199 }
200 
201 void QgsTask::subTaskStatusChanged( int status )
202 {
203  QgsTask *subTask = qobject_cast< QgsTask * >( sender() );
204  if ( !subTask )
205  return;
206 
207  if ( status == Running && mStatus == Queued )
208  {
209  mOverallStatus = Running;
210  }
211  else if ( status == Complete && mStatus == Complete )
212  {
213  //check again if all subtasks are complete
214  processSubTasksForCompletion();
215  }
216  else if ( ( status == Complete || status == Terminated ) && mStatus == Terminated )
217  {
218  //check again if all subtasks are terminated
219  processSubTasksForTermination();
220  }
221  else if ( ( status == Complete || status == Terminated || status == OnHold ) && mStatus == OnHold )
222  {
223  processSubTasksForHold();
224  }
225  else if ( status == Terminated )
226  {
227  //uh oh...
228  cancel();
229  }
230 }
231 
232 void QgsTask::setProgress( double progress )
233 {
234  mProgress = progress;
235 
236  if ( !mSubTasks.isEmpty() )
237  {
238  // calculate total progress including subtasks
239 
240  double totalProgress = 0.0;
241  const auto constMSubTasks = mSubTasks;
242  for ( const SubTask &subTask : constMSubTasks )
243  {
244  if ( subTask.task->status() == QgsTask::Complete )
245  {
246  totalProgress += 100.0;
247  }
248  else
249  {
250  totalProgress += subTask.task->progress();
251  }
252  }
253  progress = ( progress + totalProgress ) / ( mSubTasks.count() + 1 );
254  }
255 
256  // avoid flooding with too many events
257  double prevProgress = mTotalProgress;
258  mTotalProgress = progress;
259 
260  // avoid spamming with too many progressChanged reports
261  if ( static_cast< int >( prevProgress * 10 ) != static_cast< int >( mTotalProgress * 10 ) )
262  emit progressChanged( progress );
263 }
264 
265 void QgsTask::completed()
266 {
267  mStatus = Complete;
268  QMetaObject::invokeMethod( this, "processSubTasksForCompletion" );
269 }
270 
271 void QgsTask::processSubTasksForCompletion()
272 {
273  bool subTasksCompleted = true;
274  const auto constMSubTasks = mSubTasks;
275  for ( const SubTask &subTask : constMSubTasks )
276  {
277  if ( subTask.task->status() != Complete )
278  {
279  subTasksCompleted = false;
280  break;
281  }
282  }
283 
284  if ( mStatus == Complete && subTasksCompleted )
285  {
286  mOverallStatus = Complete;
287 
288  setProgress( 100.0 );
289  emit statusChanged( Complete );
290  emit taskCompleted();
291  }
292  else if ( mStatus == Complete )
293  {
294  // defer completion until all subtasks are complete
295  mOverallStatus = Running;
296  }
297 }
298 
299 void QgsTask::processSubTasksForTermination()
300 {
301  bool subTasksTerminated = true;
302  const auto constMSubTasks = mSubTasks;
303  for ( const SubTask &subTask : constMSubTasks )
304  {
305  if ( subTask.task->status() != Terminated && subTask.task->status() != Complete )
306  {
307  subTasksTerminated = false;
308  break;
309  }
310  }
311 
312  if ( mStatus == Terminated && subTasksTerminated && mOverallStatus != Terminated )
313  {
314  mOverallStatus = Terminated;
315 
316  emit statusChanged( Terminated );
317  emit taskTerminated();
318  }
319  else if ( mStatus == Terminated && !subTasksTerminated )
320  {
321  // defer termination until all subtasks are terminated (or complete)
322  mOverallStatus = Running;
323  }
324 }
325 
326 void QgsTask::processSubTasksForHold()
327 {
328  bool subTasksRunning = false;
329  const auto constMSubTasks = mSubTasks;
330  for ( const SubTask &subTask : constMSubTasks )
331  {
332  if ( subTask.task->status() == Running )
333  {
334  subTasksRunning = true;
335  break;
336  }
337  }
338 
339  if ( mStatus == OnHold && !subTasksRunning && mOverallStatus != OnHold )
340  {
341  mOverallStatus = OnHold;
342  emit statusChanged( OnHold );
343  }
344  else if ( mStatus == OnHold && subTasksRunning )
345  {
346  // defer hold until all subtasks finish running
347  mOverallStatus = Running;
348  }
349 }
350 
351 void QgsTask::terminated()
352 {
353  mStatus = Terminated;
354  QMetaObject::invokeMethod( this, "processSubTasksForTermination" );
355 }
356 
357 
359 
360 class QgsTaskRunnableWrapper : public QRunnable
361 {
362  public:
363 
364  explicit QgsTaskRunnableWrapper( QgsTask *task )
365  : mTask( task )
366  {
367  setAutoDelete( true );
368  }
369 
370  void run() override
371  {
372  Q_ASSERT( mTask );
373  mTask->start();
374  }
375 
376  private:
377 
378  QgsTask *mTask = nullptr;
379 
380 };
381 
383 
384 
385 
386 //
387 // QgsTaskManager
388 //
389 
391  : QObject( parent )
392 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
393  , mTaskMutex( new QMutex( QMutex::Recursive ) )
394 #else
395  , mTaskMutex( new QRecursiveMutex() )
396 #endif
397 {
398 
399 }
400 
402 {
403  //first tell all tasks to cancel
404  cancelAll();
405 
406  //then clean them up, including waiting for them to terminate
407  mTaskMutex->lock();
408  QMap< long, TaskInfo > tasks = mTasks;
409  mTasks.detach();
410  mTaskMutex->unlock();
411  QMap< long, TaskInfo >::const_iterator it = tasks.constBegin();
412  for ( ; it != tasks.constEnd(); ++it )
413  {
414  cleanupAndDeleteTask( it.value().task );
415  }
416 
417  delete mTaskMutex;
418 }
419 
420 long QgsTaskManager::addTask( QgsTask *task, int priority )
421 {
422  return addTaskPrivate( task, QgsTaskList(), false, priority );
423 }
424 
425 long QgsTaskManager::addTask( const QgsTaskManager::TaskDefinition &definition, int priority )
426 {
427  return addTaskPrivate( definition.task,
428  definition.dependentTasks,
429  false,
430  priority );
431 }
432 
433 
434 long QgsTaskManager::addTaskPrivate( QgsTask *task, QgsTaskList dependencies, bool isSubTask, int priority )
435 {
436  if ( !task )
437  return 0;
438 
439  if ( !mInitialized )
440  {
441  mInitialized = true;
442  // defer connection to project until we actually need it -- we don't want to connect to the project instance in the constructor,
443  // cos that forces early creation of QgsProject
444  connect( QgsProject::instance(), static_cast < void ( QgsProject::* )( const QList< QgsMapLayer * >& ) > ( &QgsProject::layersWillBeRemoved ),
445  this, &QgsTaskManager::layersWillBeRemoved );
446  }
447 
448  long taskId = mNextTaskId++;
449 
450  mTaskMutex->lock();
451  mTasks.insert( taskId, TaskInfo( task, priority ) );
452  if ( isSubTask )
453  {
454  mSubTasks << task;
455  }
456  else
457  {
458  mParentTasks << task;
459  }
460  if ( !task->dependentLayers().isEmpty() )
461  mLayerDependencies.insert( taskId, _qgis_listRawToQPointer( task->dependentLayers() ) );
462  mTaskMutex->unlock();
463 
464  connect( task, &QgsTask::statusChanged, this, &QgsTaskManager::taskStatusChanged );
465  if ( !isSubTask )
466  {
467  connect( task, &QgsTask::progressChanged, this, &QgsTaskManager::taskProgressChanged );
468  }
469 
470  // add all subtasks, must be done before dependency resolution
471  for ( const QgsTask::SubTask &subTask : std::as_const( task->mSubTasks ) )
472  {
473  switch ( subTask.dependency )
474  {
476  dependencies << subTask.task;
477  break;
478 
480  //nothing
481  break;
482  }
483  //recursively add sub tasks
484  addTaskPrivate( subTask.task, subTask.dependencies, true, priority );
485  }
486 
487  if ( !dependencies.isEmpty() )
488  {
489  mTaskDependencies.insert( taskId, dependencies );
490  }
491 
492  if ( hasCircularDependencies( taskId ) )
493  {
494  task->cancel();
495  }
496 
497  if ( !isSubTask )
498  {
499  if ( !( task->flags() & QgsTask::Hidden ) )
500  emit taskAdded( taskId );
501 
502  processQueue();
503  }
504 
505  return taskId;
506 }
507 
508 QgsTask *QgsTaskManager::task( long id ) const
509 {
510  QMutexLocker ml( mTaskMutex );
511  QgsTask *t = nullptr;
512  if ( mTasks.contains( id ) )
513  t = mTasks.value( id ).task;
514  return t;
515 }
516 
517 QList<QgsTask *> QgsTaskManager::tasks() const
518 {
519  QMutexLocker ml( mTaskMutex );
520  return qgis::setToList( mParentTasks );
521 }
522 
524 {
525  QMutexLocker ml( mTaskMutex );
526  return mParentTasks.count();
527 }
528 
529 long QgsTaskManager::taskId( QgsTask *task ) const
530 {
531  if ( !task )
532  return -1;
533 
534  QMutexLocker ml( mTaskMutex );
535  QMap< long, TaskInfo >::const_iterator it = mTasks.constBegin();
536  for ( ; it != mTasks.constEnd(); ++it )
537  {
538  if ( it.value().task == task )
539  {
540  return it.key();
541  }
542  }
543  return -1;
544 }
545 
547 {
548  mTaskMutex->lock();
549  QSet< QgsTask * > parents = mParentTasks;
550  parents.detach();
551  mTaskMutex->unlock();
552 
553  const auto constParents = parents;
554  for ( QgsTask *task : constParents )
555  {
556  task->cancel();
557  }
558 }
559 
560 bool QgsTaskManager::dependenciesSatisfied( long taskId ) const
561 {
562  mTaskMutex->lock();
563  QMap< long, QgsTaskList > dependencies = mTaskDependencies;
564  dependencies.detach();
565  mTaskMutex->unlock();
566 
567  if ( !dependencies.contains( taskId ) )
568  return true;
569 
570  const auto constValue = dependencies.value( taskId );
571  for ( QgsTask *task : constValue )
572  {
573  if ( task->status() != QgsTask::Complete )
574  return false;
575  }
576 
577  return true;
578 }
579 
580 QSet<long> QgsTaskManager::dependencies( long taskId ) const
581 {
582  QSet<long> results;
583  if ( resolveDependencies( taskId, taskId, results ) )
584  return results;
585  else
586  return QSet<long>();
587 }
588 
589 bool QgsTaskManager::resolveDependencies( long firstTaskId, long currentTaskId, QSet<long> &results ) const
590 {
591  mTaskMutex->lock();
592  QMap< long, QgsTaskList > dependencies = mTaskDependencies;
593  dependencies.detach();
594  mTaskMutex->unlock();
595 
596  if ( !dependencies.contains( currentTaskId ) )
597  return true;
598 
599  const auto constValue = dependencies.value( currentTaskId );
600  for ( QgsTask *task : constValue )
601  {
602  long dependentTaskId = taskId( task );
603  if ( dependentTaskId >= 0 )
604  {
605  if ( dependentTaskId == firstTaskId )
606  // circular
607  return false;
608 
609  //add task as dependent
610  results.insert( dependentTaskId );
611  //plus all its other dependencies
612  QSet< long > newTaskDeps;
613  if ( !resolveDependencies( firstTaskId, dependentTaskId, newTaskDeps ) )
614  return false;
615 
616  if ( newTaskDeps.contains( firstTaskId ) )
617  {
618  // circular
619  return false;
620  }
621 
622  results.unite( newTaskDeps );
623  }
624  }
625 
626  return true;
627 }
628 
629 bool QgsTaskManager::hasCircularDependencies( long taskId ) const
630 {
631  QSet< long > d;
632  return !resolveDependencies( taskId, taskId, d );
633 }
634 
635 QList<QgsMapLayer *> QgsTaskManager::dependentLayers( long taskId ) const
636 {
637  QMutexLocker ml( mTaskMutex );
638  return _qgis_listQPointerToRaw( mLayerDependencies.value( taskId, QgsWeakMapLayerPointerList() ) );
639 }
640 
641 QList<QgsTask *> QgsTaskManager::tasksDependentOnLayer( QgsMapLayer *layer ) const
642 {
643  QMutexLocker ml( mTaskMutex );
644  QList< QgsTask * > tasks;
645  QMap< long, QgsWeakMapLayerPointerList >::const_iterator layerIt = mLayerDependencies.constBegin();
646  for ( ; layerIt != mLayerDependencies.constEnd(); ++layerIt )
647  {
648  if ( _qgis_listQPointerToRaw( layerIt.value() ).contains( layer ) )
649  {
650  QgsTask *layerTask = task( layerIt.key() );
651  if ( layerTask )
652  tasks << layerTask;
653  }
654  }
655  return tasks;
656 }
657 
658 QList<QgsTask *> QgsTaskManager::activeTasks() const
659 {
660  QMutexLocker ml( mTaskMutex );
661  QSet< QgsTask * > activeTasks = mActiveTasks;
662  activeTasks.intersect( mParentTasks );
663  return qgis::setToList( activeTasks );
664 }
665 
666 int QgsTaskManager::countActiveTasks( bool includeHidden ) const
667 {
668  QMutexLocker ml( mTaskMutex );
669  QSet< QgsTask * > tasks = mActiveTasks;
670 
671  if ( !includeHidden )
672  {
673  QSet< QgsTask * > filteredTasks;
674  filteredTasks.reserve( tasks.size() );
675  for ( QgsTask *task : tasks )
676  {
677  if ( !( task->flags() & QgsTask::Hidden ) )
678  filteredTasks.insert( task );
679  }
680  tasks = filteredTasks;
681  }
682 
683  return tasks.intersect( mParentTasks ).count();
684 }
685 
687 {
688  if ( task )
689  emit taskTriggered( task );
690 }
691 
692 void QgsTaskManager::taskProgressChanged( double progress )
693 {
694  QgsTask *task = qobject_cast< QgsTask * >( sender() );
695  if ( task && task->flags() & QgsTask::Hidden )
696  return;
697 
698  //find ID of task
699  long id = taskId( task );
700  if ( id < 0 )
701  return;
702 
703  emit progressChanged( id, progress );
704 
705  if ( countActiveTasks( false ) == 1 )
706  {
707  emit finalTaskProgressChanged( progress );
708  }
709 }
710 
711 void QgsTaskManager::taskStatusChanged( int status )
712 {
713  QgsTask *task = qobject_cast< QgsTask * >( sender() );
714  const bool isHidden = task && task->flags() & QgsTask::Hidden;
715 
716  //find ID of task
717  long id = taskId( task );
718  if ( id < 0 )
719  return;
720 
721  mTaskMutex->lock();
722  QgsTaskRunnableWrapper *runnable = mTasks.value( id ).runnable;
723  mTaskMutex->unlock();
724  if ( runnable && QThreadPool::globalInstance()->tryTake( runnable ) )
725  {
726  delete runnable;
727  mTasks[ id ].runnable = nullptr;
728  }
729 
730  if ( status == QgsTask::Terminated || status == QgsTask::Complete )
731  {
732  bool result = status == QgsTask::Complete;
733  task->finished( result );
734  }
735 
736  if ( status == QgsTask::Terminated )
737  {
738  //recursively cancel dependent tasks
739  cancelDependentTasks( id );
740  }
741 
742  mTaskMutex->lock();
743  bool isParent = mParentTasks.contains( task );
744  mTaskMutex->unlock();
745  if ( isParent && !isHidden )
746  {
747  // don't emit status changed for subtasks
748  emit statusChanged( id, status );
749  }
750 
751  processQueue();
752 
753  if ( status == QgsTask::Terminated || status == QgsTask::Complete )
754  {
755  cleanupAndDeleteTask( task );
756  }
757 
758 }
759 
760 void QgsTaskManager::layersWillBeRemoved( const QList< QgsMapLayer * > &layers )
761 {
762  mTaskMutex->lock();
763  // scan through layers to be removed
764  QMap< long, QgsWeakMapLayerPointerList > layerDependencies = mLayerDependencies;
765  layerDependencies.detach();
766  mTaskMutex->unlock();
767 
768  const auto constLayers = layers;
769  for ( QgsMapLayer *layer : constLayers )
770  {
771  // scan through tasks with layer dependencies
772  for ( QMap< long, QgsWeakMapLayerPointerList >::const_iterator it = layerDependencies.constBegin();
773  it != layerDependencies.constEnd(); ++it )
774  {
775  if ( !( _qgis_listQPointerToRaw( it.value() ).contains( layer ) ) )
776  {
777  //task not dependent on this layer
778  continue;
779  }
780 
781  QgsTask *dependentTask = task( it.key() );
782  if ( dependentTask && ( dependentTask->status() != QgsTask::Complete && dependentTask->status() != QgsTask::Terminated ) )
783  {
784  // incomplete task is dependent on this layer!
785  dependentTask->cancel();
786  }
787  }
788  }
789 }
790 
791 
792 bool QgsTaskManager::cleanupAndDeleteTask( QgsTask *task )
793 {
794  if ( !task )
795  return false;
796 
797  long id = taskId( task );
798  if ( id < 0 )
799  return false;
800 
801  QgsTaskRunnableWrapper *runnable = mTasks.value( id ).runnable;
802 
803  task->disconnect( this );
804 
805  mTaskMutex->lock();
806  if ( mTaskDependencies.contains( id ) )
807  mTaskDependencies.remove( id );
808  mTaskMutex->unlock();
809 
810  emit taskAboutToBeDeleted( id );
811 
812  mTaskMutex->lock();
813  bool isParent = mParentTasks.contains( task );
814  mParentTasks.remove( task );
815  mSubTasks.remove( task );
816  mTasks.remove( id );
817  mLayerDependencies.remove( id );
818 
820  {
821  if ( isParent )
822  {
823  // delete task when it's terminated
824  connect( task, &QgsTask::taskCompleted, task, &QgsTask::deleteLater );
825  connect( task, &QgsTask::taskTerminated, task, &QgsTask::deleteLater );
826  }
827  task->cancel();
828  }
829  else
830  {
831  if ( runnable && QThreadPool::globalInstance()->tryTake( runnable ) )
832  {
833  delete runnable;
834  mTasks[ id ].runnable = nullptr;
835  }
836 
837  if ( isParent )
838  {
839  //task already finished, kill it
840  task->deleteLater();
841  }
842  }
843 
844  // at this stage (hopefully) dependent tasks have been canceled or queued
845  for ( QMap< long, QgsTaskList >::iterator it = mTaskDependencies.begin(); it != mTaskDependencies.end(); ++it )
846  {
847  if ( it.value().contains( task ) )
848  {
849  it.value().removeAll( task );
850  }
851  }
852  mTaskMutex->unlock();
853 
854  return true;
855 }
856 
857 void QgsTaskManager::processQueue()
858 {
859  int prevActiveCount = countActiveTasks( false );
860  mTaskMutex->lock();
861  mActiveTasks.clear();
862  for ( QMap< long, TaskInfo >::iterator it = mTasks.begin(); it != mTasks.end(); ++it )
863  {
864  QgsTask *task = it.value().task;
865  if ( task && task->mStatus == QgsTask::Queued && dependenciesSatisfied( it.key() ) && it.value().added.testAndSetRelaxed( 0, 1 ) )
866  {
867  it.value().createRunnable();
868  QThreadPool::globalInstance()->start( it.value().runnable, it.value().priority );
869  }
870 
871  if ( task && ( task->mStatus != QgsTask::Complete && task->mStatus != QgsTask::Terminated ) )
872  {
873  mActiveTasks << task;
874  }
875  }
876 
877  bool allFinished = mActiveTasks.isEmpty();
878  mTaskMutex->unlock();
879 
880  if ( allFinished )
881  {
882  emit allTasksFinished();
883  }
884 
885  int newActiveCount = countActiveTasks( false );
886  if ( prevActiveCount != newActiveCount )
887  {
888  emit countActiveTasksChanged( newActiveCount );
889  }
890 }
891 
892 void QgsTaskManager::cancelDependentTasks( long taskId )
893 {
894  QgsTask *canceledTask = task( taskId );
895 
896  //deep copy
897  mTaskMutex->lock();
898  QMap< long, QgsTaskList > taskDependencies = mTaskDependencies;
899  taskDependencies.detach();
900  mTaskMutex->unlock();
901 
902  QMap< long, QgsTaskList >::const_iterator it = taskDependencies.constBegin();
903  for ( ; it != taskDependencies.constEnd(); ++it )
904  {
905  if ( it.value().contains( canceledTask ) )
906  {
907  // found task with this dependency
908 
909  // cancel it - note that this will be recursive, so any tasks dependent
910  // on this one will also be canceled
911  QgsTask *dependentTask = task( it.key() );
912  if ( dependentTask )
913  dependentTask->cancel();
914  }
915  }
916 }
917 
918 QgsTaskManager::TaskInfo::TaskInfo( QgsTask *task, int priority )
919  : task( task )
920  , added( 0 )
921  , priority( priority )
922 {}
923 
924 void QgsTaskManager::TaskInfo::createRunnable()
925 {
926  Q_ASSERT( !runnable );
927  runnable = new QgsTaskRunnableWrapper( task ); // auto deleted
928 }
QgsTaskManager::tasks
QList< QgsTask * > tasks() const
Returns all tasks tracked by the manager.
Definition: qgstaskmanager.cpp:517
QgsProject::layersWillBeRemoved
void layersWillBeRemoved(const QStringList &layerIds)
Emitted when one or more layers are about to be removed from the registry.
QgsTaskManager::activeTasks
QList< QgsTask * > activeTasks() const
Returns a list of the active (queued or running) tasks.
Definition: qgstaskmanager.cpp:658
QgsWeakMapLayerPointerList
QList< QgsWeakMapLayerPointer > QgsWeakMapLayerPointerList
A list of weak pointers to QgsMapLayers.
Definition: qgsmaplayer.h:2153
QgsTaskManager::TaskDefinition::dependentTasks
QgsTaskList dependentTasks
List of dependent tasks which must be completed before task can run.
Definition: qgstaskmanager.h:429
qgstaskmanager.h
QgsTask::setDescription
void setDescription(const QString &description)
Sets the task's description.
Definition: qgstaskmanager.cpp:51
QgsTaskManager::progressChanged
void progressChanged(long taskId, double progress)
Will be emitted when a task reports a progress change.
QgsTaskManager::dependenciesSatisfied
bool dependenciesSatisfied(long taskId) const
Returns true if all dependencies for the specified task are satisfied.
Definition: qgstaskmanager.cpp:560
QgsTask::SubTaskDependency
SubTaskDependency
Controls how subtasks relate to their parent task.
Definition: qgstaskmanager.h:167
QgsTask::Complete
@ Complete
Task successfully completed.
Definition: qgstaskmanager.h:66
QgsProject::instance
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:480
QgsTask::progressChanged
void progressChanged(double progress)
Will be emitted by task when its progress changes.
QgsTaskManager::statusChanged
void statusChanged(long taskId, int status)
Will be emitted when a task reports a status change.
QgsTaskManager::dependentLayers
QList< QgsMapLayer * > dependentLayers(long taskId) const
Returns a list of layers on which as task is dependent.
Definition: qgstaskmanager.cpp:635
QgsTask::description
QString description() const
Returns the task's description.
Definition: qgstaskmanager.h:122
QgsTask::SubTaskIndependent
@ SubTaskIndependent
Subtask is independent of the parent, and can run before, after or at the same time as the parent.
Definition: qgstaskmanager.h:169
QgsTask::Running
@ Running
Task is currently running.
Definition: qgstaskmanager.h:65
QgsTaskManager::finalTaskProgressChanged
void finalTaskProgressChanged(double progress)
Will be emitted when only a single task remains to complete and that task has reported a progress cha...
QgsTaskManager::count
int count() const
Returns the number of tasks tracked by the manager.
Definition: qgstaskmanager.cpp:523
QgsProject
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:103
QgsTask::finished
virtual void finished(bool result)
If the task is managed by a QgsTaskManager, this will be called after the task has finished (whether ...
Definition: qgstaskmanager.h:284
qgsmaplayerlistutils_p.h
QgsTask::unhold
void unhold()
Releases the task from being held.
Definition: qgstaskmanager.cpp:139
QgsTaskManager::addTask
long addTask(QgsTask *task, int priority=0)
Adds a task to the manager.
Definition: qgstaskmanager.cpp:420
QgsTaskManager::taskAboutToBeDeleted
void taskAboutToBeDeleted(long taskId)
Emitted when a task is about to be deleted.
QgsTask::taskCompleted
void taskCompleted()
Will be emitted by task to indicate its successful completion.
QgsTask::Queued
@ Queued
Task is queued and has not begun.
Definition: qgstaskmanager.h:63
QgsTask::cancel
virtual void cancel()
Notifies the task that it should terminate.
Definition: qgstaskmanager.cpp:91
QgsTask::hold
void hold()
Places the task on hold.
Definition: qgstaskmanager.cpp:124
QgsTaskManager::TaskDefinition
Definition of a task for inclusion in the manager.
Definition: qgstaskmanager.h:409
QgsTask::status
TaskStatus status() const
Returns the current task status.
Definition: qgstaskmanager.h:117
QgsTask::QgsTask
QgsTask(const QString &description=QString(), QgsTask::Flags flags=AllFlags)
Constructor for QgsTask.
Definition: qgstaskmanager.cpp:29
QgsTaskManager::task
QgsTask * task(long id) const
Returns the task with matching ID.
Definition: qgstaskmanager.cpp:508
QgsTaskManager::triggerTask
void triggerTask(QgsTask *task)
Triggers a task, e.g.
Definition: qgstaskmanager.cpp:686
QgsTaskManager::~QgsTaskManager
~QgsTaskManager() override
Definition: qgstaskmanager.cpp:401
QgsTask::statusChanged
void statusChanged(int status)
Will be emitted by task when its status changes.
QgsTask::setDependentLayers
void setDependentLayers(const QList< QgsMapLayer * > &dependentLayers)
Sets a list of layers on which the task depends.
Definition: qgstaskmanager.cpp:196
QgsTaskManager::QgsTaskManager
QgsTaskManager(QObject *parent=nullptr)
Constructor for QgsTaskManager.
Definition: qgstaskmanager.cpp:390
QgsTask::Hidden
@ Hidden
Hide task from GUI (since QGIS 3.26)
Definition: qgstaskmanager.h:76
QgsTaskManager::TaskDefinition::task
QgsTask * task
Task.
Definition: qgstaskmanager.h:422
QgsTask::setProgress
void setProgress(double progress)
Sets the task's current progress.
Definition: qgstaskmanager.cpp:232
QgsTask::ParentDependsOnSubTask
@ ParentDependsOnSubTask
Subtask must complete before parent can begin.
Definition: qgstaskmanager.h:170
QgsTask::taskTerminated
void taskTerminated()
Will be emitted by task if it has terminated for any reason other then completion (e....
QgsTask::dependentLayers
QList< QgsMapLayer * > dependentLayers() const
Returns the list of layers on which the task depends.
Definition: qgstaskmanager.cpp:163
QgsTask::addSubTask
void addSubTask(QgsTask *subTask, const QgsTaskList &dependencies=QgsTaskList(), SubTaskDependency subTaskDependency=SubTaskIndependent)
Adds a subtask to this task.
Definition: qgstaskmanager.cpp:155
QgsTaskManager::taskId
long taskId(QgsTask *task) const
Returns the unique task ID corresponding to a task managed by the class.
Definition: qgstaskmanager.cpp:529
QgsTaskManager::dependencies
QSet< long > dependencies(long taskId) const
Returns the set of task IDs on which a task is dependent.
Definition: qgstaskmanager.cpp:580
QgsTaskList
QList< QgsTask * > QgsTaskList
List of QgsTask objects.
Definition: qgstaskmanager.h:33
QgsTask::Terminated
@ Terminated
Task was terminated or errored.
Definition: qgstaskmanager.h:67
QgsTaskManager::countActiveTasks
int countActiveTasks(bool includeHidden=true) const
Returns the number of active (queued or running) tasks.
Definition: qgstaskmanager.cpp:666
QgsTaskManager::cancelAll
void cancelAll()
Instructs all tasks tracked by the manager to terminate.
Definition: qgstaskmanager.cpp:546
QgsMapLayer
Base class for all map layer types. This is the base class for all map layer types (vector,...
Definition: qgsmaplayer.h:72
QgsTaskManager::taskAdded
void taskAdded(long taskId)
Emitted when a new task has been added to the manager.
QgsTask::elapsedTime
qint64 elapsedTime() const
Returns the elapsed time since the task commenced, in milliseconds.
Definition: qgstaskmanager.cpp:56
QgsTaskManager::countActiveTasksChanged
void countActiveTasksChanged(int count)
Emitted when the number of active tasks changes.
QgsTaskManager::taskTriggered
void taskTriggered(QgsTask *task)
Emitted when a task is triggered.
QgsTask::~QgsTask
~QgsTask() override
Definition: qgstaskmanager.cpp:37
QgsTask::flags
Flags flags() const
Returns the flags associated with the task.
Definition: qgstaskmanager.h:94
QgsTaskManager::tasksDependentOnLayer
QList< QgsTask * > tasksDependentOnLayer(QgsMapLayer *layer) const
Returns a list of tasks which depend on a layer.
Definition: qgstaskmanager.cpp:641
QgsTask::run
virtual bool run()=0
Performs the task's operation.
QgsTask::OnHold
@ OnHold
Task is queued but on hold and will not be started.
Definition: qgstaskmanager.h:64
qgsproject.h
QgsTask::waitForFinished
bool waitForFinished(int timeout=30000)
Blocks the current thread until the task finishes or a maximum of timeout milliseconds.
Definition: qgstaskmanager.cpp:168
QgsTask::begun
void begun()
Will be emitted by task to indicate its commencement.
QgsTaskManager::allTasksFinished
void allTasksFinished()
Emitted when all tasks are complete.
QgsTask::progress
double progress() const
Returns the task's progress (between 0.0 and 100.0)
Definition: qgstaskmanager.h:127
QgsTask::isCanceled
bool isCanceled() const
Will return true if task should terminate ASAP.
Definition: qgstaskmanager.cpp:118
QgsTask
Abstract base class for long running background tasks. Tasks can be controlled directly,...
Definition: qgstaskmanager.h:54