Skip to content

Commit

Permalink
QgsTask::waitForFinished without event loop
Browse files Browse the repository at this point in the history
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 88fcbb0
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 21 deletions.
4 changes: 2 additions & 2 deletions python/core/qgstaskmanager.sip
Expand Up @@ -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``.
Expand Down
37 changes: 20 additions & 17 deletions src/core/qgstaskmanager.cpp
Expand Up @@ -35,7 +35,9 @@ QgsTask::QgsTask( const QString &name, const Flags &flags )
, mTotalProgress( 0.0 )
, mShouldTerminate( false )
, mStartCount( 0 )
{}
{
mNotFinishedMutex.lock();
}

QgsTask::~QgsTask()
{
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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 )
{
Expand All @@ -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 )
{
Expand Down
8 changes: 6 additions & 2 deletions src/core/qgstaskmanager.h
Expand Up @@ -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:

Expand Down Expand Up @@ -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 )
Expand Down

0 comments on commit 88fcbb0

Please sign in to comment.