Skip to content

Commit 49de489

Browse files
committedOct 24, 2017
[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.
1 parent ceb31fa commit 49de489

10 files changed

+129
-12
lines changed
 

‎python/core/qgsfeatureiterator.sip

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,21 @@ end of iterating: free the resources / lock
6363
:rtype: CompileStatus
6464
%End
6565

66+
virtual bool isValid() const;
67+
%Docstring
68+
Returns if this iterator is valid.
69+
An invalid feature iterator is not able to provide a reliable source for data.
70+
If an iterator is invalid, either give up or try to send the request again (preferably
71+
after a timeout to give the system some time to stay responsive).
72+
73+
If you want to check if the iterator successfully completed, better use QgsFeatureIterator.isClosed().
74+
75+
.. note::
76+
77+
Added in QGIS 3.0
78+
:rtype: bool
79+
%End
80+
6681
protected:
6782

6883
virtual bool fetchFeature( QgsFeature &f ) = 0;
@@ -145,6 +160,7 @@ Setup the simplification of geometries to fetch using the specified simplify met
145160
:rtype: bool
146161
%End
147162

163+
148164
};
149165

150166

@@ -233,6 +249,20 @@ destructor deletes the iterator if it has no more references
233249
:rtype: bool
234250
%End
235251

252+
virtual bool isValid() const;
253+
%Docstring
254+
Will return if this iterator is valid.
255+
An invalid iterator was probably introduced by a failed attempt to acquire a connection
256+
or is a default constructed iterator.
257+
258+
.. seealso:: isClosed to check if the iterator successfully completed and returned all the features.
259+
260+
.. note::
261+
262+
Added in QGIS 3.0
263+
:rtype: bool
264+
%End
265+
236266
bool isClosed() const;
237267
%Docstring
238268
find out whether the iterator is still valid or closed already

‎python/core/qgsfeaturerequest.sip

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,19 @@ Set a subset of attributes by names that will be fetched
625625
:rtype: bool
626626
%End
627627

628+
int connectionTimeout() const;
629+
%Docstring
630+
The timeout for how long we should wait for a connection if none is available from the pool
631+
at this moment. A negative value (which is set by default) will wait forever.
632+
:rtype: int
633+
%End
634+
635+
void setConnectionTimeout( int connectionTimeout );
636+
%Docstring
637+
The timeout for how long we should wait for a connection if none is available from the pool
638+
at this moment. A negative value (which is set by default) will wait forever.
639+
%End
640+
628641
protected:
629642
};
630643

‎python/core/qgsvectorlayerfeatureiterator.sip

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ end of iterating: free the resources / lock
107107
};
108108

109109

110+
virtual bool isValid() const;
111+
110112
protected:
111113
virtual bool fetchFeature( QgsFeature &feature );
112114
%Docstring

‎src/core/qgsconnectionpool.h

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,11 @@ class QgsConnectionPoolGroup
8585
//! QgsConnectionPoolGroup cannot be copied
8686
QgsConnectionPoolGroup &operator=( const QgsConnectionPoolGroup &other ) = delete;
8787

88-
T acquire()
88+
T acquire( int timeout )
8989
{
9090
// we are going to acquire a resource - if no resource is available, we will block here
91-
sem.acquire();
91+
if ( !sem.tryAcquire( 1, timeout ) )
92+
return nullptr;
9293

9394
// quick (preferred) way - use cached connection
9495
{
@@ -259,10 +260,13 @@ class QgsConnectionPool
259260
}
260261

261262
/**
262-
* Try to acquire a connection: if no connections are available, the thread will get blocked.
263-
* \returns initialized connection or null on error
263+
* Try to acquire a connection for a maximum of \a timeout milliseconds.
264+
* If \a timeout is a negative value the calling thread will be blocked
265+
* until a connection becomes available. This is the default behavior.
266+
*
267+
* \returns initialized connection or nullptr if unsuccessful
264268
*/
265-
T acquireConnection( const QString &connInfo )
269+
T acquireConnection( const QString &connInfo, int timeout = -1 )
266270
{
267271
mMutex.lock();
268272
typename T_Groups::iterator it = mGroups.find( connInfo );
@@ -273,7 +277,7 @@ class QgsConnectionPool
273277
T_Group *group = *it;
274278
mMutex.unlock();
275279

276-
return group->acquire();
280+
return group->acquire( timeout );
277281
}
278282

279283
//! Release an existing connection so it will get back into the pool and can be reused

‎src/core/qgsfeatureiterator.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,3 +235,8 @@ QgsFeatureIterator &QgsFeatureIterator::operator=( const QgsFeatureIterator &oth
235235
}
236236
return *this;
237237
}
238+
239+
bool QgsFeatureIterator::isValid() const
240+
{
241+
return mIter && mIter->isValid();
242+
}

‎src/core/qgsfeatureiterator.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,21 @@ class CORE_EXPORT QgsAbstractFeatureIterator
8282
*/
8383
CompileStatus compileStatus() const { return mCompileStatus; }
8484

85+
/**
86+
* Returns if this iterator is valid.
87+
* An invalid feature iterator is not able to provide a reliable source for data.
88+
* If an iterator is invalid, either give up or try to send the request again (preferably
89+
* after a timeout to give the system some time to stay responsive).
90+
*
91+
* If you want to check if the iterator successfully completed, better use QgsFeatureIterator::isClosed().
92+
*
93+
* @note Added in QGIS 3.0
94+
*/
95+
virtual bool isValid() const
96+
{
97+
return mValid;
98+
}
99+
85100
protected:
86101

