Skip to content

Commit

Permalink
Add provider suite test for thread safety of provider getFeatures()
Browse files Browse the repository at this point in the history
Hammers providers by iterating through features from multiple threads
  • Loading branch information
nyalldawson committed Jan 16, 2018
1 parent c2e0775 commit 741afdc
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 0 deletions.
1 change: 1 addition & 0 deletions python/core/core_auto.sip
Expand Up @@ -423,4 +423,5 @@
%Include layertree/qgslayertreeregistrybridge.sip
%Include qgsuserprofilemanager.sip
%Include symbology/qgsarrowsymbollayer.sip
%Include qgstestutils.sip
%Include qgsuserprofile.sip
36 changes: 36 additions & 0 deletions python/core/qgstestutils.sip
@@ -0,0 +1,36 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/core/qgstestutils.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/




%ModuleHeaderCode
#include "qgstestutils.h"
%End


namespace QgsTestUtils
{

bool testProviderIteratorThreadSafety( QgsVectorDataProvider *provider, const QgsFeatureRequest &request = QgsFeatureRequest() );
%Docstring
Runs a thready safety test on iterators from a vector data ``provider``, by concurrently
requesting features from the provider.

This method returns true... or it segfaults.
%End

};

/************************************************************************
* This file has been generated automatically from *
* *
* src/core/qgstestutils.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
2 changes: 2 additions & 0 deletions src/core/CMakeLists.txt
Expand Up @@ -319,6 +319,7 @@ SET(QGIS_CORE_SRCS
qgsxmlutils.cpp
qgssettings.cpp
qgsarchive.cpp
qgstestutils.cpp
qgsziputils.cpp

composer/qgsaddremoveitemcommand.cpp
Expand Down Expand Up @@ -995,6 +996,7 @@ SET(QGIS_CORE_HDRS
qgsmapthemecollection.h
qgsxmlutils.h
qgsarchive.h
qgstestutils.h
qgsziputils.h
qgsvector.h
qgslocalec.h
Expand Down
51 changes: 51 additions & 0 deletions src/core/qgstestutils.cpp
@@ -0,0 +1,51 @@
/***************************************************************************
qgstestutils.cpp
--------------------
begin : January 2018
copyright : (C) 2018 by Nyall Dawson
email : nyall.dawson@gmail.com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgstestutils.h"
#include "qgsvectordataprovider.h"
#include "qgsconnectionpool.h"
#include <QtConcurrentMap>
///@cond PRIVATE
///

static void getFeaturesForProvider( QPair< QgsVectorDataProvider *, QgsFeatureRequest > pair )
{
QgsFeatureIterator it = pair.first->getFeatures( pair.second );
QgsFeature f;
while ( it.nextFeature( f ) )
{

}
}

bool QgsTestUtils::testProviderIteratorThreadSafety( QgsVectorDataProvider *provider, const QgsFeatureRequest &request )
{
constexpr int JOBS_TO_RUN = 100;
QList< QPair< QgsVectorDataProvider *, QgsFeatureRequest > > jobs;
jobs.reserve( JOBS_TO_RUN );
for ( int i = 0; i < JOBS_TO_RUN; ++i )
{
jobs.append( qMakePair( provider, request ) );
}

//freaking hammmer the provider with a ton of concurrent requests.
//thread unsafe providers... you better be ready!!!!
QtConcurrent::blockingMap( jobs, getFeaturesForProvider );

return true;
}


///@endcond
48 changes: 48 additions & 0 deletions src/core/qgstestutils.h
@@ -0,0 +1,48 @@
/***************************************************************************
qgstestutils.h
---------------------
begin : January 2018
copyright : (C) 2018 by Nyall Dawson
email : nyall.dawson@gmail.com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#ifndef QGSTESTUTILS_H
#define QGSTESTUTILS_H

#include "qgis_core.h"
#include "qgis.h"
#include "qgsfeaturerequest.h"

class QgsVectorDataProvider;

#ifdef SIP_RUN
% ModuleHeaderCode
#include "qgstestutils.h"
% End
#endif

///@cond PRIVATE

// Used only for utilities required for the QGIS Python unit tests - not stable or public API
namespace QgsTestUtils
{

/**
* Runs a thready safety test on iterators from a vector data \a provider, by concurrently
* requesting features from the provider.
*
* This method returns true... or it segfaults.
*/
CORE_EXPORT bool testProviderIteratorThreadSafety( QgsVectorDataProvider *provider, const QgsFeatureRequest &request = QgsFeatureRequest() );

};

///@endcond
#endif //QGSTESTUTILS_H
10 changes: 10 additions & 0 deletions tests/src/python/providertestbase.py
Expand Up @@ -26,6 +26,7 @@
QgsVectorDataProvider,
QgsVectorLayerFeatureSource,
QgsFeatureSink,
QgsTestUtils,
NULL
)

Expand Down Expand Up @@ -192,6 +193,15 @@ def getSubsetStringNoMatching(self):
"""Individual providers may need to override this depending on their subset string formats"""
return '"name"=\'AppleBearOrangePear\''

def testGetFeaturesThreadSafety(self):
# no request
self.assertTrue(QgsTestUtils.testProviderIteratorThreadSafety(self.source))

# filter rect request
extent = QgsRectangle(-73, 70, -63, 80)
request = QgsFeatureRequest().setFilterRect(extent)
self.assertTrue(QgsTestUtils.testProviderIteratorThreadSafety(self.source, request))

def testOrderBy(self):
try:
self.disableCompiler()
Expand Down
5 changes: 5 additions & 0 deletions tests/src/python/test_provider_virtual.py
Expand Up @@ -81,6 +81,11 @@ def setUp(self):
print("****************************************************")
pass

def testGetFeaturesThreadSafety(self):
# provider does not work with this test - sqlite mutex prevents
# execution
pass

def tearDown(self):
"""Run after each test."""
pass
Expand Down
Binary file modified tests/testdata/provider/bug_17795.gpkg
Binary file not shown.

0 comments on commit 741afdc

Please sign in to comment.