Skip to content
Permalink
Browse files
[processing] Default to allowing background execution of algorithms
Since the underlying issues with the Python bindings are now fixed,
in most cases we can safely default to allowing an algorithm to
run in a background thread!!

So now we make this the default, and require individual algorithms
which are NOT thread safe to declare this. This includes algorithms
which directly manipulate the current project or layers (such as
setting layer styles), alter the selections in layers, or which
rely on 3rd party libraries (for now, SAGA and GRASS algorithms
are marked as not thread safe... TODO - someone more familiar with
these libraries can investigate and remove the flag if appropriate).

Also models are marked as non-thread safe. TODO: only flag an
individual model as thread-unsafe if any of its child algorithms
report this flag.
  • Loading branch information
nyalldawson committed Jan 29, 2018
1 parent 070e137 commit a05d941e4e50bc912756833e2c6091fb1196ad94
Show file tree
Hide file tree
Showing 117 changed files with 89 additions and 288 deletions.
@@ -47,6 +47,8 @@ Constructor for QgsProcessingModelAlgorithm.

virtual QString helpUrl() const;

virtual Flags flags() const;


virtual bool canExecute( QString *errorMessage /Out/ = 0 ) const;

@@ -44,7 +44,7 @@ Abstract base class for processing algorithms.
FlagSupportsBatch,
FlagCanCancel,
FlagRequiresMatchingCrs,
FlagCanRunInBackground,
FlagNoThreading,
FlagDeprecated,
};
typedef QFlags<QgsProcessingAlgorithm::Flag> Flags;
@@ -151,6 +151,10 @@ def icon(self):
def svgIconPath(self):
return QgsApplication.iconPath("providerGrass.svg")

def flags(self):
# TODO - maybe it's safe to background thread this?
return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

def tr(self, string, context=''):
if context == '':
context = self.__class__.__name__
@@ -27,6 +27,7 @@

from qgis.core import (QgsVectorDataProvider,
QgsFields,
QgsProcessingAlgorithm,
QgsProcessingParameterVectorLayer,
QgsProcessingParameterField,
QgsProcessingOutputVectorLayer)
@@ -56,6 +57,9 @@ def initAlgorithm(self, config=None):
self.tr('Attribute to index'), None, self.INPUT))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Indexed layer')))

def flags(self):
return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

def name(self):
return 'createattributeindex'

@@ -29,6 +29,7 @@
import re

from qgis.core import (QgsProcessing,
QgsProcessingAlgorithm,
QgsProcessingParameterVectorLayer,
QgsProcessingParameterCrs,
QgsProcessingOutputVectorLayer)
@@ -65,6 +66,9 @@ def name(self):
def displayName(self):
return self.tr('Define current projection')

def flags(self):
return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

def processAlgorithm(self, parameters, context, feedback):
layer = self.parameterAsVectorLayer(parameters, self.INPUT, context)
crs = self.parameterAsCrs(parameters, self.CRS, context)
@@ -33,6 +33,7 @@
QgsFeature,
QgsFeatureSink,
QgsGeometry,
QgsProcessingAlgorithm,
QgsProcessingException,
QgsProcessingUtils,
QgsProcessingParameterVectorLayer,
@@ -67,6 +68,9 @@ def groupId(self):
def __init__(self):
super().__init__()

def flags(self):
return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