87102
/**
@@ -175,6 +190,16 @@ class CORE_EXPORT QgsAbstractFeatureIterator
175190
//! Setup the simplification of geometries to fetch using the specified simplify method
176191
virtual bool prepareSimplification( const QgsSimplifyMethod &simplifyMethod );
177192

193+
/**
194+
* An invalid state of a feature iterator indicates that there was a problem with
195+
* even getting it up and running.
196+
* This should be set to false by subclasses if they have problems connecting to
197+
* the provider.
198+
* Do NOT set this to false when the feature iterator closes or has no features but
199+
* we are sure, that it's just an empty dataset.
200+
*/
201+
bool mValid = true;
202+
178203
private:
179204
bool mUseCachedFeatures;
180205
QList<QgsIndexedFeature> mCachedFeatures;
@@ -279,6 +304,17 @@ class CORE_EXPORT QgsFeatureIterator
279304
bool rewind();
280305
bool close();
281306

307+
/**
308+
* Will return if this iterator is valid.
309+
* An invalid iterator was probably introduced by a failed attempt to acquire a connection
310+
* or is a default constructed iterator.
311+
*
312+
* \see isClosed to check if the iterator successfully completed and returned all the features.
313+
*
314+
* @note Added in QGIS 3.0
315+
*/
316+
virtual bool isValid() const;
317+
282318
//! find out whether the iterator is still valid or closed already
283319
bool isClosed() const;
284320

‎src/core/qgsfeaturerequest.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ QgsFeatureRequest &QgsFeatureRequest::operator=( const QgsFeatureRequest &rh )
8585
mOrderBy = rh.mOrderBy;
8686
mCrs = rh.mCrs;
8787
mTransformErrorCallback = rh.mTransformErrorCallback;
88+
mConnectionTimeout = rh.mConnectionTimeout;
8889
return *this;
8990
}
9091

@@ -281,6 +282,16 @@ bool QgsFeatureRequest::acceptFeature( const QgsFeature &feature )
281282
return true;
282283
}
283284

285+
int QgsFeatureRequest::connectionTimeout() const
286+
{
287+
return mConnectionTimeout;
288+
}
289+
290+
void QgsFeatureRequest::setConnectionTimeout( int connectionTimeout )
291+
{
292+
mConnectionTimeout = connectionTimeout;
293+
}
294+
284295

285296
#include "qgsfeatureiterator.h"
286297
#include "qgslogger.h"

‎src/core/qgsfeaturerequest.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,18 @@ class CORE_EXPORT QgsFeatureRequest
601601
*/
602602
bool acceptFeature( const QgsFeature &feature );
603603

604+
/**
605+
* The timeout for how long we should wait for a connection if none is available from the pool
606+
* at this moment. A negative value (which is set by default) will wait forever.
607+
*/
608+
int connectionTimeout() const;
609+
610+
/**
611+
* The timeout for how long we should wait for a connection if none is available from the pool
612+
* at this moment. A negative value (which is set by default) will wait forever.
613+
*/
614+
void setConnectionTimeout( int connectionTimeout );
615+
604616
protected:
605617
FilterType mFilter = FilterNone;
606618
QgsRectangle mFilterRect;
@@ -617,6 +629,7 @@ class CORE_EXPORT QgsFeatureRequest
617629
std::function< void( const QgsFeature & ) > mInvalidGeometryCallback;
618630
std::function< void( const QgsFeature & ) > mTransformErrorCallback;
619631
QgsCoordinateReferenceSystem mCrs;
632+
int mConnectionTimeout = -1;
620633
};
621634

622635
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsFeatureRequest::Flags )

‎src/core/qgsvectorlayerfeatureiterator.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -211,12 +211,8 @@ QgsVectorLayerFeatureIterator::QgsVectorLayerFeatureIterator( QgsVectorLayerFeat
211211
if ( mSource->mHasEditBuffer )
212212
{
213213
mChangedFeaturesRequest = mProviderRequest;
214-
QgsFeatureIds changedIds;
215-
QgsChangedAttributesMap::const_iterator attIt = mSource->mChangedAttributeValues.constBegin();
216-
for ( ; attIt != mSource->mChangedAttributeValues.constEnd(); ++attIt )
217-
{
218-
changedIds << attIt.key();
219-
}
214+
const QgsFeatureIds changedIds = mSource->mChangedAttributeValues.keys().toSet();
215+
220216
mChangedFeaturesRequest.setFilterFids( changedIds );
221217

222218
if ( mChangedFeaturesRequest.limit() > 0 )
@@ -408,6 +404,11 @@ void QgsVectorLayerFeatureIterator::setInterruptionChecker( QgsInterruptionCheck
408404
mInterruptionChecker = interruptionChecker;
409405
}
410406

407+
bool QgsVectorLayerFeatureIterator::isValid() const
408+
{
409+
return mProviderIterator.isValid();
410+
}
411+
411412
bool QgsVectorLayerFeatureIterator::fetchNextAddedFeature( QgsFeature &f )
412413
{
413414
while ( mFetchAddedFeaturesIt-- != mSource->mAddedFeatures.constBegin() )

‎src/core/qgsvectorlayerfeatureiterator.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ class CORE_EXPORT QgsVectorLayerFeatureIterator : public QgsAbstractFeatureItera
139139
};
140140

141141

142+
virtual bool isValid() const override;
143+
142144
protected:
143145
//! fetch next feature, return true on success
144146
virtual bool fetchFeature( QgsFeature &feature ) override;

0 commit comments

Comments
 (0)
Please sign in to comment.