QGIS API Documentation 3.27.0-Master (0a97e3138f)
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"
21#include <mutex>
22#include <QtConcurrentRun>
23
24
25//
26// QgsTask
27//
28
29QgsTask::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
51void QgsTask::setDescription( const QString &description )
52{
53 mDescription = description;
54}
55
57{
58 return mElapsedTime.elapsed();
59}
60
61void 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
155void 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
163QList<QgsMapLayer *> QgsTask::dependentLayers() const
164{
165 return _qgis_listQPointerToRaw( mDependentLayers );
166}
167
168bool 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
196void QgsTask::setDependentLayers( const QList< QgsMapLayer * > &dependentLayers )
197{
198 mDependentLayers = _qgis_listRawToQPointer( dependentLayers );
199}
200
201void 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
232void 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 ) )
263}
264
265void QgsTask::completed()
266{
267 mStatus = Complete;
268 QMetaObject::invokeMethod( this, "processSubTasksForCompletion" );
269}
270
271void 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
299void 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
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
326void 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
351void QgsTask::terminated()
352{
353 mStatus = Terminated;
354 QMetaObject::invokeMethod( this, "processSubTasksForTermination" );
355}
356
357
359
360class 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
420long QgsTaskManager::addTask( QgsTask *task, int priority )
421{
422 return addTaskPrivate( task, QgsTaskList(), false, priority );
423}
424
425long QgsTaskManager::addTask( const QgsTaskManager::TaskDefinition &definition, int priority )
426{
427 return addTaskPrivate( definition.task,
428 definition.dependentTasks,
429 false,
430 priority );
431}
432
433
434long 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
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
517QList<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
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
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
580QSet<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
589bool 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
629bool QgsTaskManager::hasCircularDependencies( long taskId ) const
630{
631 QSet< long > d;
632 return !resolveDependencies( taskId, taskId, d );
633}
634
635QList<QgsMapLayer *> QgsTaskManager::dependentLayers( long taskId ) const
636{
637 QMutexLocker ml( mTaskMutex );
638 return _qgis_listQPointerToRaw( mLayerDependencies.value( taskId, QgsWeakMapLayerPointerList() ) );
639}
640
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
658QList<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
666int 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
692void 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
711void 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
760void 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
792bool 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
857void 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
892void 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
918QgsTaskManager::TaskInfo::TaskInfo( QgsTask *task, int priority )
919 : task( task )
920 , added( 0 )
921 , priority( priority )
922{}
923
924void QgsTaskManager::TaskInfo::createRunnable()
925{
926 Q_ASSERT( !runnable );
927 runnable = new QgsTaskRunnableWrapper( task ); // auto deleted
928}
Base class for all map layer types.
Definition: qgsmaplayer.h:73
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:104
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:478
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 ...
~QgsTask() override
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.
Definition: qgsmaplayer.h:2153
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.