[feature] Allow to specify timeout when requesting features

This is potentially interesting for race conditions, but whenever it is specified,
iterators will need to be carefully checked for validity and error handling needs
to be taken care of.
This commit is contained in:
Matthias Kuhn 2017-10-25 00:53:49 +02:00
parent ceb31fa45b
commit 49de489c09
10 changed files with 129 additions and 12 deletions

View File

@ -63,6 +63,21 @@ end of iterating: free the resources / lock
:rtype: CompileStatus :rtype: CompileStatus
%End %End
virtual bool isValid() const;
%Docstring
Returns if this iterator is valid.
An invalid feature iterator is not able to provide a reliable source for data.
If an iterator is invalid, either give up or try to send the request again (preferably
after a timeout to give the system some time to stay responsive).
If you want to check if the iterator successfully completed, better use QgsFeatureIterator.isClosed().
.. note::
Added in QGIS 3.0
:rtype: bool
%End
protected: protected:
virtual bool fetchFeature( QgsFeature &f ) = 0; virtual bool fetchFeature( QgsFeature &f ) = 0;
@ -145,6 +160,7 @@ Setup the simplification of geometries to fetch using the specified simplify met
:rtype: bool :rtype: bool
%End %End
}; };
@ -233,6 +249,20 @@ destructor deletes the iterator if it has no more references
:rtype: bool :rtype: bool
%End %End
virtual bool isValid() const;
%Docstring
Will return if this iterator is valid.
An invalid iterator was probably introduced by a failed attempt to acquire a connection
or is a default constructed iterator.
.. seealso:: isClosed to check if the iterator successfully completed and returned all the features.
.. note::
Added in QGIS 3.0
:rtype: bool
%End
bool isClosed() const; bool isClosed() const;
%Docstring %Docstring
find out whether the iterator is still valid or closed already find out whether the iterator is still valid or closed already

View File

@ -625,6 +625,19 @@ Set a subset of attributes by names that will be fetched
:rtype: bool :rtype: bool
%End %End
int connectionTimeout() const;
%Docstring
The timeout for how long we should wait for a connection if none is available from the pool
at this moment. A negative value (which is set by default) will wait forever.
:rtype: int
%End
void setConnectionTimeout( int connectionTimeout );
%Docstring
The timeout for how long we should wait for a connection if none is available from the pool
at this moment. A negative value (which is set by default) will wait forever.
%End
protected: protected:
}; };

View File

@ -107,6 +107,8 @@ end of iterating: free the resources / lock
}; };
virtual bool isValid() const;
protected: protected:
virtual bool fetchFeature( QgsFeature &feature ); virtual bool fetchFeature( QgsFeature &feature );
%Docstring %Docstring

View File

@ -85,10 +85,11 @@ class QgsConnectionPoolGroup
//! QgsConnectionPoolGroup cannot be copied //! QgsConnectionPoolGroup cannot be copied
QgsConnectionPoolGroup &operator=( const QgsConnectionPoolGroup &other ) = delete; QgsConnectionPoolGroup &operator=( const QgsConnectionPoolGroup &other ) = delete;
T acquire() T acquire( int timeout )
{ {
// we are going to acquire a resource - if no resource is available, we will block here // we are going to acquire a resource - if no resource is available, we will block here
sem.acquire(); if ( !sem.tryAcquire( 1, timeout ) )
return nullptr;
// quick (preferred) way - use cached connection // quick (preferred) way - use cached connection
{ {
@ -259,10 +260,13 @@ class QgsConnectionPool
} }
/** /**
* Try to acquire a connection: if no connections are available, the thread will get blocked. * Try to acquire a connection for a maximum of \a timeout milliseconds.
* \returns initialized connection or null on error * If \a timeout is a negative value the calling thread will be blocked
* until a connection becomes available. This is the default behavior.
*
* \returns initialized connection or nullptr if unsuccessful
*/ */
T acquireConnection( const QString &connInfo ) T acquireConnection( const QString &connInfo, int timeout = -1 )
{ {
mMutex.lock(); mMutex.lock();
typename T_Groups::iterator it = mGroups.find( connInfo ); typename T_Groups::iterator it = mGroups.find( connInfo );
@ -273,7 +277,7 @@ class QgsConnectionPool
T_Group *group = *it; T_Group *group = *it;
mMutex.unlock(); mMutex.unlock();
return group->acquire(); return group->acquire( timeout );
} }
//! Release an existing connection so it will get back into the pool and can be reused //! Release an existing connection so it will get back into the pool and can be reused

View File

@ -235,3 +235,8 @@ QgsFeatureIterator &QgsFeatureIterator::operator=( const QgsFeatureIterator &oth
} }
return *this; return *this;
} }
bool QgsFeatureIterator::isValid() const
{
return mIter && mIter->isValid();
}

