Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid using thread unsafe proj API - approach 2 #4561

Merged
merged 4 commits into from
May 16, 2017

Conversation

nyalldawson
Copy link
Collaborator

(Alternative approach to #4553)

While working on #4515 I noticed some odd behavior were QgsCsExceptions were being unpredictably thrown while rendering multiple map layers in parallel. I believe this is caused by QGIS not using the thread safe proj API.

This PR avoids unpredictable behavior when transforms are being conducted in background threads, such as map renders, and is likely a fix for https://issues.qgis.org/issues/11441

This commit:

  1. Uses thread_local storage for projCtx objects, to ensure that every thread correctly has its own projCtx context.

  2. Refactors QgsCoordinateTransformPrivate so that the projPJ source and destination objects are instead stored in a map (by projCtx). This allows transforms to be transparently performed using the correct projPJ objects for the particular thread in which the transform is being conducted. This approach avoids expensive detachment of QgsCoordinateTransformPrivate, and allows a single QgsCoordinateTransformPrivate to be safely utilised by QgsCoordinateTransform objects in different threads.

The advantage of this approach over #4553 is that the thread safety is transparent to API users - there's no need to prepare a QgsCoordinateTransform in advance before using in a background thread. The disadvantage is that the code is more complex, and there's also a small performance hit due to the use of the context->projPj map (and the mutex required to accompany this). Still, of the two I'd prefer this approach...

@wonder-sk
Copy link
Member

Very nice - I really like where this is going!

I would merge initializeCurrentContext(), sourceProjection(), destProjection() into one method (e.g. threadLocalProjData()) that would return QPair<projPJ, projPJ>. This should:

  • avoid code duplication (sourceProjection() vs destProjection())
  • make code faster (fewer locks + map lookups)
  • make the code easier to read (everything in once place and no recursive call).

The transformCoords() would call that method once when it starts and keep the projPJ instances locally instead of doing locking + map lookup several times.

(micro optimization alert!) The use of QMap::contains + QMap::value can be replaced by QMap::constFind to avoid one extra lookup.

@nyalldawson
Copy link
Collaborator Author

(micro optimization alert!)

That's the BEST kind! ;)

Avoids unpredictable behavior when transforms are being
conducted in background threads, such as map renders.

Refs qgis#11441

This commit:
1. Uses thread_local storage for projCtx objects, to ensure
that every thread correctly has its own projCtx context.

2. Refactors QgsCoordinateTransformPrivate so that the
projPJ source and destination objects are instead stored
in a map (by projCtx). This allows transforms to be
transparently performed using the correct projPJ objects
for the particular thread in which the transform is being
conducted. This approach avoids expensive detachment
of QgsCoordinateTransformPrivate, and allows a single
QgsCoordinateTransformPrivate to be safely utilised
by QgsCoordinateTransform objects in different threads.
@nyalldawson
Copy link
Collaborator Author

@wonder-sk good optimisations, done.

Copy link
Member

@wonder-sk wonder-sk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah! 🥇

@nyalldawson nyalldawson merged commit 7e345a7 into qgis:master May 16, 2017
@nyalldawson nyalldawson deleted the proj_thread2 branch May 16, 2017 05:12
static thread_local QgsProjContextStore mProjContext;

QReadWriteLock mProjLock;
QMap < uintptr_t, QPair< projPJ, projPJ > > mProjProjections;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this member would deserve a tiny bit of commenting.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope. Self explanatory.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

;)

@m-kuhn
Copy link
Member

m-kuhn commented May 16, 2017

Good job, both of you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants