Skip to content
Permalink
Browse files

QgsTask::waitForFinished without event loop

because the old approach with using signals, using deleteLater and using
an internal event loop is a very good recipe for big troubles.
  • Loading branch information
m-kuhn committed Sep 21, 2017
1 parent 15e650d commit 88fcbb0aace22967e757970e43db5862701d07db
Showing with 28 additions and 21 deletions.
  1. +2 −2 python/core/qgstaskmanager.sip
  2. +20 −17 src/core/qgstaskmanager.cpp
  3. +6 −2 src/core/qgstaskmanager.h
@@ -170,10 +170,10 @@ class QgsTask : QObject
:rtype: list of QgsMapLayer
%End

bool waitForFinished( int timeout = 30000 );
bool waitForFinished( unsigned long timeout = 30000 );
%Docstring
Blocks the current thread until the task finishes or a maximum of ``timeout`` milliseconds.
If the ``timeout`` is ``-1`` the thread will be blocked forever.
If ``timeout`` is ``0`` the thread will be blocked forever.
In case of a timeout, the task will still be running.
In case the task already is finished, the method will return immediately while
returning ``true``.
@@ -35,7 +35,9 @@ QgsTask::QgsTask( const QString &name, const Flags &flags )
, mTotalProgress( 0.0 )
, mShouldTerminate( false )
, mStartCount( 0 )
{}
{
mNotFinishedMutex.lock();
}

QgsTask::~QgsTask()
{
@@ -142,26 +144,19 @@ QList<QgsMapLayer *> QgsTask::dependentLayers() const
return _qgis_listQPointerToRaw( mDependentLayers );
}

bool QgsTask::waitForFinished( int timeout )
bool QgsTask::waitForFinished( unsigned long timeout )
{
QEventLoop loop;
bool rv = true;

connect( this, &QgsTask::taskCompleted, &loop, &QEventLoop::quit );
connect( this, &QgsTask::taskTerminated, &loop, &QEventLoop::quit );
QTimer timer;

if ( timeout != -1 )
if ( mOverallStatus == Complete || mOverallStatus == Terminated )
{
timer.start( timeout );
connect( &timer, &QTimer::timeout, [&rv]() { rv = false; } );
connect( &timer, &QTimer::timeout, &loop, &QEventLoop::quit );
rv = true;
}
else
{
if ( timeout == 0 )
timeout = ULONG_MAX;
rv = mTaskFinished.wait( &mNotFinishedMutex, timeout );
}

if ( status() == QgsTask::Complete || status() == QgsTask::Terminated )
return true;
loop.exec();

return rv;
}

@@ -249,9 +244,13 @@ void QgsTask::processSubTasksForCompletion()
if ( mStatus == Complete && subTasksCompleted )
{
mOverallStatus = Complete;

setProgress( 100.0 );
emit statusChanged( Complete );
emit taskCompleted();
mTaskFinished.wakeAll();
mNotFinishedMutex.unlock();
mTaskFinished.wakeAll();
}
else if ( mStatus == Complete )
{
@@ -275,8 +274,12 @@ void QgsTask::processSubTasksForTermination()
if ( mStatus == Terminated && subTasksTerminated && mOverallStatus != Terminated )
{
mOverallStatus = Terminated;

emit statusChanged( Terminated );
emit taskTerminated();
mTaskFinished.wakeAll();
mNotFinishedMutex.unlock();
mTaskFinished.wakeAll();
}
else if ( mStatus == Terminated && !subTasksTerminated )
{
@@ -188,14 +188,14 @@ class CORE_EXPORT QgsTask : public QObject

/**
* Blocks the current thread until the task finishes or a maximum of \a timeout milliseconds.
* If the \a timeout is ``-1`` the thread will be blocked forever.
* If \a timeout is ``0`` the thread will be blocked forever.
* In case of a timeout, the task will still be running.
* In case the task already is finished, the method will return immediately while
* returning ``true``.
*
* The result will be false if the wait timed out and true in any other case.
*/
bool waitForFinished( int timeout = 30000 );
bool waitForFinished( unsigned long timeout = 30000 );

signals:

@@ -290,13 +290,17 @@ class CORE_EXPORT QgsTask : public QObject
//! Status of this task and all subtasks
TaskStatus mOverallStatus;

QMutex mNotFinishedMutex;

//! Progress of this (parent) task alone
double mProgress;
//! Overall progress of this task and all subtasks
double mTotalProgress;
bool mShouldTerminate;
int mStartCount;

QWaitCondition mTaskFinished;

struct SubTask
{
SubTask( QgsTask *task, QgsTaskList dependencies, SubTaskDependency dependency )

0 comments on commit 88fcbb0

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