View File

@ -82,6 +82,21 @@ class CORE_EXPORT QgsAbstractFeatureIterator
*/ */
CompileStatus compileStatus() const { return mCompileStatus; } CompileStatus compileStatus() const { return mCompileStatus; }
/**
* Returns if this iterator is valid.
* An invalid feature iterator is not able to provide a reliable source for data.
* If an iterator is invalid, either give up or try to send the request again (preferably
* after a timeout to give the system some time to stay responsive).
*
* If you want to check if the iterator successfully completed, better use QgsFeatureIterator::isClosed().
*
* @note Added in QGIS 3.0
*/
virtual bool isValid() const
{
return mValid;
}
protected: protected:
/** /**
@ -175,6 +190,16 @@ class CORE_EXPORT QgsAbstractFeatureIterator
//! Setup the simplification of geometries to fetch using the specified simplify method //! Setup the simplification of geometries to fetch using the specified simplify method
virtual bool prepareSimplification( const QgsSimplifyMethod &simplifyMethod ); virtual bool prepareSimplification( const QgsSimplifyMethod &simplifyMethod );
/**
* An invalid state of a feature iterator indicates that there was a problem with
* even getting it up and running.
* This should be set to false by subclasses if they have problems connecting to
* the provider.
* Do NOT set this to false when the feature iterator closes or has no features but
* we are sure, that it's just an empty dataset.
*/
bool mValid = true;
private: private:
bool mUseCachedFeatures; bool mUseCachedFeatures;
QList<QgsIndexedFeature> mCachedFeatures; QList<QgsIndexedFeature> mCachedFeatures;
@ -279,6 +304,17 @@ class CORE_EXPORT QgsFeatureIterator
bool rewind(); bool rewind();
bool close(); bool close();
/**
* Will return if this iterator is valid.
* An invalid iterator was probably introduced by a failed attempt to acquire a connection
* or is a default constructed iterator.
*
* \see isClosed to check if the iterator successfully completed and returned all the features.
*
* @note Added in QGIS 3.0
*/
virtual bool isValid() const;
//! find out whether the iterator is still valid or closed already //! find out whether the iterator is still valid or closed already
bool isClosed() const; bool isClosed() const;

View File

@ -85,6 +85,7 @@ QgsFeatureRequest &QgsFeatureRequest::operator=( const QgsFeatureRequest &rh )
mOrderBy = rh.mOrderBy; mOrderBy = rh.mOrderBy;
mCrs = rh.mCrs; mCrs = rh.mCrs;
mTransformErrorCallback = rh.mTransformErrorCallback; mTransformErrorCallback = rh.mTransformErrorCallback;
mConnectionTimeout = rh.mConnectionTimeout;
return *this; return *this;
} }
@ -281,6 +282,16 @@ bool QgsFeatureRequest::acceptFeature( const QgsFeature &feature )
return true; return true;
} }
int QgsFeatureRequest::connectionTimeout() const
{
return mConnectionTimeout;
}
void QgsFeatureRequest::setConnectionTimeout( int connectionTimeout )
{
mConnectionTimeout = connectionTimeout;
}
#include "qgsfeatureiterator.h" #include "qgsfeatureiterator.h"
#include "qgslogger.h" #include "qgslogger.h"

