QGIS API Documentation 3.29.0-Master (da8bb1db43)
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 , mTaskMutex( new QRecursiveMutex() )
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
416long QgsTaskManager::addTask( QgsTask *task, int priority )
417{
418 return addTaskPrivate( task, QgsTaskList(), false, priority );
419}
420
421long QgsTaskManager::addTask( const QgsTaskManager::TaskDefinition &definition, int priority )
422{
423 return addTaskPrivate( definition.task,
424 definition.dependentTasks,
425 false,
426 priority );
427}
428
429
430long 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 : std::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 if ( !( task->flags() & QgsTask::Hidden ) )
496 emit taskAdded( taskId );
497
498 processQueue();
499 }
500
501 return taskId;
502}
503
505{
506 QMutexLocker ml( mTaskMutex );
507 QgsTask *t = nullptr;
508 if ( mTasks.contains( id ) )
509 t = mTasks.value( id ).task;
510 return t;
511}
512
513QList<QgsTask *> QgsTaskManager::tasks() const
514{
515 QMutexLocker ml( mTaskMutex );
516 return QList<QgsTask *>( mParentTasks.begin(), mParentTasks.end() );
517}
518
520{
521 QMutexLocker ml( mTaskMutex );
522 return mParentTasks.count();
523}
524
526{
527 if ( !task )
528 return -1;
529
530 QMutexLocker ml( mTaskMutex );
531 QMap< long, TaskInfo >::const_iterator it = mTasks.constBegin();
532 for ( ; it != mTasks.constEnd(); ++it )
533 {
534 if ( it.value().task == task )
535 {
536 return it.key();
537 }
538 }
539 return -1;
540}
541
543{
544 mTaskMutex->lock();
545 QSet< QgsTask * > parents = mParentTasks;
546 parents.detach();
547 mTaskMutex->unlock();
548
549 const auto constParents = parents;
550 for ( QgsTask *task : constParents )
551 {
552 task->cancel();
553 }
554}
555
557{
558 mTaskMutex->lock();
559 QMap< long, QgsTaskList > dependencies = mTaskDependencies;
560 dependencies.detach();
561 mTaskMutex->unlock();
562
563 if ( !dependencies.contains( taskId ) )
564 return true;
565
566 const auto constValue = dependencies.value( taskId );
567 for ( QgsTask *task : constValue )
568 {
569 if ( task->status() != QgsTask::Complete )
570 return false;
571 }
572
573 return true;
574}
575
576QSet<long> QgsTaskManager::dependencies( long taskId ) const
577{
578 QSet<long> results;
579 if ( resolveDependencies( taskId, taskId, results ) )
580 return results;
581 else
582 return QSet<long>();
583}
584
585bool QgsTaskManager::resolveDependencies( long firstTaskId, long currentTaskId, QSet<long> &results ) const
586{
587 mTaskMutex->lock();
588 QMap< long, QgsTaskList > dependencies = mTaskDependencies;
589 dependencies.detach();
590 mTaskMutex->unlock();
591
592 if ( !dependencies.contains( currentTaskId ) )
593 return true;
594
595 const auto constValue = dependencies.value( currentTaskId );
596 for ( QgsTask *task : constValue )
597 {
598 long dependentTaskId = taskId( task );
599 if ( dependentTaskId >= 0 )
600 {
601 if ( dependentTaskId == firstTaskId )
602 // circular
603 return false;
604
605 //add task as dependent
606 results.insert( dependentTaskId );
607 //plus all its other dependencies
608 QSet< long > newTaskDeps;
609 if ( !resolveDependencies( firstTaskId, dependentTaskId, newTaskDeps ) )
610 return false;
611
612 if ( newTaskDeps.contains( firstTaskId ) )
613 {
614 // circular
615 return false;
616 }
617
618 results.unite( newTaskDeps );
619 }
620 }
621
622 return true;
623}
624
625bool QgsTaskManager::hasCircularDependencies( long taskId ) const
626{
627 QSet< long > d;
628 return !resolveDependencies( taskId, taskId, d );
629}
630
631QList<QgsMapLayer *> QgsTaskManager::dependentLayers( long taskId ) const
632{
633 QMutexLocker ml( mTaskMutex );
634 return _qgis_listQPointerToRaw( mLayerDependencies.value( taskId, QgsWeakMapLayerPointerList() ) );
635}
636
638{
639 QMutexLocker ml( mTaskMutex );
640 QList< QgsTask * > tasks;
641 QMap< long, QgsWeakMapLayerPointerList >::const_iterator layerIt = mLayerDependencies.constBegin();
642 for ( ; layerIt != mLayerDependencies.constEnd(); ++layerIt )
643 {
644 if ( _qgis_listQPointerToRaw( layerIt.value() ).contains( layer ) )
645 {
646 QgsTask *layerTask = task( layerIt.key() );
647 if ( layerTask )
648 tasks << layerTask;
649 }
650 }
651 return tasks;
652}
653
654QList<QgsTask *> QgsTaskManager::activeTasks() const
655{
656 QMutexLocker ml( mTaskMutex );
657 QSet< QgsTask * > activeTasks = mActiveTasks;
658 activeTasks.intersect( mParentTasks );
659 return QList<QgsTask *>( activeTasks.constBegin(), activeTasks.constEnd() );
660}
661
662int QgsTaskManager::countActiveTasks( bool includeHidden ) const
663{
664 QMutexLocker ml( mTaskMutex );
665 QSet< QgsTask * > tasks = mActiveTasks;
666
667 if ( !includeHidden )
668 {
669 QSet< QgsTask * > filteredTasks;
670 filteredTasks.reserve( tasks.size() );
671 for ( QgsTask *task : tasks )
672 {
673 if ( !( task->flags() & QgsTask::Hidden ) )
674 filteredTasks.insert( task );
675 }
676 tasks = filteredTasks;
677 }
678
679 return tasks.intersect( mParentTasks ).count();
680}
681
683{
684 if ( task )
685 emit taskTriggered( task );
686}
687
688void QgsTaskManager::taskProgressChanged( double progress )
689{
690 QgsTask *task = qobject_cast< QgsTask * >( sender() );
691 if ( task && task->flags() & QgsTask::Hidden )
692 return;
693
694 //find ID of task
695 long id = taskId( task );
696 if ( id < 0 )
697 return;
698
699 emit progressChanged( id, progress );
700
701 if ( countActiveTasks( false ) == 1 )
702 {
703 emit finalTaskProgressChanged( progress );
704 }
705}
706
707void QgsTaskManager::taskStatusChanged( int status )
708{
709 QgsTask *task = qobject_cast< QgsTask * >( sender() );
710 const bool isHidden = task && task->flags() & QgsTask::Hidden;
711
712 //find ID of task
713 long id = taskId( task );
714 if ( id < 0 )
715 return;
716
717 mTaskMutex->lock();
718 QgsTaskRunnableWrapper *runnable = mTasks.value( id ).runnable;
719 mTaskMutex->unlock();
720 if ( runnable && QThreadPool::globalInstance()->tryTake( runnable ) )
721 {
722 delete runnable;
723 mTasks[ id ].runnable = nullptr;
724 }
725
726 if ( status == QgsTask::Terminated || status == QgsTask::Complete )
727 {
728 bool result = status == QgsTask::Complete;
729 task->finished( result );
730 }
731
732 if ( status == QgsTask::Terminated )
733 {
734 //recursively cancel dependent tasks
735 cancelDependentTasks( id );
736 }
737
738 mTaskMutex->lock();
739 bool isParent = mParentTasks.contains( task );
740 mTaskMutex->unlock();
741 if ( isParent && !isHidden )
742 {
743 // don't emit status changed for subtasks
744 emit statusChanged( id, status );
745 }
746
747 processQueue();
748
749 if ( status == QgsTask::Terminated || status == QgsTask::Complete )
750 {
751 cleanupAndDeleteTask( task );
752 }
753
754}
755
756void QgsTaskManager::layersWillBeRemoved( const QList< QgsMapLayer * > &layers )
757{
758 mTaskMutex->lock();
759 // scan through layers to be removed
760 QMap< long, QgsWeakMapLayerPointerList > layerDependencies = mLayerDependencies;
761 layerDependencies.detach();
762 mTaskMutex->unlock();
763
764 const auto constLayers = layers;
765 for ( QgsMapLayer *layer : constLayers )
766 {
767 // scan through tasks with layer dependencies
768 for ( QMap< long, QgsWeakMapLayerPointerList >::const_iterator it = layerDependencies.constBegin();
769 it != layerDependencies.constEnd(); ++it )
770 {
771 if ( !( _qgis_listQPointerToRaw( it.value() ).contains( layer ) ) )
772 {
773 //task not dependent on this layer
774 continue;
775 }
776
777 QgsTask *dependentTask = task( it.key() );
778 if ( dependentTask && ( dependentTask->status() != QgsTask::Complete && dependentTask->status() != QgsTask::Terminated ) )
779 {
780 // incomplete task is dependent on this layer!
781 dependentTask->cancel();
782 }
783 }
784 }
785}
786
787
788bool QgsTaskManager::cleanupAndDeleteTask( QgsTask *task )
789{
790 if ( !task )
791 return false;
792
793 long id = taskId( task );
794 if ( id < 0 )
795 return false;
796
797 QgsTaskRunnableWrapper *runnable = mTasks.value( id ).runnable;
798
799 task->disconnect( this );
800
801 mTaskMutex->lock();
802 if ( mTaskDependencies.contains( id ) )
803 mTaskDependencies.remove( id );
804 mTaskMutex->unlock();
805
806 emit taskAboutToBeDeleted( id );
807
808 mTaskMutex->lock();
809 bool isParent = mParentTasks.contains( task );
810 mParentTasks.remove( task );
811 mSubTasks.remove( task );
812 mTasks.remove( id );
813 mLayerDependencies.remove( id );
814
816 {
817 if ( isParent )
818 {
819 // delete task when it's terminated
820 connect( task, &QgsTask::taskCompleted, task, &QgsTask::deleteLater );
821 connect( task, &QgsTask::taskTerminated, task, &QgsTask::deleteLater );
822 }
823 task->cancel();
824 }
825 else
826 {
827 if ( runnable && QThreadPool::globalInstance()->tryTake( runnable ) )
828 {
829 delete runnable;
830 mTasks[ id ].runnable = nullptr;
831 }
832
833 if ( isParent )
834 {
835 //task already finished, kill it
836 task->deleteLater();
837 }
838 }
839
840 // at this stage (hopefully) dependent tasks have been canceled or queued
841 for ( QMap< long, QgsTaskList >::iterator it = mTaskDependencies.begin(); it != mTaskDependencies.end(); ++it )
842 {
843 if ( it.value().contains( task ) )
844 {
845 it.value().removeAll( task );
846 }
847 }
848 mTaskMutex->unlock();
849
850 return true;
851}
852
853void QgsTaskManager::processQueue()
854{
855 int prevActiveCount = countActiveTasks( false );
856 mTaskMutex->lock();
857 mActiveTasks.clear();
858 for ( QMap< long, TaskInfo >::iterator it = mTasks.begin(); it != mTasks.end(); ++it )
859 {
860 QgsTask *task = it.value().task;
861 if ( task && task->mStatus == QgsTask::Queued && dependenciesSatisfied( it.key() ) && it.value().added.testAndSetRelaxed( 0, 1 ) )
862 {
863 it.value().createRunnable();
864 QThreadPool::globalInstance()->start( it.value().runnable, it.value().priority );
865 }
866
867 if ( task && ( task->mStatus != QgsTask::Complete && task->mStatus != QgsTask::Terminated ) )
868 {
869 mActiveTasks << task;
870 }
871 }
872
873 bool allFinished = mActiveTasks.isEmpty();
874 mTaskMutex->unlock();
875
876 if ( allFinished )
877 {
878 emit allTasksFinished();
879 }
880
881 int newActiveCount = countActiveTasks( false );
882 if ( prevActiveCount != newActiveCount )
883 {
884 emit countActiveTasksChanged( newActiveCount );
885 }
886}
887
888void QgsTaskManager::cancelDependentTasks( long taskId )
889{
890 QgsTask *canceledTask = task( taskId );
891
892 //deep copy
893 mTaskMutex->lock();
894 QMap< long, QgsTaskList > taskDependencies = mTaskDependencies;
895 taskDependencies.detach();
896 mTaskMutex->unlock();
897
898 QMap< long, QgsTaskList >::const_iterator it = taskDependencies.constBegin();
899 for ( ; it != taskDependencies.constEnd(); ++it )
900 {
901 if ( it.value().contains( canceledTask ) )
902 {
903 // found task with this dependency
904
905 // cancel it - note that this will be recursive, so any tasks dependent
906 // on this one will also be canceled
907 QgsTask *dependentTask = task( it.key() );
908 if ( dependentTask )
909 dependentTask->cancel();
910 }
911 }
912}
913
914QgsTaskManager::TaskInfo::TaskInfo( QgsTask *task, int priority )
915 : task( task )
916 , added( 0 )
917 , priority( priority )
918{}
919
920void QgsTaskManager::TaskInfo::createRunnable()
921{
922 Q_ASSERT( !runnable );
923 runnable = new QgsTaskRunnableWrapper( task ); // auto deleted
924}
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:106
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:477
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:2206
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.