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