View File

@ -601,6 +601,18 @@ class CORE_EXPORT QgsFeatureRequest
*/ */
bool acceptFeature( const QgsFeature &feature ); bool acceptFeature( const QgsFeature &feature );
/**
* The timeout for how long we should wait for a connection if none is available from the pool
* at this moment. A negative value (which is set by default) will wait forever.
*/
int connectionTimeout() const;
/**
* The timeout for how long we should wait for a connection if none is available from the pool
* at this moment. A negative value (which is set by default) will wait forever.
*/
void setConnectionTimeout( int connectionTimeout );
protected: protected:
FilterType mFilter = FilterNone; FilterType mFilter = FilterNone;
QgsRectangle mFilterRect; QgsRectangle mFilterRect;
@ -617,6 +629,7 @@ class CORE_EXPORT QgsFeatureRequest
std::function< void( const QgsFeature & ) > mInvalidGeometryCallback; std::function< void( const QgsFeature & ) > mInvalidGeometryCallback;
std::function< void( const QgsFeature & ) > mTransformErrorCallback; std::function< void( const QgsFeature & ) > mTransformErrorCallback;
QgsCoordinateReferenceSystem mCrs; QgsCoordinateReferenceSystem mCrs;
int mConnectionTimeout = -1;
}; };
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsFeatureRequest::Flags ) Q_DECLARE_OPERATORS_FOR_FLAGS( QgsFeatureRequest::Flags )

View File

@ -211,12 +211,8 @@ QgsVectorLayerFeatureIterator::QgsVectorLayerFeatureIterator( QgsVectorLayerFeat
if ( mSource->mHasEditBuffer ) if ( mSource->mHasEditBuffer )
{ {
mChangedFeaturesRequest = mProviderRequest; mChangedFeaturesRequest = mProviderRequest;
QgsFeatureIds changedIds; const QgsFeatureIds changedIds = mSource->mChangedAttributeValues.keys().toSet();
QgsChangedAttributesMap::const_iterator attIt = mSource->mChangedAttributeValues.constBegin();
for ( ; attIt != mSource->mChangedAttributeValues.constEnd(); ++attIt )
{
changedIds << attIt.key();
}
mChangedFeaturesRequest.setFilterFids( changedIds ); mChangedFeaturesRequest.setFilterFids( changedIds );
if ( mChangedFeaturesRequest.limit() > 0 ) if ( mChangedFeaturesRequest.limit() > 0 )
@ -408,6 +404,11 @@ void QgsVectorLayerFeatureIterator::setInterruptionChecker( QgsInterruptionCheck
mInterruptionChecker = interruptionChecker; mInterruptionChecker = interruptionChecker;
} }
bool QgsVectorLayerFeatureIterator::isValid() const
{
return mProviderIterator.isValid();
}
bool QgsVectorLayerFeatureIterator::fetchNextAddedFeature( QgsFeature &f ) bool QgsVectorLayerFeatureIterator::fetchNextAddedFeature( QgsFeature &f )
{ {
while ( mFetchAddedFeaturesIt-- != mSource->mAddedFeatures.constBegin() ) while ( mFetchAddedFeaturesIt-- != mSource->mAddedFeatures.constBegin() )

View File

@ -139,6 +139,8 @@ class CORE_EXPORT QgsVectorLayerFeatureIterator : public QgsAbstractFeatureItera
}; };
virtual bool isValid() const override;
protected: protected:
//! fetch next feature, return true on success //! fetch next feature, return true on success
virtual bool fetchFeature( QgsFeature &feature ) override; virtual bool fetchFeature( QgsFeature &feature ) override;