QGIS API Documentation  3.14.0-Pi (9f7028fd23)
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.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  , mTaskMutex( new QMutex( QMutex::Recursive ) )
393 {
394 
395 }
396 
398 {
399  //first tell all tasks to cancel
400  cancelAll();
401 
402  //then clean them up, including waiting for them to terminate
403  mTaskMutex->lock();
404  QMap< long, TaskInfo > tasks = mTasks;
405  mTasks.detach();
406  mTaskMutex->unlock();
407  QMap< long, TaskInfo >::const_iterator it = tasks.constBegin();
408  for ( ; it != tasks.constEnd(); ++it )
409  {
410  cleanupAndDeleteTask( it.value().task );
411  }
412 
413  delete mTaskMutex;
414 }
415 
416 long QgsTaskManager::addTask( QgsTask *task, int priority )
417 {
418  return addTaskPrivate( task, QgsTaskList(), false, priority );
419 }
420 
421 long QgsTaskManager::addTask( const QgsTaskManager::TaskDefinition &definition, int priority )
422 {
423  return addTaskPrivate( definition.task,
424  definition.dependentTasks,
425  false,
426  priority );
427 }
428 
429 
430 long QgsTaskManager::addTaskPrivate( QgsTask *task, QgsTaskList dependencies, bool isSubTask, int priority )
431 {
432  if ( !task )
433  return 0;
434 
435  if ( !mInitialized )
436  {
437  mInitialized = true;
438  // defer connection to project until we actually need it -- we don't want to connect to the project instance in the constructor,
439  // cos that forces early creation of QgsProject
440  connect( QgsProject::instance(), static_cast < void ( QgsProject::* )( const QList< QgsMapLayer * >& ) > ( &QgsProject::layersWillBeRemoved ),
441  this, &QgsTaskManager::layersWillBeRemoved );
442  }
443 
444  long taskId = mNextTaskId++;
445 
446  mTaskMutex->lock();
447  mTasks.insert( taskId, TaskInfo( task, priority ) );
448  if ( isSubTask )
449  {
450  mSubTasks << task;
451  }
452  else
453  {
454  mParentTasks << task;
455  }
456  if ( !task->dependentLayers().isEmpty() )
457  mLayerDependencies.insert( taskId, _qgis_listRawToQPointer( task->dependentLayers() ) );
458  mTaskMutex->unlock();
459 
460  connect( task, &QgsTask::statusChanged, this, &QgsTaskManager::taskStatusChanged );
461  if ( !isSubTask )
462  {
463  connect( task, &QgsTask::progressChanged, this, &QgsTaskManager::taskProgressChanged );
464  }
465 
466  // add all subtasks, must be done before dependency resolution
467  for ( const QgsTask::SubTask &subTask : qgis::as_const( task->mSubTasks ) )
468  {
469  switch ( subTask.dependency )
470  {
472  dependencies << subTask.task;
473  break;
474 
476  //nothing
477  break;
478  }
479  //recursively add sub tasks
480  addTaskPrivate( subTask.task, subTask.dependencies, true, priority );
481  }
482 
483  if ( !dependencies.isEmpty() )
484  {
485  mTaskDependencies.insert( taskId, dependencies );
486  }
487 
488  if ( hasCircularDependencies( taskId ) )
489  {
490  task->cancel();
491  }
492 
493  if ( !isSubTask )
494  {
495  emit taskAdded( taskId );
496  processQueue();
497  }
498 
499  return taskId;
500 }
501 
502 QgsTask *QgsTaskManager::task( long id ) const
503 {
504  QMutexLocker ml( mTaskMutex );
505  QgsTask *t = nullptr;
506  if ( mTasks.contains( id ) )
507  t = mTasks.value( id ).task;
508  return t;
509 }
510 
511 QList<QgsTask *> QgsTaskManager::tasks() const
512 {
513  QMutexLocker ml( mTaskMutex );
514  return qgis::setToList( mParentTasks );
515 }
516 
518 {
519  QMutexLocker ml( mTaskMutex );
520  return mParentTasks.count();
521 }
522 
523 long QgsTaskManager::taskId( QgsTask *task ) const
524 {
525  if ( !task )
526  return -1;
527 
528  QMutexLocker ml( mTaskMutex );
529  QMap< long, TaskInfo >::const_iterator it = mTasks.constBegin();
530  for ( ; it != mTasks.constEnd(); ++it )
531  {
532  if ( it.value().task == task )
533  {
534  return it.key();
535  }
536  }
537  return -1;
538 }
539 
541 {
542  mTaskMutex->lock();
543  QSet< QgsTask * > parents = mParentTasks;
544  parents.detach();
545  mTaskMutex->unlock();
546 
547  const auto constParents = parents;
548  for ( QgsTask *task : constParents )
549  {
550  task->cancel();
551  }
552 }
553 
554 bool QgsTaskManager::dependenciesSatisfied( long taskId ) const
555 {
556  mTaskMutex->lock();
557  QMap< long, QgsTaskList > dependencies = mTaskDependencies;
558  dependencies.detach();
559  mTaskMutex->unlock();
560 
561  if ( !dependencies.contains( taskId ) )
562  return true;
563 
564  const auto constValue = dependencies.value( taskId );
565  for ( QgsTask *task : constValue )
566  {
567  if ( task->status() != QgsTask::Complete )
568  return false;
569  }
570 
571  return true;
572 }
573 
574 QSet<long> QgsTaskManager::dependencies( long taskId ) const
575 {
576  QSet<long> results;
577  if ( resolveDependencies( taskId, taskId, results ) )
578  return results;
579  else
580  return QSet<long>();
581 }
582 
583 bool QgsTaskManager::resolveDependencies( long firstTaskId, long currentTaskId, QSet<long> &results ) const
584 {
585  mTaskMutex->lock();
586  QMap< long, QgsTaskList > dependencies = mTaskDependencies;
587  dependencies.detach();
588  mTaskMutex->unlock();
589 
590  if ( !dependencies.contains( currentTaskId ) )
591  return true;
592 
593  const auto constValue = dependencies.value( currentTaskId );
594  for ( QgsTask *task : constValue )
595  {
596  long dependentTaskId = taskId( task );
597  if ( dependentTaskId >= 0 )
598  {
599  if ( dependentTaskId == firstTaskId )
600  // circular
601  return false;
602 
603  //add task as dependent
604  results.insert( dependentTaskId );
605  //plus all its other dependencies
606  QSet< long > newTaskDeps;
607  if ( !resolveDependencies( firstTaskId, dependentTaskId, newTaskDeps ) )
608  return false;
609 
610  if ( newTaskDeps.contains( firstTaskId ) )
611  {
612  // circular
613  return false;
614  }
615 
616  results.unite( newTaskDeps );
617  }
618  }
619 
620  return true;
621 }
622 
623 bool QgsTaskManager::hasCircularDependencies( long taskId ) const
624 {
625  QSet< long > d;
626  return !resolveDependencies( taskId, taskId, d );
627 }
628 
629 QList<QgsMapLayer *> QgsTaskManager::dependentLayers( long taskId ) const
630 {
631  QMutexLocker ml( mTaskMutex );
632  return _qgis_listQPointerToRaw( mLayerDependencies.value( taskId, QgsWeakMapLayerPointerList() ) );
633 }
634 
635 QList<QgsTask *> QgsTaskManager::tasksDependentOnLayer( QgsMapLayer *layer ) const
636 {
637  QMutexLocker ml( mTaskMutex );
638  QList< QgsTask * > tasks;
639  QMap< long, QgsWeakMapLayerPointerList >::const_iterator layerIt = mLayerDependencies.constBegin();
640  for ( ; layerIt != mLayerDependencies.constEnd(); ++layerIt )
641  {
642  if ( _qgis_listQPointerToRaw( layerIt.value() ).contains( layer ) )
643  {
644  QgsTask *layerTask = task( layerIt.key() );
645  if ( layerTask )
646  tasks << layerTask;
647  }
648  }
649  return tasks;
650 }
651 
652 QList<QgsTask *> QgsTaskManager::activeTasks() const
653 {
654  QMutexLocker ml( mTaskMutex );
655  QSet< QgsTask * > activeTasks = mActiveTasks;
656  activeTasks.intersect( mParentTasks );
657  return qgis::setToList( activeTasks );
658 }
659 
661 {
662  QMutexLocker ml( mTaskMutex );
663  QSet< QgsTask * > tasks = mActiveTasks;
664  return tasks.intersect( mParentTasks ).count();
665 }
666 
668 {
669  if ( task )
670  emit taskTriggered( task );
671 }
672 
673 void QgsTaskManager::taskProgressChanged( double progress )
674 {
675  QgsTask *task = qobject_cast< QgsTask * >( sender() );
676 
677  //find ID of task
678  long id = taskId( task );
679  if ( id < 0 )
680  return;
681 
682  emit progressChanged( id, progress );
683 
684  if ( countActiveTasks() == 1 )
685  {
686  emit finalTaskProgressChanged( progress );
687  }
688 }
689 
690 void QgsTaskManager::taskStatusChanged( int status )
691 {
692  QgsTask *task = qobject_cast< QgsTask * >( sender() );
693 
694  //find ID of task
695  long id = taskId( task );
696  if ( id < 0 )
697  return;
698 
699  mTaskMutex->lock();
700  QgsTaskRunnableWrapper *runnable = mTasks.value( id ).runnable;
701  mTaskMutex->unlock();
702  if ( runnable && QThreadPool::globalInstance()->tryTake( runnable ) )
703  {
704  delete runnable;
705  mTasks[ id ].runnable = nullptr;
706  }
707 
708  if ( status == QgsTask::Terminated || status == QgsTask::Complete )
709  {
710  bool result = status == QgsTask::Complete;
711  task->finished( result );
712  }
713 
714  if ( status == QgsTask::Terminated )
715  {
716  //recursively cancel dependent tasks
717  cancelDependentTasks( id );
718  }
719 
720  mTaskMutex->lock();
721  bool isParent = mParentTasks.contains( task );
722  mTaskMutex->unlock();
723  if ( isParent )
724  {
725  // don't emit status changed for subtasks
726  emit statusChanged( id, status );
727  }
728 
729  processQueue();
730 
731  if ( status == QgsTask::Terminated || status == QgsTask::Complete )
732  {
733  cleanupAndDeleteTask( task );
734  }
735 
736 }
737 
738 void QgsTaskManager::layersWillBeRemoved( const QList< QgsMapLayer * > &layers )
739 {
740  mTaskMutex->lock();
741  // scan through layers to be removed
742  QMap< long, QgsWeakMapLayerPointerList > layerDependencies = mLayerDependencies;
743  layerDependencies.detach();
744  mTaskMutex->unlock();
745 
746  const auto constLayers = layers;
747  for ( QgsMapLayer *layer : constLayers )
748  {
749  // scan through tasks with layer dependencies
750  for ( QMap< long, QgsWeakMapLayerPointerList >::const_iterator it = layerDependencies.constBegin();
751  it != layerDependencies.constEnd(); ++it )
752  {
753  if ( !( _qgis_listQPointerToRaw( it.value() ).contains( layer ) ) )
754  {
755  //task not dependent on this layer
756  continue;
757  }
758 
759  QgsTask *dependentTask = task( it.key() );
760  if ( dependentTask && ( dependentTask->status() != QgsTask::Complete && dependentTask->status() != QgsTask::Terminated ) )
761  {
762  // incomplete task is dependent on this layer!
763  dependentTask->cancel();
764  }
765  }
766  }
767 }
768 
769 
770 bool QgsTaskManager::cleanupAndDeleteTask( QgsTask *task )
771 {
772  if ( !task )
773  return false;
774 
775  long id = taskId( task );
776  if ( id < 0 )
777  return false;
778 
779  QgsTaskRunnableWrapper *runnable = mTasks.value( id ).runnable;
780 
781  task->disconnect( this );
782 
783  mTaskMutex->lock();
784  if ( mTaskDependencies.contains( id ) )
785  mTaskDependencies.remove( id );
786  mTaskMutex->unlock();
787 
788  emit taskAboutToBeDeleted( id );
789 
790  mTaskMutex->lock();
791  bool isParent = mParentTasks.contains( task );
792  mParentTasks.remove( task );
793  mSubTasks.remove( task );
794  mTasks.remove( id );
795  mLayerDependencies.remove( id );
796 
798  {
799  if ( isParent )
800  {
801  // delete task when it's terminated
802  connect( task, &QgsTask::taskCompleted, task, &QgsTask::deleteLater );
803  connect( task, &QgsTask::taskTerminated, task, &QgsTask::deleteLater );
804  }
805  task->cancel();
806  }
807  else
808  {
809  if ( runnable && QThreadPool::globalInstance()->tryTake( runnable ) )
810  {
811  delete runnable;
812  mTasks[ id ].runnable = nullptr;
813  }
814 
815  if ( isParent )
816  {
817  //task already finished, kill it
818  task->deleteLater();
819  }
820  }
821 
822  // at this stage (hopefully) dependent tasks have been canceled or queued
823  for ( QMap< long, QgsTaskList >::iterator it = mTaskDependencies.begin(); it != mTaskDependencies.end(); ++it )
824  {
825  if ( it.value().contains( task ) )
826  {
827  it.value().removeAll( task );
828  }
829  }
830  mTaskMutex->unlock();
831 
832  return true;
833 }
834 
835 void QgsTaskManager::processQueue()
836 {
837  int prevActiveCount = countActiveTasks();
838  mTaskMutex->lock();
839  mActiveTasks.clear();
840  for ( QMap< long, TaskInfo >::iterator it = mTasks.begin(); it != mTasks.end(); ++it )
841  {
842  QgsTask *task = it.value().task;
843  if ( task && task->mStatus == QgsTask::Queued && dependenciesSatisfied( it.key() ) && it.value().added.testAndSetRelaxed( 0, 1 ) )
844  {
845  it.value().createRunnable();
846  QThreadPool::globalInstance()->start( it.value().runnable, it.value().priority );
847  }
848 
849  if ( task && ( task->mStatus != QgsTask::Complete && task->mStatus != QgsTask::Terminated ) )
850  {
851  mActiveTasks << task;
852  }
853  }
854 
855  bool allFinished = mActiveTasks.isEmpty();
856  mTaskMutex->unlock();
857 
858  if ( allFinished )
859  {
860  emit allTasksFinished();
861  }
862 
863  int newActiveCount = countActiveTasks();
864  if ( prevActiveCount != newActiveCount )
865  {
866  emit countActiveTasksChanged( newActiveCount );
867  }
868 }
869 
870 void QgsTaskManager::cancelDependentTasks( long taskId )
871 {
872  QgsTask *canceledTask = task( taskId );
873 
874  //deep copy
875  mTaskMutex->lock();
876  QMap< long, QgsTaskList > taskDependencies = mTaskDependencies;
877  taskDependencies.detach();
878  mTaskMutex->unlock();
879 
880  QMap< long, QgsTaskList >::const_iterator it = taskDependencies.constBegin();
881  for ( ; it != taskDependencies.constEnd(); ++it )
882  {
883  if ( it.value().contains( canceledTask ) )
884  {
885  // found task with this dependency
886 
887  // cancel it - note that this will be recursive, so any tasks dependent
888  // on this one will also be canceled
889  QgsTask *dependentTask = task( it.key() );
890  if ( dependentTask )
891  dependentTask->cancel();
892  }
893  }
894 }
895 
896 QgsTaskManager::TaskInfo::TaskInfo( QgsTask *task, int priority )
897  : task( task )
898  , added( 0 )
899  , priority( priority )
900 {}
901 
902 void QgsTaskManager::TaskInfo::createRunnable()
903 {
904  Q_ASSERT( !runnable );
905  runnable = new QgsTaskRunnableWrapper( task ); // auto deleted
906 }
QgsTaskManager::tasks
QList< QgsTask * > tasks() const
Returns all tasks tracked by the manager.
Definition: qgstaskmanager.cpp:511
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:652
QgsWeakMapLayerPointerList
QList< QgsWeakMapLayerPointer > QgsWeakMapLayerPointerList
A list of weak pointers to QgsMapLayers.
Definition: qgsmaplayer.h:1677
QgsTaskManager::TaskDefinition::dependentTasks
QgsTaskList dependentTasks
List of dependent tasks which must be completed before task can run.
Definition: qgstaskmanager.h:425
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:554
QgsTask::SubTaskDependency
SubTaskDependency
Controls how subtasks relate to their parent task.
Definition: qgstaskmanager.h:163
QgsTask::Complete
@ Complete
Task successfully completed.
Definition: qgstaskmanager.h:64
QgsProject::instance
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:458
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:629
QgsTask::description
QString description() const
Returns the task's description.
Definition: qgstaskmanager.h:118
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:165
QgsTask::Running
@ Running
Task is currently running.
Definition: qgstaskmanager.h:63
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:517
QgsProject
Definition: qgsproject.h:92
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:280
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:416
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:61
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:405
QgsTask::status
TaskStatus status() const
Returns the current task status.
Definition: qgstaskmanager.h:113
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:502
QgsTaskManager::triggerTask
void triggerTask(QgsTask *task)
Triggers a task, e.g.
Definition: qgstaskmanager.cpp:667
QgsTaskManager::~QgsTaskManager
~QgsTaskManager() override
Definition: qgstaskmanager.cpp:397
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
QgsTaskManager::TaskDefinition::task
QgsTask * task
Task.
Definition: qgstaskmanager.h:418
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:166
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:523
QgsTaskManager::dependencies
QSet< long > dependencies(long taskId) const
Returns the set of task IDs on which a task is dependent.
Definition: qgstaskmanager.cpp:574
QgsTaskList
QList< QgsTask * > QgsTaskList
List of QgsTask objects.
Definition: qgstaskmanager.h:31
QgsTask::Terminated
@ Terminated
Task was terminated or errored.
Definition: qgstaskmanager.h:65
QgsTaskManager::cancelAll
void cancelAll()
Instructs all tasks tracked by the manager to terminate.
Definition: qgstaskmanager.cpp:540
QgsMapLayer
Definition: qgsmaplayer.h:81
QgsTaskManager::taskAdded
void taskAdded(long taskId)
Emitted when a new task has been added to the manager.
qgsmaplayerlistutils.h
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
QgsTaskManager::tasksDependentOnLayer
QList< QgsTask * > tasksDependentOnLayer(QgsMapLayer *layer) const
Returns a list of tasks which depend on a layer.
Definition: qgstaskmanager.cpp:635
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:62
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:123
QgsTask::isCanceled
bool isCanceled() const
Will return true if task should terminate ASAP.
Definition: qgstaskmanager.cpp:118
QgsTaskManager::countActiveTasks
int countActiveTasks() const
Returns the number of active (queued or running) tasks.
Definition: qgstaskmanager.cpp:660
QgsTask
Abstract base class for long running background tasks. Tasks can be controlled directly,...
Definition: qgstaskmanager.h:52