Make sure ellipsoid parameter and definition caches are cleared before app exit (related to #31762)

This should apply to both PROJ < 6 and >=6 builds.

Fixes the following error reported by Valgrind
```
==5848== Invalid read of size 4
==5848==    at 0xE5E4195: internal_pj_ctx_get_errno (in /home/even/proj/install-proj-master/lib/libproj.so.15.2.0)
==5848==    by 0xE5E16AE: internal_pj_free (in /home/even/proj/install-proj-master/lib/libproj.so.15.2.0)
==5848==    by 0xE5F4E78: internal_proj_destroy (in /home/even/proj/install-proj-master/lib/libproj.so.15.2.0)
==5848==    by 0x7F961A7: QgsProjUtils::ProjPJDeleter::operator()(PJconsts*) (qgsprojutils.cpp:76)
==5848==    by 0x7D2D44C: std::unique_ptr<PJconsts, QgsProjUtils::ProjPJDeleter>::~unique_ptr() (unique_ptr.h:239)
==5848==    by 0x7D2C9BD: QgsCoordinateReferenceSystemPrivate::~QgsCoordinateReferenceSystemPrivate() (qgscoordinatereferencesystem_p.h:94)
==5848==    by 0x7D2D6FA: QExplicitlySharedDataPointer<QgsCoordinateReferenceSystemPrivate>::~QExplicitlySharedDataPointer() (qshareddata.h:165)
==5848==    by 0x7CDFA77: QgsCoordinateReferenceSystem::~QgsCoordinateReferenceSystem() (qgscoordinatereferencesystem.cpp:233)
==5848==    by 0x55CC0AF: QgsEllipsoidUtils::EllipsoidParameters::~EllipsoidParameters() (qgsellipsoidutils.h:39)
==5848==    by 0x7D8C40B: QHashNode<QString, QgsEllipsoidUtils::EllipsoidParameters>::~QHashNode() (qhash.h:149)
==5848==    by 0x7D8C43F: QHash<QString, QgsEllipsoidUtils::EllipsoidParameters>::deleteNode2(QHashData::Node*) (qhash.h:536)
==5848==    by 0x9BE3B78: QHashData::free_helper(void (*)(QHashData::Node*)) (in /opt/qt59/lib/libQt5Core.so.5.9.1)
==5848==    by 0x7D8C466: QHash<QString, QgsEllipsoidUtils::EllipsoidParameters>::freeData(QHashData*) (qhash.h:576)
==5848==    by 0x7D8CCED: QHash<QString, QgsEllipsoidUtils::EllipsoidParameters>::~QHash() (qhash.h:254)
==5848==    by 0xA60E369: __cxa_finalize (cxa_finalize.c:56)
==5848==    by 0x77BE742: ??? (in /home/even/qgis/QGIS/build/output/lib/libqgis_core.so.3.9.0)
==5848==    by 0x4010DF6: _dl_fini (dl-fini.c:235)
==5848==    by 0xA60DFF7: __run_exit_handlers (exit.c:82)
==5848==    by 0xA60E044: exit (exit.c:104)
==5848==    by 0xA5F4836: (below main) (libc-start.c:325)
==5848==  Address 0x45db80e0 is 0 bytes inside a block of size 136 free'd
==5848==    at 0x4C2F440: operator delete(void*) (vg_replace_malloc.c:586)
==5848==    by 0xE5F5A3A: internal_proj_context_destroy (in /home/even/proj/install-proj-master/lib/libproj.so.15.2.0)
==5848==    by 0x7F9617A: QgsProjContext::~QgsProjContext() (qgsprojutils.cpp:48)
==5848==    by 0xA60E5FE: __call_tls_dtors (cxa_thread_atexit_impl.c:155)
==5848==    by 0xA60DF26: __run_exit_handlers (exit.c:40)
==5848==    by 0xA60E044: exit (exit.c:104)
==5848==    by 0xA5F4836: (below main) (libc-start.c:325)
==5848==  Block was alloc'd at
==5848==    at 0x4C2E709: operator new(unsigned long, std::nothrow_t const&) (vg_replace_malloc.c:387)
==5848==    by 0xE5E445E: internal_pj_ctx_alloc (in /home/even/proj/install-proj-master/lib/libproj.so.15.2.0)
==5848==    by 0x7F96151: QgsProjContext::QgsProjContext() (qgsprojutils.cpp:39)
==5848==    by 0x7F970A9: __tls_init (qgsprojutils.cpp:31)
==5848==    by 0x7F970F3: TLS wrapper function for QgsProjContext::sProjContext (in /home/even/qgis/QGIS/build/output/lib/libqgis_core.so.3.9.0)
==5848==    by 0x7F96186: QgsProjContext::get() (qgsprojutils.cpp:57)
==5848==    by 0x7D86B13: QgsEllipsoidUtils::definitions() (qgsellipsoidutils.cpp:351)
==5848==    by 0x7D863FE: QgsEllipsoidUtils::ellipsoidParameters(QString const&)::{lambda()#1}::operator()() const (qgsellipsoidutils.cpp:173)
==5848==    by 0x7D87EE1: void std::_Bind_simple<QgsEllipsoidUtils::ellipsoidParameters(QString const&)::{lambda()#1} ()>::_M_invoke<>(std::_Index_tuple<>) (functional:1531)
==5848==    by 0x7D87BC5: std::_Bind_simple<QgsEllipsoidUtils::ellipsoidParameters(QString const&)::{lambda()#1} ()>::operator()() (functional:1520)
==5848==    by 0x7D879A2: void std::__once_call_impl<std::_Bind_simple<QgsEllipsoidUtils::ellipsoidParameters(QString const&)::{lambda()#1} ()> >() (mutex:706)
==5848==    by 0x10A6AA98: __pthread_once_slow (pthread_once.c:116)
==5848==    by 0x7D863CE: __gthread_once(int*, void (*)()) (gthr-default.h:699)
==5848==    by 0x7D8787F: void std::call_once<QgsEllipsoidUtils::ellipsoidParameters(QString const&)::{lambda()#1}>(std::once_flag&, QgsEllipsoidUtils::ellipsoidParameters(QString const&)::{lambda()#1}&&) (mutex:738)
==5848==    by 0x7D86476: QgsEllipsoidUtils::ellipsoidParameters(QString const&) (qgsellipsoidutils.cpp:174)
==5848==    by 0x7E8D572: QgsMapSettings::setEllipsoid(QString const&) (qgsmapsettings.cpp:322)
==5848==    by 0x6A4E5F1: QgsMapCanvas::QgsMapCanvas(QWidget*) (qgsmapcanvas.cpp:143)
==5848==    by 0x5110841: QgisApp::QgisApp(QSplashScreen*, bool, bool, QString const&, QString const&, QWidget*, QFlags<Qt::WindowType>) (qgisapp.cpp:803)
==5848==    by 0x417DB9: main (main.cpp:1339)
==5848==
```
This commit is contained in:
Even Rouault 2019-09-14 15:03:37 +02:00
parent 738ced6488
commit fdcca14f65
No known key found for this signature in database
GPG Key ID: 33EBBFC47B3DD87D
5 changed files with 81 additions and 22 deletions

View File

@ -64,6 +64,7 @@ ellipsoid database.
.. seealso:: :py:func:`definitions`
%End
};

