Skip to content

Commit e5b156b

Browse files
committedJul 6, 2017
Make processing algorithms safe to run in threads
1 parent 0231e77 commit e5b156b

File tree

3 files changed

+54
-5
lines changed

3 files changed

+54
-5
lines changed
 

‎python/core/processing/qgsprocessingcontext.sip

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,6 @@ Destination project
132132
.. seealso:: layersToLoadOnCompletion()
133133
%End
134134

135-
136135
QgsFeatureRequest::InvalidGeometryCheck invalidGeometryCheck() const;
137136
%Docstring
138137
Returns the behavior used for checking invalid geometries in input layers.

‎src/core/processing/qgsprocessingalgorithm.cpp

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ QVariantMap QgsProcessingAlgorithm::run( const QVariantMap &parameters, QgsProce
341341

342342
bool QgsProcessingAlgorithm::prepare( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
343343
{
344-
Q_ASSERT_X( QApplication::instance()->thread() == context.temporaryLayerStore()->thread(), "QgsProcessingAlgorithm::prepare", "prepare() must be called from the same thread as context was created in" );
344+
Q_ASSERT_X( QThread::currentThread() == context.temporaryLayerStore()->thread(), "QgsProcessingAlgorithm::prepare", "prepare() must be called from the same thread as context was created in" );
345345
Q_ASSERT_X( !mHasPrepared, "QgsProcessingAlgorithm::prepare", "prepare() has already been called for the algorithm instance" );
346346
try
347347
{
@@ -361,25 +361,74 @@ bool QgsProcessingAlgorithm::runPrepared( QgsProcessingContext &context, QgsProc
361361
Q_ASSERT_X( mHasPrepared, "QgsProcessingAlgorithm::runPrepared", "prepare() was not called for the algorithm instance" );
362362
Q_ASSERT_X( !mHasExecuted, "QgsProcessingAlgorithm::runPrepared", "runPrepared() was already called for this algorithm instance" );
363363

364+
// Hey kids, let's all be thread safe! It's the fun thing to do!
365+
//
366+
// First, let's see if we're going to run into issues.
367+
QgsProcessingContext *runContext = nullptr;
368+
if ( context.thread() == QThread::currentThread() )
369+
{
370+
// OH. No issues. Seems you're running everything in the same thread, so go about your business. Sorry about
371+
// the intrusion, we're just making sure everything's nice and safe here. We like to keep a clean and tidy neighbourhood,
372+
// you know, for the kids and dogs and all.
373+
runContext = &context;
374+
}
375+
else
376+
{
377+
// HA! I knew things looked a bit suspicious - seems you're running this algorithm in a different thread
378+
// from that which the passed context has an affinity for. That's fine and all, but we need to make sure
379+
// we proceed safely...
380+
381+
// So first we create a temporary local context with affinity for the current thread
382+
mLocalContext.reset( new QgsProcessingContext() );
383+
// copy across everything we can safely do from the passed context
384+
mLocalContext->copyThreadSafeSettings( context );
385+
// and we'll run the actual algorithm processing using the local thread safe context
386+
runContext = mLocalContext.get();
387+
}
388+
364389
try
365390
{
366-
mHasExecuted = processAlgorithm( context, feedback );
391+
mHasExecuted = processAlgorithm( runContext, feedback );
392+
393+
if ( mLocalContext )
394+
{
395+
// ok, time to clean things up. We need to push the temporary context back into
396+
// the thread that the passed context is associated with (we can only push from the
397+
// current thread, so we HAVE to do this here)
398+
mLocalContext->pushToThread( context.thread() );
399+
}
367400
return mHasExecuted;
368401
}
369402
catch ( QgsProcessingException &e )
370403
{
371404
QgsMessageLog::logMessage( e.what(), QObject::tr( "Processing" ), QgsMessageLog::CRITICAL );
372405
feedback->reportError( e.what() );
406+
407+
if ( mLocalContext )
408+
{
409+
// see above!
410+
mLocalContext->pushToThread( context.thread() );
411+
}
373412
return false;
374413
}
375414
}
376415

377416
QVariantMap QgsProcessingAlgorithm::postProcess( QgsProcessingContext &context, QgsProcessingFeedback *feedback )
378417
{
379-
Q_ASSERT_X( QApplication::instance()->thread() == context.temporaryLayerStore()->thread(), "QgsProcessingAlgorithm::postProcess", "postProcess() must be called from the same thread the context was created in" );
418+
Q_ASSERT_X( QThread::currentThread() == context.temporaryLayerStore()->thread(), "QgsProcessingAlgorithm::postProcess", "postProcess() must be called from the same thread the context was created in" );
380419
Q_ASSERT_X( mHasExecuted, "QgsProcessingAlgorithm::postProcess", "runPrepared() was not called for the algorithm instance" );
381420
Q_ASSERT_X( !mHasPostProcessed, "QgsProcessingAlgorithm::postProcess", "postProcess() was already called for this algorithm instance" );
382421

422+
if ( mLocalContext )
423+
{
424+
// algorithm was processed using a temporary thread safe context. So now we need
425+
// to take the results from that temporary context, and smash them into the passed
426+
// context
427+
context.takeResultsFrom( *mLocalContext );
428+
// now get lost, we don't need you anymore
429+
mLocalContext.reset();
430+
}
431+
383432
mHasPostProcessed = true;
384433
try
385434
{

‎src/core/processing/qgsprocessingalgorithm.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@
2222
#include "qgis.h"
2323
#include "qgsprocessingparameters.h"
2424
#include "qgsprocessingoutputs.h"
25+
#include "qgsprocessingcontext.h"
2526
#include <QString>
2627
#include <QVariant>
2728
#include <QIcon>
2829

2930
class QgsProcessingProvider;
30-
class QgsProcessingContext;
3131
class QgsProcessingFeedback;
3232
class QgsFeatureSink;
3333

@@ -539,6 +539,7 @@ class CORE_EXPORT QgsProcessingAlgorithm
539539
bool mHasPrepared = false;
540540
bool mHasExecuted = false;
541541
bool mHasPostProcessed = false;
542+
std::unique_ptr< QgsProcessingContext > mLocalContext;
542543

543544
// friend class to access setProvider() - we do not want this public!
544545
friend class QgsProcessingProvider;

0 commit comments

Comments
 (0)
Please sign in to comment.