def initAlgorithm(self, config=None):
self.modes = [self.tr('Largest Area'),
self.tr('Smallest Area'),
@@ -28,6 +28,7 @@
from qgis.core import (QgsVirtualLayerDefinition,
QgsVectorLayer,
QgsWkbTypes,
QgsProcessingAlgorithm,
QgsProcessingParameterMultipleLayers,
QgsProcessingParameterString,
QgsProcessingParameterEnum,
@@ -62,6 +63,9 @@ def groupId(self):
def __init__(self):
super().__init__()

def flags(self):
return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

def initAlgorithm(self, config=None):
self.addParameter(QgsProcessingParameterMultipleLayers(name=self.INPUT_DATASOURCES,
description=self.tr('Additional input datasources (called input1, .., inputN in the query)'),
@@ -27,6 +27,7 @@

from qgis.core import (QgsDataSourceUri,
QgsFeatureSink,
QgsProcessingAlgorithm,
QgsVectorLayerExporter,
QgsProcessingException,
QgsProcessingParameterFeatureSource,
@@ -76,6 +77,9 @@ def initAlgorithm(self, config=None):
self.addParameter(QgsProcessingParameterBoolean(self.DROP_STRING_LENGTH, self.tr('Drop length constraints on character fields'), False))
self.addParameter(QgsProcessingParameterBoolean(self.FORCE_SINGLEPART, self.tr('Create single-part geometries instead of multi-part'), False))

def flags(self):
return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

def name(self):
return 'importintospatialite'

@@ -32,6 +32,7 @@
from qgis.core import (QgsFeatureSink,
QgsProcessingException,
QgsProcessingUtils,
QgsProcessingAlgorithm,
QgsProcessingParameterVectorLayer,
QgsProcessingParameterEnum,
QgsProcessingParameterNumber,
@@ -59,6 +60,9 @@ def group(self):
def groupId(self):
return 'vectorselection'

def flags(self):
return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

def __init__(self):
super().__init__()

@@ -33,6 +33,7 @@
from qgis.core import (QgsFeatureRequest,
QgsProcessingException,
QgsProcessingUtils,
QgsProcessingAlgorithm,
QgsProcessingParameterVectorLayer,
QgsProcessingParameterEnum,
QgsProcessingParameterField,
@@ -65,6 +66,9 @@ def groupId(self):
def __init__(self):
super().__init__()

def flags(self):
return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

def initAlgorithm(self, config=None):
self.methods = [self.tr('Number of selected features'),
self.tr('Percentage of selected features')]
@@ -28,6 +28,7 @@
from qgis.PyQt.QtCore import QVariant
from qgis.core import (QgsExpression,
QgsProcessingException,
QgsProcessingAlgorithm,
QgsProcessingParameterVectorLayer,
QgsProcessingParameterField,
QgsProcessingParameterEnum,
@@ -71,6 +72,9 @@ def groupId(self):
def __init__(self):
super().__init__()

def flags(self):
return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

def initAlgorithm(self, config=None):
self.i18n_operators = ['=',
'!=',
@@ -26,6 +26,7 @@

from qgis.core import (QgsExpression,
QgsVectorLayer,
QgsProcessingAlgorithm,
QgsProcessingException,
QgsProcessingParameterVectorLayer,
QgsProcessingParameterExpression,
@@ -50,6 +51,9 @@ def groupId(self):
def __init__(self):
super().__init__()

def flags(self):
return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

def initAlgorithm(self, config=None):
self.methods = [self.tr('creating new selection'),
self.tr('adding to current selection'),
@@ -29,7 +29,8 @@

from qgis.PyQt.QtXml import QDomDocument

from qgis.core import (QgsProcessingParameterRasterLayer,
from qgis.core import (QgsProcessingAlgorithm,
QgsProcessingParameterRasterLayer,
QgsProcessingParameterFile,
QgsProcessingOutputRasterLayer)
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
@@ -50,6 +51,9 @@ def groupId(self):
def __init__(self):
super().__init__()

def flags(self):
return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

def initAlgorithm(self, config=None):
self.addParameter(QgsProcessingParameterRasterLayer(self.INPUT,
self.tr('Raster layer')))
@@ -25,7 +25,8 @@

__revision__ = '$Format:%H$'

from qgis.core import (QgsProcessingParameterFile,
from qgis.core import (QgsProcessingAlgorithm,
QgsProcessingParameterFile,
QgsProcessingParameterVectorLayer,
QgsProcessingOutputVectorLayer)
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
@@ -46,6 +47,9 @@ def groupId(self):
def __init__(self):
super().__init__()

def flags(self):
return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

def initAlgorithm(self, config=None):
self.addParameter(QgsProcessingParameterVectorLayer(self.INPUT,
self.tr('Vector layer')))
@@ -28,6 +28,7 @@
import os

from qgis.core import (QgsVectorDataProvider,
QgsProcessingAlgorithm,
QgsProcessingParameterVectorLayer,
QgsProcessingOutputVectorLayer,
QgsProcessing)
@@ -57,6 +58,9 @@ def initAlgorithm(self, config=None):
[QgsProcessing.TypeVectorPolygon, QgsProcessing.TypeVectorPoint, QgsProcessing.TypeVectorLine]))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Indexed layer')))

def flags(self):
return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

def name(self):
return 'createspatialindex'

@@ -26,6 +26,7 @@
__revision__ = '$Format:%H$'

from qgis.core import (QgsDataSourceUri,
QgsProcessingAlgorithm,
QgsProcessingException,
QgsProcessingParameterVectorLayer,
QgsProcessingParameterString)
@@ -58,6 +59,9 @@ def name(self):
def displayName(self):
return self.tr('SpatiaLite execute SQL')

def flags(self):
return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

def processAlgorithm(self, parameters, context, feedback):
database = self.parameterAsVectorLayer(parameters, self.DATABASE, context)
databaseuri = database.dataProvider().dataSourceUri()
@@ -25,7 +25,8 @@

__revision__ = '$Format:%H$'

from qgis.core import (QgsProcessingParameterVectorLayer,
from qgis.core import (QgsProcessingAlgorithm,
QgsProcessingParameterVectorLayer,
QgsProcessingOutputVectorLayer,
QgsProcessingException)
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
@@ -53,6 +54,9 @@ def initAlgorithm(self, config=None):
self.tr('Input Layer')))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Truncated layer')))

def flags(self):
return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

def name(self):
return 'truncatetable'

@@ -33,6 +33,7 @@
QgsProcessingException,
QgsMessageLog,
QgsProcessing,
QgsProcessingAlgorithm,
QgsProcessingParameterRasterLayer,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterBoolean,
@@ -99,6 +100,10 @@ def groupId(self):
def shortHelpString(self):
return shortHelp.get(self.id(), None)

def flags(self):
# TODO - maybe it's safe to background thread this?
return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

def defineCharacteristicsFromFile(self):
with open(self.description_file) as lines:
line = lines.readline().strip('\n').strip()
@@ -251,7 +251,7 @@ def on_complete(ok, results):

self.finish(ok, results, context, feedback)

if self.algorithm().flags() & QgsProcessingAlgorithm.FlagCanRunInBackground:
if not (self.algorithm().flags() & QgsProcessingAlgorithm.FlagNoThreading):
# Make sure the Log tab is visible before executing the algorithm
self.showLog()

@@ -31,11 +31,6 @@ QString QgsTessellateAlgorithm::displayName() const
return QObject::tr( "Tessellate" );
}

QgsProcessingAlgorithm::Flags QgsTessellateAlgorithm::flags() const
{
return QgsProcessingFeatureBasedAlgorithm::flags() | QgsProcessingAlgorithm::FlagCanRunInBackground;
}

QStringList QgsTessellateAlgorithm::tags() const
{
return QObject::tr( "3d,triangle" ).split( ',' );
@@ -36,7 +36,6 @@ class QgsTessellateAlgorithm : public QgsProcessingFeatureBasedAlgorithm
QgsTessellateAlgorithm() = default;
QString name() const override;
QString displayName() const override;
Flags flags() const override;
QStringList tags() const override;
QString group() const override;
QString shortHelpString() const override;
@@ -19,11 +19,6 @@

///@cond PRIVATE

QgsProcessingAlgorithm::Flags QgsAddIncrementalFieldAlgorithm::flags() const
{
return QgsProcessingFeatureBasedAlgorithm::flags() | QgsProcessingAlgorithm::FlagCanRunInBackground;
}

QString QgsAddIncrementalFieldAlgorithm::name() const
{
return QStringLiteral( "addautoincrementalfield" );
@@ -34,7 +34,6 @@ class QgsAddIncrementalFieldAlgorithm : public QgsProcessingFeatureBasedAlgorith
public:

QgsAddIncrementalFieldAlgorithm() = default;
Flags flags() const override;
QString name() const override;
QString displayName() const override;
QStringList tags() const override;
@@ -19,11 +19,6 @@

///@cond PRIVATE

QgsProcessingAlgorithm::Flags QgsAssignProjectionAlgorithm::flags() const
{
return QgsProcessingFeatureBasedAlgorithm::flags() | QgsProcessingAlgorithm::FlagCanRunInBackground;
}

QString QgsAssignProjectionAlgorithm::name() const