Skip to content
Permalink
Browse files

Merge pull request #34469 from troopa81/fix_taskmgr_raceconditions

Fix Task manager race conditions issues
  • Loading branch information
rouault committed Mar 11, 2020
2 parents 7fe1742 + a9452e8 commit 136ad8c87936f889fa6a5e3d65f2327b3137c034
Showing with 244 additions and 103 deletions.
  1. +14 −8 src/core/qgstaskmanager.cpp
  2. +8 −4 src/core/qgstaskmanager.h
  3. +210 −79 tests/src/core/testqgstaskmanager.cpp
  4. +12 −12 tests/src/python/test_qgstaskmanager.py
@@ -36,7 +36,8 @@ QgsTask::QgsTask( const QString &name, Flags flags )
QgsTask::~QgsTask()
{
Q_ASSERT_X( mStatus != Running, "delete", QStringLiteral( "status was %1" ).arg( mStatus ).toLatin1() );
mNotFinishedMutex.tryLock(); // we're not guaranteed to already have the lock in place here
// even here we are not sure that task start method has ended
mNotFinishedMutex.lock();
const auto constMSubTasks = mSubTasks;
for ( const SubTask &subTask : constMSubTasks )
{
@@ -58,7 +59,7 @@ qint64 QgsTask::elapsedTime() const

void QgsTask::start()
{
mNotFinishedMutex.lock();
QMutexLocker locker( &mNotFinishedMutex );
mNotStartedMutex.release();
mStartCount++;
Q_ASSERT( mStartCount == 1 );
@@ -91,7 +92,9 @@ void QgsTask::cancel()
if ( mOverallStatus == Complete || mOverallStatus == Terminated )
return;

mShouldTerminateMutex.lock();
mShouldTerminate = true;
mShouldTerminateMutex.unlock();
if ( mStatus == Queued || mStatus == OnHold )
{
// immediately terminate unstarted jobs
@@ -111,6 +114,12 @@ void QgsTask::cancel()
}
}

bool QgsTask::isCanceled() const
{
QMutexLocker locker( &mShouldTerminateMutex );
return mShouldTerminate;
}

void QgsTask::hold()
{
if ( mStatus == Queued )
@@ -172,6 +181,7 @@ bool QgsTask::waitForFinished( int timeout )
if ( mNotFinishedMutex.tryLock( timeout ) )
{
mNotFinishedMutex.unlock();
QCoreApplication::sendPostedEvents( this );
rv = true;
}
else
@@ -254,7 +264,7 @@ void QgsTask::setProgress( double progress )
void QgsTask::completed()
{
mStatus = Complete;
processSubTasksForCompletion();
QMetaObject::invokeMethod( this, "processSubTasksForCompletion" );
}

void QgsTask::processSubTasksForCompletion()
@@ -277,8 +287,6 @@ void QgsTask::processSubTasksForCompletion()
setProgress( 100.0 );
emit statusChanged( Complete );
emit taskCompleted();
mNotFinishedMutex.tryLock(); // we're not guaranteed to already have the lock in place here
mNotFinishedMutex.unlock();
}
else if ( mStatus == Complete )
{
@@ -306,8 +314,6 @@ void QgsTask::processSubTasksForTermination()

emit statusChanged( Terminated );
emit taskTerminated();
mNotFinishedMutex.tryLock(); // we're not guaranteed to already have the lock in place here
mNotFinishedMutex.unlock();
}
else if ( mStatus == Terminated && !subTasksTerminated )
{
@@ -344,7 +350,7 @@ void QgsTask::processSubTasksForHold()
void QgsTask::terminated()
{
mStatus = Terminated;
processSubTasksForTermination();
QMetaObject::invokeMethod( this, "processSubTasksForTermination" );
}


@@ -282,7 +282,7 @@ class CORE_EXPORT QgsTask : public QObject
* flag, then derived classes' run() methods should periodically check this and
* terminate in a safe manner.
*/
bool isCanceled() const { return mShouldTerminate; }
bool isCanceled() const;

protected slots:

@@ -322,6 +322,7 @@ class CORE_EXPORT QgsTask : public QObject
//! Overall progress of this task and all subtasks
double mTotalProgress = 0.0;
bool mShouldTerminate = false;
mutable QMutex mShouldTerminateMutex;
int mStartCount = 0;

struct SubTask
@@ -357,16 +358,19 @@ class CORE_EXPORT QgsTask : public QObject
*/
void terminated();

void processSubTasksForCompletion();

void processSubTasksForTermination();

void processSubTasksForHold();

friend class QgsTaskManager;
friend class QgsTaskRunnableWrapper;
friend class TestQgsTaskManager;

private slots:

void processSubTasksForCompletion();

void processSubTasksForTermination();

};


0 comments on commit 136ad8c

Please sign in to comment.
You can’t perform that action at this time.