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