Bug report #17351

Link failure on OS without thread local storage

Added by landry Landry Breuil over 2 years ago. Updated over 2 years ago.

Status:Closed
Priority:Normal
Assignee:Nyall Dawson
Category:Projection Support
Affected QGIS version:master Regression?:Yes
Operating System:OpenBSD Easy fix?:No
Pull Request or Patch supplied:No Resolution:
Crashes QGIS or corrupts data:No Copied to github as #:25248

Description

OpenBSD doesnt have support (yet) for thread local storage, so since https://github.com/qgis/QGIS/pull/4561 the linking will fail.

Is there an alternative to use thread_local here ?

Associated revisions

Revision 265be41d
Added by Nyall Dawson over 2 years ago

Don't use thread_local on mingw or OpenBSD builds

MingW has broken support for thread_local, so force disabling it
see
https://sourceforge.net/p/mingw-w64/bugs/445/
https://sourceforge.net/p/mingw-w64/bugs/527/
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80816

also OpenBSD has no thread_local support, see https://issues.qgis.org/issues/17351

So on these platforms we fall back to using QThreadStorage.

Fixes #17351

History

#1 Updated by Nyall Dawson over 2 years ago

You may have luck by ifdefing in QThreadStorage for bsd. This is what's used in the 2.18 backport - see af3370d03e7e7502e8a738d99cd99e38df23768d

#2 Updated by landry Landry Breuil over 2 years ago

I have a bit of a hard time diffing the commits in 2.18 and master for that feature - you mean just replacing thread_local by QThreadStorage ? ie thread_local QgsProjContextStore replaced by QThreadStorage< QgsProjContextStore* > ?

#3 Updated by Nyall Dawson over 2 years ago

I can't recall if that was the only change required, but I'd start with that and see if it compiles OK and the transform related unit tests pass.

#4 Updated by landry Landry Breuil over 2 years ago

I probably need a bit more:

-    static thread_local QgsProjContextStore mProjContext;
+    static QThreadStorage< QgsProjContextStore* > mProjContext;

Yields:

src/core/qgscoordinatetransform_p.cpp:188:139: error: no member named 'get' in 'QThreadStorage<QgsProjContextStore *>'
  QMap < uintptr_t, QPair< projPJ, projPJ > >::const_iterator it = mProjProjections.constFind( reinterpret_cast< uintptr_t>( mProjContext.get() ) );

I'll try to look a bit more at the 2.18 code.

Edit: hah, seems QThreadStorage needs to wrap the calls into .localData() to access the stored pointer. Right ?

#5 Updated by landry Landry Breuil over 2 years ago

Alas, with the following blind patch

Index: src/core/qgscoordinatetransform_p.h
--- src/core/qgscoordinatetransform_p.h.orig
+++ src/core/qgscoordinatetransform_p.h
@@ -31,6 +31,7 @@
 //

 #include <QSharedData>
+#include <QThreadStorage>
 #include "qgscoordinatereferencesystem.h" 

 typedef void *projPJ;
@@ -100,7 +101,7 @@ class QgsCoordinateTransformPrivate : public QSharedDa
      * Thread local proj context storage. A new proj context will be created
      * for every thread.
      */
-    static thread_local QgsProjContextStore mProjContext;
+    static QThreadStorage< QgsProjContextStore* > mProjContext;

     QReadWriteLock mProjLock;
     QMap < uintptr_t, QPair< projPJ, projPJ > > mProjProjections;

Index: src/core/qgscoordinatetransform_p.cpp
--- src/core/qgscoordinatetransform_p.cpp.orig
+++ src/core/qgscoordinatetransform_p.cpp
@@ -29,7 +29,7 @@ extern "C" 

 /// @cond PRIVATE