View File

@ -351,15 +351,20 @@ QgsApplication::~QgsApplication()
delete mQgisTranslator;
delete mQtTranslator;
// invalidate coordinate cache while the PROJ context held by the thread-locale
// QgsProjContextStore object is still alive. Otherwise if this later object
// is destroyed before the static variables of the cache, we might use freed memory.
// we do this here as well as in exitQgis() -- it's safe to call as often as we want,
// and there's just a *chance* that someone hasn't properly called exitQgis prior to
// this destructor...
invalidateCaches();
}
void QgsApplication::invalidateCaches()
{
// invalidate coordinate cache while the PROJ context held by the thread-locale
// QgsProjContextStore object is still alive. Otherwise if this later object
// is destroyed before the static variables of the cache, we might use freed memory.
QgsCoordinateTransform::invalidateCache( true );
QgsCoordinateReferenceSystem::invalidateCache( true );
QgsEllipsoidUtils::invalidateCache( true );
}
QgsApplication *QgsApplication::instance()
@ -1267,11 +1272,7 @@ void QgsApplication::exitQgis()
if ( QgsProviderRegistry::exists() )
delete QgsProviderRegistry::instance();
// invalidate coordinate cache AND DISABLE THEM! while the PROJ context held by the thread-locale
// QgsProjContextStore object is still alive. Otherwise if this later object
// is destroyed before the static variables of the cache, we might use freed memory.
QgsCoordinateTransform::invalidateCache( true );
QgsCoordinateReferenceSystem::invalidateCache( true );
invalidateCaches();
QgsStyle::cleanDefaultStyle();

