-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
[RFC 101 implementation] Add GDALGetThreadSafeDataset() and GDAL_OF_THREAD_SAFE #10746
Conversation
24f82da
to
d263783
Compare
35ad269
to
ae5944a
Compare
4daeb38
to
c8459a1
Compare
bf2411b
to
849e9ce
Compare
CC @abellgithub if you want to review it. I've made an effort to heavily comment the new gcore/gdalthreadsafedataset.cpp file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm having trouble understanding the point of the LRU cache -- you have to mess with a map anyway. The only benefit I can see is that it will evict datasets after 64 (or was it 32?), but from what I've seen having that many datasets open, especially in one thread, probably indicates an issue and developers should probably be doing the destruction of datasets before that ever occurs.
I started a vector-based implementation instead of the LRU cache/map that doesn't require a lock except when something is deleted. I mostly just wanted to see how it would work. It's not done and I may just drop it. If I finish and it seems any better, I'll let you know.
* | ||
* The implementation of this method must be thread-safe. | ||
*/ | ||
bool MEMDataset::CanBeCloned(int nScopeFlags, bool bCanShareState) const |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems weird to return a boolean from one that you passed in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is an overloaded method. Documentation is in the base class
* | ||
* The implementation of this method must be thread-safe. | ||
*/ | ||
std::unique_ptr<GDALDataset> MEMDataset::Clone(int nScopeFlags, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are the arguments?
Why isn't this invoked by copy constructor?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is an overloaded method. Documentation is in the base class
@@ -1582,6 +1591,11 @@ class CPL_DLL GDALRasterBand : public GDALMajorObject | |||
m_poBandRef = bOwned ? nullptr : poBand; | |||
} | |||
|
|||
const GDALRasterBand *get() const |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is confusing. I see below the operator that makes it work, but it's not intuitive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not new code, just making it const-safe. Basically this class is a inspired from unique_ptr, but it does not own always have ownership of the stored band
@@ -1592,6 +1606,11 @@ class CPL_DLL GDALRasterBand : public GDALMajorObject | |||
return m_poBandOwned != nullptr; | |||
} | |||
|
|||
operator const GDALRasterBand *() const |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And this is also odd. I guess it's an inner class so protected, but maybe just a function name instead of the operator?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh wait, I guess it was this way before. Still odd.
@@ -3577,6 +3613,39 @@ GDALDatasetH CPL_STDCALL GDALOpenEx(const char *pszFilename, | |||
{ | |||
VALIDATE_POINTER1(pszFilename, "GDALOpen", nullptr); | |||
|
|||
// Do some sanity checks on incompatible flags with thread-safe mode. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
General comment: There is a lot of bit twiddling here that's easy to get wrong and perhaps another scheme that provides access to these options would improve readability.
@@ -172,8 +178,34 @@ struct OGRSpatialReference::Private | |||
const char *nullifyTargetKeyIfPossible(const char *pszTargetKey); | |||
|
|||
void refreshAxisMapping(); | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like a lot of complexity. Is there some reason not to just take a lock, rather then all the optional stuff? I'm assuming that this was introduced because this isn't thread-safe and you may not access from multiple threads. Is the right solution perhaps to lock when you access this structure rather than to push locking into this structure? Also, using a recursive mutex makes me scratch my head and wonder why it's necessary and if there is a reasonable way to allow a non-recursive mutex. If nothing else, a comment seems in order.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added this comment:
// This structures enables locking during calls to OGRSpatialReference
// public methods. Locking is only needed for instances of
// OGRSpatialReference that have been asked to be thread-safe at
// construction.
// The lock is not just for a single call to OGRSpatialReference::Private,
// but for the series of calls done by a OGRSpatialReference method.
// We need a recursive mutex, because some OGRSpatialReference methods
// may call other ones.
|
||
explicit OptionalLockGuard(Private *p) : m_private(*p) | ||
{ | ||
if (m_private.m_bIsThreadSafe) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will this state change during use? It doesn't seem correct that it would, so it seems like the lock guard should simply be initialized with that state and hold it.
Perhaps better would be to create either a regular lock or a null mutex depending on the thread safety. Then you could just lock_guard the mutex without all of this.
Can you create either a thread-safe object or a non-thread-safe one and avoid opening up a state change with SetThreadSafe?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changed to
/** Assignment method, with thread-safety.
*
* Same as an assignment operator, but asking also that the *this instance
* becomes thread-safe.
*
* @param oSource SRS to assign to *this
* @return *this
* @since 3.10
*/
OGRSpatialReference &
OGRSpatialReference::AssignAndSetThreadSafe(const OGRSpatialReference &oSource)
{
*this = oSource;
d->SetThreadSafe();
return *this;
}
* This file is at the core of the "RFC 101 - Raster dataset read-only thread-safety". | ||
* Please consult it for high level understanding. | ||
* | ||
* 3 classes are involved: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And yet you list 4 ;)
One twice...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hum, I do list 3 distinct classes in the bullet points: GDALThreadSafeDataset, GDALThreadSafeRasterBand and GDALThreadLocalDatasetCache
} | ||
} | ||
|
||
for (const auto &oEntry : aoDSToFree) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs a comment to explain why you're sticking things in a list to delete them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment is a few line before, at the start of the method:
// Collect TLS datasets in a vector, and free them after releasing
// g_nInDestructorCounter to limit contention
849e9ce
to
eab1540
Compare
@abellgithub Thanks for the review. I believe I've addressed one way or another your comments
yes, this is to handle such unlikely cases |
faa65a3
to
f2a6849
Compare
…k virtual; make GDALDataset::BlockBasedRasterIO virtual
…n thread-safe dataset
f2a6849
to
580c610
Compare
Implements PR #10676