-thread_local QgsProjContextStore QgsCoordinateTransformPrivate::mProjContext;
+QThreadStorage< QgsProjContextStore* > QgsCoordinateTransformPrivate::mProjContext;

 QgsProjContextStore::QgsProjContextStore()
 {
@@ -185,7 +185,7 @@ QPair<projPJ, projPJ> QgsCoordinateTransformPrivate::t
 {
   mProjLock.lockForRead();

-  QMap < uintptr_t, QPair< projPJ, projPJ > >::const_iterator it = mProjProjections.constFind( reinterpret_cast< uintptr_t>( mProjContext.get() ) );
+  QMap < uintptr_t, QPair< projPJ, projPJ > >::const_iterator it = mProjProjections.constFind( reinterpret_cast< uintptr_t>( mProjContext.localData()->get() ) );
   if ( it != mProjProjections.constEnd() )
   {
     QPair<projPJ, projPJ> res = it.value();
@@ -196,9 +196,9 @@ QPair<projPJ, projPJ> QgsCoordinateTransformPrivate::t
   // proj projections don't exist yet, so we need to create
   mProjLock.unlock();
   mProjLock.lockForWrite();
-  QPair<projPJ, projPJ> res = qMakePair( pj_init_plus_ctx( mProjContext.get(), mSourceProjString.toUtf8() ),
-                                         pj_init_plus_ctx( mProjContext.get(), mDestProjString.toUtf8() ) );
-  mProjProjections.insert( reinterpret_cast< uintptr_t>( mProjContext.get() ), res );
+  QPair<projPJ, projPJ> res = qMakePair( pj_init_plus_ctx( mProjContext.localData()->get(), mSourceProjString.toUtf8() ),
+                                         pj_init_plus_ctx( mProjContext.localData()->get(), mDestProjString.toUtf8() ) );
+  mProjProjections.insert( reinterpret_cast< uintptr_t>( mProjContext.localData()->get() ), res );
   mProjLock.unlock();
   return res;

at runtime it blows.
#0 QgsProjContextStore::get (this=0x0) at /usr/obj/ports/qgis-2.99pre0/QGIS-a843df8947b3597118ad162eb0c7263469e2f1b2/src/core/qgscoordinatetransform_p.h:52
#1 QgsCoordinateTransformPrivate::threadLocalProjData (this=0x1dc74ecacb80)
at /usr/obj/ports/qgis-2.99pre0/QGIS-a843df8947b3597118ad162eb0c7263469e2f1b2/src/core/qgscoordinatetransform_p.cpp:199
#2 0x00001dc735cce7cc in QgsCoordinateTransformPrivate::initialize (this=0x1dc74ecacb80)
at /usr/obj/ports/qgis-2.99pre0/QGIS-a843df8947b3597118ad162eb0c7263469e2f1b2/src/core/qgscoordinatetransform_p.cpp:131
#3 0x00001dc7288c9e53 in QgsMeasureDialog::updateSettings (this=0x1dc7d6613a00)
at /usr/obj/ports/qgis-2.99pre0/QGIS-a843df8947b3597118ad162eb0c7263469e2f1b2/src/app/qgsmeasuredialog.cpp:108
#4 0x00001dc7288d02fc in QgsMeasureTool::updateSettings (this=0x1dc7fe5fd700)
at /usr/obj/ports/qgis-2.99pre0/QGIS-a843df8947b3597118ad162eb0c7263469e2f1b2/src/app/qgsmeasuretool.cpp:163
#5 0x00001dc7246971e4 in QMetaObject::activate(QObject*, int, int, void**) () from /usr/local/lib/qt5/./libQt5Core.so.2.2

#6 Updated by Nyall Dawson over 2 years ago

Does 2.18.13 work ok? Maybe QThreadStorage doesn't work on bsd either...

#7 Updated by Nyall Dawson over 2 years ago

Actually you're missing bits:

QPair<projPJ, projPJ> QgsCoordinateTransform::threadLocalProjData() const {
mProjLock.lockForRead();
projCtx pContext = nullptr;
if ( mProjContext.hasLocalData() )
pContext = mProjContext.localData()->get();
else {
mProjContext.setLocalData( new QgsProjContextStore() );
pContext = mProjContext.localData()->get();
}

QMap < uintptr_t, QPair< projPJ, projPJ > >::const_iterator it = mProjProjections.constFind( reinterpret_cast< uintptr_t>( pContext ) );
...
QPair&lt;projPJ, projPJ&gt; res = qMakePair( pj_init_plus_ctx( pContext, mSourceProjString.toUtf8() ),
pj_init_plus_ctx( pContext, mDestProjString.toUtf8() ) );
mProjProjections.insert( reinterpret_cast< uintptr_t>( pContext ), res );

(untested - may be some other changes required)

#9 Updated by landry Landry Breuil over 2 years ago

Nyall Dawson wrote:

Does 2.18.13 work ok? Maybe QThreadStorage doesn't work on bsd either...

2.18.13 & 14 work fine on OpenBSD.

#11 Updated by Nyall Dawson over 2 years ago

  • % Done changed from 0 to 100
  • Status changed from Open to Closed

Also available in: Atom PDF