View File

@ -923,6 +923,8 @@ class CORE_EXPORT QgsApplication : public QApplication
static QgsAuthManager *sAuthManager;
static ApplicationMembers *members();
static void invalidateCaches();
};
// clazy:excludeall=qstring-allocations

View File

@ -31,6 +31,7 @@ QReadWriteLock QgsEllipsoidUtils::sEllipsoidCacheLock;
QHash< QString, QgsEllipsoidUtils::EllipsoidParameters > QgsEllipsoidUtils::sEllipsoidCache;
QReadWriteLock QgsEllipsoidUtils::sDefinitionCacheLock;
QList< QgsEllipsoidUtils::EllipsoidDefinition > QgsEllipsoidUtils::sDefinitionCache;
static bool sDisableCache = false;
// maps older QGIS ellipsoid acronyms to proj acronyms/names
const QMap< QString, QString > sProj6EllipsoidAcronymMap
@ -179,12 +180,15 @@ QgsEllipsoidUtils::EllipsoidParameters QgsEllipsoidUtils::ellipsoidParameters( c
// check cache
{
QgsReadWriteLocker locker( sEllipsoidCacheLock, QgsReadWriteLocker::Read );
QHash< QString, EllipsoidParameters >::const_iterator cacheIt = sEllipsoidCache.constFind( ellipsoid );
if ( cacheIt != sEllipsoidCache.constEnd() )
if ( !sDisableCache )
{
// found a match in the cache
QgsEllipsoidUtils::EllipsoidParameters params = cacheIt.value();
return params;
QHash< QString, EllipsoidParameters >::const_iterator cacheIt = sEllipsoidCache.constFind( ellipsoid );
if ( cacheIt != sEllipsoidCache.constEnd() )
{
// found a match in the cache
QgsEllipsoidUtils::EllipsoidParameters params = cacheIt.value();
return params;
}
}
}
@ -213,7 +217,10 @@ QgsEllipsoidUtils::EllipsoidParameters QgsEllipsoidUtils::ellipsoidParameters( c
}
QgsReadWriteLocker locker( sEllipsoidCacheLock, QgsReadWriteLocker::Write );
sEllipsoidCache.insert( ellipsoid, params );
if ( !sDisableCache )
{
sEllipsoidCache.insert( ellipsoid, params );
}
return params;
}
@ -256,7 +263,10 @@ QgsEllipsoidUtils::EllipsoidParameters QgsEllipsoidUtils::ellipsoidParameters( c
QgsDebugMsg( QStringLiteral( "setEllipsoid: no row in tbl_ellipsoid for acronym '%1'" ).arg( ellipsoid ) );
params.valid = false;
sEllipsoidCacheLock.lockForWrite();
sEllipsoidCache.insert( ellipsoid, params );
if ( !sDisableCache )
{
sEllipsoidCache.insert( ellipsoid, params );
}
sEllipsoidCacheLock.unlock();
return params;
}
@ -269,7 +279,10 @@ QgsEllipsoidUtils::EllipsoidParameters QgsEllipsoidUtils::ellipsoidParameters( c
QgsDebugMsg( QStringLiteral( "setEllipsoid: wrong format of radius field: '%1'" ).arg( radius ) );
params.valid = false;
sEllipsoidCacheLock.lockForWrite();
sEllipsoidCache.insert( ellipsoid, params );
if ( !sDisableCache )
{
sEllipsoidCache.insert( ellipsoid, params );
}
sEllipsoidCacheLock.unlock();
return params;
}
@ -292,7 +305,10 @@ QgsEllipsoidUtils::EllipsoidParameters QgsEllipsoidUtils::ellipsoidParameters( c
QgsDebugMsg( QStringLiteral( "setEllipsoid: wrong format of parameter2 field: '%1'" ).arg( parameter2 ) );
params.valid = false;
sEllipsoidCacheLock.lockForWrite();
sEllipsoidCache.insert( ellipsoid, params );
if ( !sDisableCache )
{
sEllipsoidCache.insert( ellipsoid, params );
}
sEllipsoidCacheLock.unlock();
return params;
}
@ -320,14 +336,20 @@ QgsEllipsoidUtils::EllipsoidParameters QgsEllipsoidUtils::ellipsoidParameters( c
params.crs = destCRS;
sEllipsoidCacheLock.lockForWrite();
sEllipsoidCache.insert( ellipsoid, params );
if ( !sDisableCache )
{
sEllipsoidCache.insert( ellipsoid, params );
}
sEllipsoidCacheLock.unlock();
return params;
#else
params.valid = false;
QgsReadWriteLocker l( sEllipsoidCacheLock, QgsReadWriteLocker::Write );
sEllipsoidCache.insert( ellipsoid, params );
if ( !sDisableCache )
{
sEllipsoidCache.insert( ellipsoid, params );
}
return params;
#endif
@ -388,7 +410,10 @@ QList<QgsEllipsoidUtils::EllipsoidDefinition> QgsEllipsoidUtils::definitions()
}
defs << def;
sEllipsoidCache.insert( def.acronym, def.parameters );
if ( !sDisableCache )
{
sEllipsoidCache.insert( def.acronym, def.parameters );
}
}
codesIt++;
@ -444,7 +469,10 @@ QList<QgsEllipsoidUtils::EllipsoidDefinition> QgsEllipsoidUtils::definitions()
{
return collator.compare( a.description, b.description ) < 0;
} );
sDefinitionCache = defs;
if ( !sDisableCache )
{
sDefinitionCache = defs;
}
return defs;
}
@ -459,3 +487,17 @@ QStringList QgsEllipsoidUtils::acronyms()
}
return result;
}
void QgsEllipsoidUtils::invalidateCache( bool disableCache )
{
QgsReadWriteLocker locker1( sEllipsoidCacheLock, QgsReadWriteLocker::Write );
QgsReadWriteLocker locker2( sDefinitionCacheLock, QgsReadWriteLocker::Write );
if ( !sDisableCache )
{
if ( disableCache )
sDisableCache = true;
sEllipsoidCache.clear();
sDefinitionCache.clear();
}
}

View File

@ -90,6 +90,19 @@ class CORE_EXPORT QgsEllipsoidUtils
*/
static QStringList acronyms();
#ifndef SIP_RUN
/**
* Clears the internal cache used.
*
* If \a disableCache is TRUE then the inbuilt cache will be completely disabled. This
* argument is for internal use only.
*
* \since QGIS 3.10
*/
static void invalidateCache( bool disableCache = false );
#endif
private:
// ellipsoid cache