Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL Core
4 : * Purpose: Base class for thread safe dataset
5 : * Author: Even Rouault <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #ifndef DOXYGEN_SKIP
14 :
15 : #include "cpl_mem_cache.h"
16 : #include "gdal_proxy.h"
17 : #include "gdal_rat.h"
18 : #include "gdal_priv.h"
19 :
20 : #include <map>
21 : #include <memory>
22 : #include <mutex>
23 : #include <thread>
24 : #include <vector>
25 :
26 : bool GDALThreadLocalDatasetCacheIsInDestruction();
27 :
28 : /** Design notes of this file.
29 : *
30 : * This file is at the core of the "RFC 101 - Raster dataset read-only thread-safety".
31 : * Please consult it for high level understanding.
32 : *
33 : * 3 classes are involved:
34 : * - GDALThreadSafeDataset whose instances are returned to the user, and can
35 : * use them in a thread-safe way.
36 : * - GDALThreadSafeRasterBand whose instances are created (and owned) by a
37 : * GDALThreadSafeDataset instance, and returned to the user, which can use
38 : * them in a thread-safe way.
39 : * - GDALThreadLocalDatasetCache which is an internal class, which holds the
40 : * thread-local datasets.
41 : */
42 :
43 : /************************************************************************/
44 : /* GDALThreadLocalDatasetCache */
45 : /************************************************************************/
46 :
47 : class GDALThreadSafeDataset;
48 :
49 : /** This class is instantiated once per thread that uses a
50 : * GDALThreadSafeDataset instance. It holds mostly a cache that maps a
51 : * GDALThreadSafeDataset* pointer to the corresponding per-thread dataset.
52 : */
53 : class GDALThreadLocalDatasetCache
54 : {
55 : private:
56 : /** Least-recently-used based cache that maps a GDALThreadSafeDataset*
57 : * instance to the corresponding per-thread dataset.
58 : * It should be noted as this a LRU cache, entries might get evicted when
59 : * its capacity is reached (64 datasets), which might be undesirable.
60 : * Hence it is doubled with m_oMapReferencedDS for datasets that are in
61 : * active used by a thread.
62 : *
63 : * This cache is created as a unique_ptr, and not a standard object, for
64 : * delicate reasons related to application termination, where we might
65 : * want to leak the memory associated to it, to avoid the dataset it
66 : * references from being closed, after GDAL has been "closed" (typically
67 : * GDALDestroyDriverManager() has been called), which would otherwise lead
68 : * to crashes.
69 : */
70 : std::unique_ptr<lru11::Cache<const GDALThreadSafeDataset *,
71 : std::shared_ptr<GDALDataset>>>
72 : m_poCache{};
73 :
74 : static thread_local bool tl_inDestruction;
75 :
76 : GDALThreadLocalDatasetCache(const GDALThreadLocalDatasetCache &) = delete;
77 : GDALThreadLocalDatasetCache &
78 : operator=(const GDALThreadLocalDatasetCache &) = delete;
79 :
80 : public:
81 : GDALThreadLocalDatasetCache();
82 : ~GDALThreadLocalDatasetCache();
83 :
84 : /** Thread-id of the thread that instantiated this object. Used only for
85 : * CPLDebug() purposes
86 : */
87 : GIntBig m_nThreadID = 0;
88 :
89 : /** Mutex that protects access to m_oCache. There is "competition" around
90 : * access to m_oCache since the destructor of a GDALThreadSafeDataset
91 : * instance needs to evict entries corresponding to itself from all
92 : * GDALThreadLocalDatasetCache instances.
93 : */
94 : std::mutex m_oMutex{};
95 :
96 : /** This is a reference to *(m_poCache.get()).
97 : */
98 : lru11::Cache<const GDALThreadSafeDataset *, std::shared_ptr<GDALDataset>>
99 : &m_oCache;
100 :
101 : /** Pair of shared_ptr<GDALDataset> with associated thread-local config
102 : * options that were valid in the calling thread at the time
103 : * GDALThreadLocalDatasetCache::RefUnderlyingDataset() was called, so they
104 : * can be restored at UnrefUnderlyingDataset() time.
105 : */
106 : struct SharedPtrDatasetThreadLocalConfigOptionsPair
107 : {
108 : std::shared_ptr<GDALDataset> poDS;
109 : CPLStringList aosTLConfigOptions;
110 :
111 748749 : SharedPtrDatasetThreadLocalConfigOptionsPair(
112 : const std::shared_ptr<GDALDataset> &poDSIn,
113 : CPLStringList &&aosTLConfigOptionsIn)
114 748749 : : poDS(poDSIn), aosTLConfigOptions(std::move(aosTLConfigOptionsIn))
115 : {
116 746614 : }
117 : };
118 :
119 : /** Maps a GDALThreadSafeDataset*
120 : * instance to the corresponding per-thread dataset. Insertion into this
121 : * map is done by GDALThreadLocalDatasetCache::RefUnderlyingDataset() and
122 : * removal by UnrefUnderlyingDataset(). In most all use cases, the size of
123 : * this map should be 0 or 1 (not clear if it could be more, that would
124 : * involve RefUnderlyingDataset() being called in nested ways by the same
125 : * thread, but it doesn't hurt from being robust to that potential situation)
126 : */
127 : std::map<const GDALThreadSafeDataset *,
128 : SharedPtrDatasetThreadLocalConfigOptionsPair>
129 : m_oMapReferencedDS{};
130 :
131 : /** Maps a GDALRasterBand* returned by GDALThreadSafeRasterBand::RefUnderlyingDataset()
132 : * to the (thread-local) dataset that owns it (that is a dataset returned
133 : * by RefUnderlyingDataset(). The size of his map should be 0 or 1 in
134 : * most cases.
135 : */
136 : std::map<GDALRasterBand *, GDALDataset *> m_oMapReferencedDSFromBand{};
137 :
138 219876 : static bool IsInDestruction()
139 : {
140 219876 : return tl_inDestruction;
141 : }
142 : };
143 :
144 : /************************************************************************/
145 : /* GDALThreadSafeDataset */
146 : /************************************************************************/
147 :
148 : /** Global variable used to determine if the singleton of GlobalCache
149 : * owned by GDALThreadSafeDataset is valid.
150 : * This is needed to avoid issues at process termination where the order
151 : * of destruction between static global instances and TLS instances can be
152 : * tricky.
153 : */
154 : static bool bGlobalCacheValid = false;
155 :
156 : /** Thread-safe GDALDataset class.
157 : *
158 : * That class delegates all calls to its members to per-thread GDALDataset
159 : * instances.
160 : */
161 : class GDALThreadSafeDataset final : public GDALProxyDataset
162 : {
163 : public:
164 : GDALThreadSafeDataset(std::unique_ptr<GDALDataset> poPrototypeDSUniquePtr,
165 : GDALDataset *poPrototypeDS);
166 : ~GDALThreadSafeDataset() override;
167 :
168 : static std::unique_ptr<GDALDataset>
169 : Create(std::unique_ptr<GDALDataset> poPrototypeDS, int nScopeFlags);
170 :
171 : static GDALDataset *Create(GDALDataset *poPrototypeDS, int nScopeFlags);
172 :
173 : /* All below public methods override GDALDataset methods, and instead of
174 : * forwarding to a thread-local dataset, they act on the prototype dataset,
175 : * because they return a non-trivial type, that could be invalidated
176 : * otherwise if the thread-local dataset is evicted from the LRU cache.
177 : */
178 800 : const OGRSpatialReference *GetSpatialRef() const override
179 : {
180 800 : std::lock_guard oGuard(m_oPrototypeDSMutex);
181 800 : if (m_oSRS.IsEmpty())
182 : {
183 401 : auto poSRS = m_poPrototypeDS->GetSpatialRef();
184 401 : if (poSRS)
185 : {
186 1 : m_oSRS.AssignAndSetThreadSafe(*poSRS);
187 : }
188 : }
189 1600 : return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
190 : }
191 :
192 800 : const OGRSpatialReference *GetGCPSpatialRef() const override
193 : {
194 800 : std::lock_guard oGuard(m_oPrototypeDSMutex);
195 800 : if (m_oGCPSRS.IsEmpty())
196 : {
197 401 : auto poSRS = m_poPrototypeDS->GetGCPSpatialRef();
198 401 : if (poSRS)
199 : {
200 1 : m_oGCPSRS.AssignAndSetThreadSafe(*poSRS);
201 : }
202 : }
203 1600 : return m_oGCPSRS.IsEmpty() ? nullptr : &m_oGCPSRS;
204 : }
205 :
206 400 : const GDAL_GCP *GetGCPs() override
207 : {
208 800 : std::lock_guard oGuard(m_oPrototypeDSMutex);
209 800 : return const_cast<GDALDataset *>(m_poPrototypeDS)->GetGCPs();
210 : }
211 :
212 3 : const char *GetMetadataItem(const char *pszName,
213 : const char *pszDomain = "") override
214 : {
215 6 : std::lock_guard oGuard(m_oPrototypeDSMutex);
216 3 : return const_cast<GDALDataset *>(m_poPrototypeDS)
217 6 : ->GetMetadataItem(pszName, pszDomain);
218 : }
219 :
220 2 : char **GetMetadata(const char *pszDomain = "") override
221 : {
222 4 : std::lock_guard oGuard(m_oPrototypeDSMutex);
223 2 : return const_cast<GDALDataset *>(m_poPrototypeDS)
224 4 : ->GetMetadata(pszDomain);
225 : }
226 :
227 : /* End of methods that forward on the prototype dataset */
228 :
229 1 : GDALAsyncReader *BeginAsyncReader(int, int, int, int, void *, int, int,
230 : GDALDataType, int, int *, int, int, int,
231 : char **) override
232 : {
233 1 : CPLError(CE_Failure, CPLE_AppDefined,
234 : "GDALThreadSafeDataset::BeginAsyncReader() not supported");
235 1 : return nullptr;
236 : }
237 :
238 : protected:
239 : GDALDataset *RefUnderlyingDataset() const override;
240 :
241 : void
242 : UnrefUnderlyingDataset(GDALDataset *poUnderlyingDataset) const override;
243 :
244 : int CloseDependentDatasets() override;
245 :
246 : private:
247 : friend class GDALThreadSafeRasterBand;
248 : friend class GDALThreadLocalDatasetCache;
249 :
250 : /** Mutex that protects accesses to m_poPrototypeDS */
251 : mutable std::mutex m_oPrototypeDSMutex{};
252 :
253 : /** "Prototype" dataset, that is the dataset that was passed to the
254 : * GDALThreadSafeDataset constructor. All calls on to it should be on
255 : * const methods, and should be protected by m_oPrototypeDSMutex (except
256 : * during GDALThreadSafeDataset instance construction)
257 : */
258 : const GDALDataset *m_poPrototypeDS = nullptr;
259 :
260 : /** Unique pointer for m_poPrototypeDS in the cases where GDALThreadSafeDataset
261 : * has been passed a unique pointer */
262 : std::unique_ptr<GDALDataset> m_poPrototypeDSUniquePtr{};
263 :
264 : /** Thread-local config options at the time where GDALThreadSafeDataset
265 : * has been constructed.
266 : */
267 : const CPLStringList m_aosThreadLocalConfigOptions{};
268 :
269 : /** Cached value returned by GetSpatialRef() */
270 : mutable OGRSpatialReference m_oSRS{};
271 :
272 : /** Cached value returned by GetGCPSpatialRef() */
273 : mutable OGRSpatialReference m_oGCPSRS{};
274 :
275 : /** Structure that references all GDALThreadLocalDatasetCache* instances.
276 : */
277 : struct GlobalCache
278 : {
279 : /** Mutex that protect access to oSetOfCache */
280 : std::mutex oMutex{};
281 :
282 : /** Set of GDALThreadLocalDatasetCache* instances. That is it has
283 : * one entry per thread that has used at least once a
284 : * GDALThreadLocalDatasetCache/GDALThreadSafeRasterBand
285 : */
286 : std::set<GDALThreadLocalDatasetCache *> oSetOfCache{};
287 :
288 2 : GlobalCache()
289 2 : {
290 2 : bGlobalCacheValid = true;
291 2 : }
292 :
293 2 : ~GlobalCache()
294 4 : {
295 2 : bGlobalCacheValid = false;
296 2 : }
297 : };
298 :
299 : /** Returns a singleton for a GlobalCache instance that references all
300 : * GDALThreadLocalDatasetCache* instances.
301 : */
302 220 : static GlobalCache &GetSetOfCache()
303 : {
304 220 : static GlobalCache cache;
305 220 : return cache;
306 : }
307 :
308 : /** Thread-local dataset cache. */
309 : static thread_local std::unique_ptr<GDALThreadLocalDatasetCache> tl_poCache;
310 :
311 : void UnrefUnderlyingDataset(GDALDataset *poUnderlyingDataset,
312 : GDALThreadLocalDatasetCache *poCache) const;
313 :
314 : GDALThreadSafeDataset(const GDALThreadSafeDataset &) = delete;
315 : GDALThreadSafeDataset &operator=(const GDALThreadSafeDataset &) = delete;
316 : };
317 :
318 : /************************************************************************/
319 : /* GDALThreadSafeRasterBand */
320 : /************************************************************************/
321 :
322 : /** Thread-safe GDALRasterBand class.
323 : *
324 : * That class delegates all calls to its members to per-thread GDALDataset
325 : * instances.
326 : */
327 : class GDALThreadSafeRasterBand final : public GDALProxyRasterBand
328 : {
329 : public:
330 : GDALThreadSafeRasterBand(GDALThreadSafeDataset *poTSDS,
331 : GDALDataset *poParentDS, int nBandIn,
332 : GDALRasterBand *poPrototypeBand,
333 : int nBaseBandOfMaskBand, int nOvrIdx);
334 :
335 : GDALRasterBand *GetMaskBand() override;
336 : int GetOverviewCount() override;
337 : GDALRasterBand *GetOverview(int idx) override;
338 : GDALRasterBand *GetRasterSampleOverview(GUIntBig nDesiredSamples) override;
339 :
340 : GDALRasterAttributeTable *GetDefaultRAT() override;
341 :
342 : /* All below public methods override GDALRasterBand methods, and instead of
343 : * forwarding to a thread-local dataset, they act on the prototype band,
344 : * because they return a non-trivial type, that could be invalidated
345 : * otherwise if the thread-local dataset is evicted from the LRU cache.
346 : */
347 2 : const char *GetMetadataItem(const char *pszName,
348 : const char *pszDomain = "") override
349 : {
350 4 : std::lock_guard oGuard(m_poTSDS->m_oPrototypeDSMutex);
351 2 : return const_cast<GDALRasterBand *>(m_poPrototypeBand)
352 4 : ->GetMetadataItem(pszName, pszDomain);
353 : }
354 :
355 2 : char **GetMetadata(const char *pszDomain = "") override
356 : {
357 4 : std::lock_guard oGuard(m_poTSDS->m_oPrototypeDSMutex);
358 2 : return const_cast<GDALRasterBand *>(m_poPrototypeBand)
359 4 : ->GetMetadata(pszDomain);
360 : }
361 :
362 1 : const char *GetUnitType() override
363 : {
364 2 : std::lock_guard oGuard(m_poTSDS->m_oPrototypeDSMutex);
365 2 : return const_cast<GDALRasterBand *>(m_poPrototypeBand)->GetUnitType();
366 : }
367 :
368 2 : GDALColorTable *GetColorTable() override
369 : {
370 4 : std::lock_guard oGuard(m_poTSDS->m_oPrototypeDSMutex);
371 4 : return const_cast<GDALRasterBand *>(m_poPrototypeBand)->GetColorTable();
372 : }
373 :
374 : /* End of methods that forward on the prototype band */
375 :
376 1 : CPLVirtualMem *GetVirtualMemAuto(GDALRWFlag, int *, GIntBig *,
377 : char **) override
378 : {
379 1 : CPLError(CE_Failure, CPLE_AppDefined,
380 : "GDALThreadSafeRasterBand::GetVirtualMemAuto() not supported");
381 1 : return nullptr;
382 : }
383 :
384 : protected:
385 : GDALRasterBand *RefUnderlyingRasterBand(bool bForceOpen) const override;
386 : void UnrefUnderlyingRasterBand(
387 : GDALRasterBand *poUnderlyingRasterBand) const override;
388 :
389 : private:
390 : /** Pointer to the thread-safe dataset from which this band has been
391 : *created */
392 : GDALThreadSafeDataset *m_poTSDS = nullptr;
393 :
394 : /** Pointer to the "prototype" raster band that corresponds to us.
395 : * All calls to m_poPrototypeBand should be protected by
396 : * GDALThreadSafeDataset:m_oPrototypeDSMutex.
397 : */
398 : const GDALRasterBand *m_poPrototypeBand = nullptr;
399 :
400 : /** 0 for standard bands, otherwise > 0 value that indicates that this
401 : * band is a mask band and m_nBaseBandOfMaskBand is then the number
402 : * of the band that is the parent of the mask band.
403 : */
404 : const int m_nBaseBandOfMaskBand;
405 :
406 : /** 0 for standard bands, otherwise >= 0 value that indicates that this
407 : * band is an overview band and m_nOvrIdx is then the index of the overview.
408 : */
409 : const int m_nOvrIdx;
410 :
411 : /** Mask band associated with this band. */
412 : std::unique_ptr<GDALRasterBand> m_poMaskBand{};
413 :
414 : /** List of overviews associated with this band. */
415 : std::vector<std::unique_ptr<GDALRasterBand>> m_apoOverviews{};
416 :
417 : GDALThreadSafeRasterBand(const GDALThreadSafeRasterBand &) = delete;
418 : GDALThreadSafeRasterBand &
419 : operator=(const GDALThreadSafeRasterBand &) = delete;
420 : };
421 :
422 : /************************************************************************/
423 : /* Global variables initialization. */
424 : /************************************************************************/
425 :
426 : /** Instantiation of the TLS cache of datasets */
427 : thread_local std::unique_ptr<GDALThreadLocalDatasetCache>
428 : GDALThreadSafeDataset::tl_poCache;
429 :
430 : thread_local bool GDALThreadLocalDatasetCache::tl_inDestruction = false;
431 :
432 : /************************************************************************/
433 : /* GDALThreadLocalDatasetCache() */
434 : /************************************************************************/
435 :
436 : /** Constructor of GDALThreadLocalDatasetCache. This is called implicitly
437 : * when GDALThreadSafeDataset::tl_poCache is called the first time by a
438 : * thread.
439 : */
440 41 : GDALThreadLocalDatasetCache::GDALThreadLocalDatasetCache()
441 : : m_poCache(std::make_unique<lru11::Cache<const GDALThreadSafeDataset *,
442 : std::shared_ptr<GDALDataset>>>()),
443 41 : m_nThreadID(CPLGetPID()), m_oCache(*m_poCache.get())
444 : {
445 41 : CPLDebug("GDAL",
446 : "Registering thread-safe dataset cache for thread " CPL_FRMT_GIB,
447 : m_nThreadID);
448 :
449 : // We reference ourselves to the GDALThreadSafeDataset set-of-cache singleton
450 41 : auto &oSetOfCache = GDALThreadSafeDataset::GetSetOfCache();
451 41 : std::lock_guard oLock(oSetOfCache.oMutex);
452 41 : oSetOfCache.oSetOfCache.insert(this);
453 41 : }
454 :
455 : /************************************************************************/
456 : /* ~GDALThreadLocalDatasetCache() */
457 : /************************************************************************/
458 :
459 : /** Destructor of GDALThreadLocalDatasetCache. This is called implicitly when a
460 : * thread is terminated.
461 : */
462 41 : GDALThreadLocalDatasetCache::~GDALThreadLocalDatasetCache()
463 : {
464 41 : tl_inDestruction = true;
465 :
466 : // If GDAL has been de-initialized explicitly (ie GDALDestroyDriverManager()
467 : // has been called), or we are during process termination, do not try to
468 : // free m_poCache at all, which would cause the datasets its owned to be
469 : // destroyed, which will generally lead to crashes in those situations where
470 : // GDAL has been de-initialized.
471 41 : const bool bDriverManagerDestroyed = *GDALGetphDMMutex() == nullptr;
472 41 : if (bDriverManagerDestroyed || !bGlobalCacheValid)
473 : {
474 : #ifndef __COVERITY__
475 : // Leak datasets when GDAL has been de-initialized
476 0 : if (!m_poCache->empty())
477 : {
478 0 : CPL_IGNORE_RET_VAL(m_poCache.release());
479 : }
480 : #endif
481 0 : return;
482 : }
483 :
484 : // Unreference ourselves from the GDALThreadSafeDataset set-of-cache singleton
485 41 : CPLDebug("GDAL",
486 : "Unregistering thread-safe dataset cache for thread " CPL_FRMT_GIB,
487 : m_nThreadID);
488 : {
489 41 : auto &oSetOfCache = GDALThreadSafeDataset::GetSetOfCache();
490 41 : std::lock_guard oLock(oSetOfCache.oMutex);
491 41 : oSetOfCache.oSetOfCache.erase(this);
492 : }
493 :
494 : // Below code is just for debugging purposes and show which internal
495 : // thread-local datasets are released at thread termination.
496 : const auto lambda =
497 156 : [this](const lru11::KeyValuePair<const GDALThreadSafeDataset *,
498 156 : std::shared_ptr<GDALDataset>> &kv)
499 : {
500 312 : CPLDebug("GDAL",
501 : "~GDALThreadLocalDatasetCache(): GDALClose(%s, this=%p) "
502 : "for thread " CPL_FRMT_GIB,
503 156 : kv.value->GetDescription(), kv.value.get(), m_nThreadID);
504 197 : };
505 41 : m_oCache.cwalk(lambda);
506 41 : }
507 :
508 : /************************************************************************/
509 : /* GDALThreadLocalDatasetCacheIsInDestruction() */
510 : /************************************************************************/
511 :
512 219877 : bool GDALThreadLocalDatasetCacheIsInDestruction()
513 : {
514 219877 : return GDALThreadLocalDatasetCache::IsInDestruction();
515 : }
516 :
517 : /************************************************************************/
518 : /* GDALThreadSafeDataset() */
519 : /************************************************************************/
520 :
521 : /** Constructor of GDALThreadSafeDataset.
522 : * It may be called with poPrototypeDSUniquePtr set to null, in the situations
523 : * where GDALThreadSafeDataset doesn't own the prototype dataset.
524 : * poPrototypeDS should always be not-null, and if poPrototypeDSUniquePtr is
525 : * not null, then poPrototypeDS should be equal to poPrototypeDSUniquePtr.get()
526 : */
527 138 : GDALThreadSafeDataset::GDALThreadSafeDataset(
528 : std::unique_ptr<GDALDataset> poPrototypeDSUniquePtr,
529 138 : GDALDataset *poPrototypeDS)
530 : : m_poPrototypeDS(poPrototypeDS),
531 138 : m_aosThreadLocalConfigOptions(CPLGetThreadLocalConfigOptions())
532 : {
533 138 : CPLAssert(poPrototypeDS != nullptr);
534 138 : if (poPrototypeDSUniquePtr)
535 : {
536 123 : CPLAssert(poPrototypeDS == poPrototypeDSUniquePtr.get());
537 : }
538 :
539 : // Replicate the characteristics of the prototype dataset onto ourselves
540 138 : nRasterXSize = poPrototypeDS->GetRasterXSize();
541 138 : nRasterYSize = poPrototypeDS->GetRasterYSize();
542 304 : for (int i = 1; i <= poPrototypeDS->GetRasterCount(); ++i)
543 : {
544 166 : SetBand(i, std::make_unique<GDALThreadSafeRasterBand>(
545 332 : this, this, i, poPrototypeDS->GetRasterBand(i), 0, -1));
546 : }
547 138 : nOpenFlags = GDAL_OF_RASTER | GDAL_OF_THREAD_SAFE;
548 138 : SetDescription(poPrototypeDS->GetDescription());
549 138 : papszOpenOptions = CSLDuplicate(poPrototypeDS->GetOpenOptions());
550 :
551 138 : m_poPrototypeDSUniquePtr = std::move(poPrototypeDSUniquePtr);
552 :
553 : // In the case where we are constructed without owning the prototype
554 : // dataset, let's increase its reference counter though.
555 138 : if (!m_poPrototypeDSUniquePtr)
556 15 : const_cast<GDALDataset *>(m_poPrototypeDS)->Reference();
557 138 : }
558 :
559 : /************************************************************************/
560 : /* Create() */
561 : /************************************************************************/
562 :
563 : /** Utility method used by GDALGetThreadSafeDataset() to construct a
564 : * GDALThreadSafeDataset instance in the case where the GDALThreadSafeDataset
565 : * instance owns the prototype dataset.
566 : */
567 :
568 : /* static */ std::unique_ptr<GDALDataset>
569 124 : GDALThreadSafeDataset::Create(std::unique_ptr<GDALDataset> poPrototypeDS,
570 : int nScopeFlags)
571 : {
572 124 : if (nScopeFlags != GDAL_OF_RASTER)
573 : {
574 0 : CPLError(CE_Failure, CPLE_NotSupported,
575 : "GDALGetThreadSafeDataset(): Only nScopeFlags == "
576 : "GDAL_OF_RASTER is supported");
577 0 : return nullptr;
578 : }
579 124 : if (poPrototypeDS->IsThreadSafe(nScopeFlags))
580 : {
581 1 : return poPrototypeDS;
582 : }
583 123 : if (!poPrototypeDS->CanBeCloned(nScopeFlags, /* bCanShareState = */ true))
584 : {
585 0 : CPLError(CE_Failure, CPLE_NotSupported,
586 : "GDALGetThreadSafeDataset(): Source dataset cannot be "
587 : "cloned");
588 0 : return nullptr;
589 : }
590 123 : auto poPrototypeDSRaw = poPrototypeDS.get();
591 246 : return std::make_unique<GDALThreadSafeDataset>(std::move(poPrototypeDS),
592 123 : poPrototypeDSRaw);
593 : }
594 :
595 : /************************************************************************/
596 : /* Create() */
597 : /************************************************************************/
598 :
599 : /** Utility method used by GDALGetThreadSafeDataset() to construct a
600 : * GDALThreadSafeDataset instance in the case where the GDALThreadSafeDataset
601 : * instance does not own the prototype dataset.
602 : */
603 :
604 : /* static */ GDALDataset *
605 16 : GDALThreadSafeDataset::Create(GDALDataset *poPrototypeDS, int nScopeFlags)
606 : {
607 16 : if (nScopeFlags != GDAL_OF_RASTER)
608 : {
609 0 : CPLError(CE_Failure, CPLE_NotSupported,
610 : "GDALGetThreadSafeDataset(): Only nScopeFlags == "
611 : "GDAL_OF_RASTER is supported");
612 0 : return nullptr;
613 : }
614 16 : if (poPrototypeDS->IsThreadSafe(nScopeFlags))
615 : {
616 1 : poPrototypeDS->Reference();
617 1 : return poPrototypeDS;
618 : }
619 15 : if (!poPrototypeDS->CanBeCloned(nScopeFlags, /* bCanShareState = */ true))
620 : {
621 0 : CPLError(CE_Failure, CPLE_NotSupported,
622 : "GDALGetThreadSafeDataset(): Source dataset cannot be "
623 : "cloned");
624 0 : return nullptr;
625 : }
626 30 : return std::make_unique<GDALThreadSafeDataset>(nullptr, poPrototypeDS)
627 15 : .release();
628 : }
629 :
630 : /************************************************************************/
631 : /* ~GDALThreadSafeDataset() */
632 : /************************************************************************/
633 :
634 276 : GDALThreadSafeDataset::~GDALThreadSafeDataset()
635 : {
636 : // Collect TLS datasets in a vector, and free them after releasing
637 : // g_nInDestructorCounter to limit contention
638 276 : std::vector<std::pair<std::shared_ptr<GDALDataset>, GIntBig>> aoDSToFree;
639 : {
640 138 : auto &oSetOfCache = GetSetOfCache();
641 276 : std::lock_guard oLock(oSetOfCache.oMutex);
642 301 : for (auto *poCache : oSetOfCache.oSetOfCache)
643 : {
644 326 : std::unique_lock oLockCache(poCache->m_oMutex);
645 163 : std::shared_ptr<GDALDataset> poDS;
646 163 : if (poCache->m_oCache.tryGet(this, poDS))
647 : {
648 15 : aoDSToFree.emplace_back(std::move(poDS), poCache->m_nThreadID);
649 15 : poCache->m_oCache.remove(this);
650 : }
651 : }
652 : }
653 :
654 153 : for (const auto &oEntry : aoDSToFree)
655 : {
656 15 : CPLDebug("GDAL",
657 : "~GDALThreadSafeDataset(): GDALClose(%s, this=%p) for "
658 : "thread " CPL_FRMT_GIB,
659 15 : GetDescription(), oEntry.first.get(), oEntry.second);
660 : }
661 : // Actually release TLS datasets
662 138 : aoDSToFree.clear();
663 :
664 138 : GDALThreadSafeDataset::CloseDependentDatasets();
665 276 : }
666 :
667 : /************************************************************************/
668 : /* CloseDependentDatasets() */
669 : /************************************************************************/
670 :
671 : /** Implements GDALDataset::CloseDependentDatasets()
672 : *
673 : * Takes care of releasing the prototype dataset.
674 : *
675 : * As implied by the contract of CloseDependentDatasets(), returns true if
676 : * the prototype dataset has actually been released (or false if
677 : * CloseDependentDatasets() has already been closed)
678 : */
679 138 : int GDALThreadSafeDataset::CloseDependentDatasets()
680 : {
681 138 : int bRet = false;
682 138 : if (m_poPrototypeDSUniquePtr)
683 : {
684 123 : bRet = true;
685 : }
686 15 : else if (m_poPrototypeDS)
687 : {
688 15 : if (const_cast<GDALDataset *>(m_poPrototypeDS)->ReleaseRef())
689 : {
690 0 : bRet = true;
691 : }
692 : }
693 :
694 138 : m_poPrototypeDSUniquePtr.reset();
695 138 : m_poPrototypeDS = nullptr;
696 :
697 138 : return bRet;
698 : }
699 :
700 : /************************************************************************/
701 : /* RefUnderlyingDataset() */
702 : /************************************************************************/
703 :
704 : /** Implements GDALProxyDataset::RefUnderlyingDataset.
705 : *
706 : * This method is called by all virtual methods of GDALDataset overridden by
707 : * RefUnderlyingDataset() when it delegates the calls to the underlying
708 : * dataset.
709 : *
710 : * Our implementation takes care of opening a thread-local dataset, on the
711 : * same underlying dataset of m_poPrototypeDS, if needed, and to insert it
712 : * into a cache for fast later uses by the same thread.
713 : */
714 750605 : GDALDataset *GDALThreadSafeDataset::RefUnderlyingDataset() const
715 : {
716 : // Back-up thread-local config options at the time we are called
717 1499600 : CPLStringList aosTLConfigOptionsBackup(CPLGetThreadLocalConfigOptions());
718 :
719 : // Now merge the thread-local config options at the time where this
720 : // instance has been created with the current ones.
721 : const CPLStringList aosMerged(
722 : CSLMerge(CSLDuplicate(m_aosThreadLocalConfigOptions.List()),
723 1497860 : aosTLConfigOptionsBackup.List()));
724 :
725 : // And make that merged list active
726 748660 : CPLSetThreadLocalConfigOptions(aosMerged.List());
727 :
728 749078 : std::shared_ptr<GDALDataset> poTLSDS;
729 :
730 : // Get the thread-local dataset cache for this thread.
731 749208 : GDALThreadLocalDatasetCache *poCache = tl_poCache.get();
732 748471 : if (!poCache)
733 : {
734 82 : auto poCacheUniquePtr = std::make_unique<GDALThreadLocalDatasetCache>();
735 41 : poCache = poCacheUniquePtr.get();
736 41 : tl_poCache = std::move(poCacheUniquePtr);
737 : }
738 :
739 : // Check if there's an entry in this cache for our current GDALThreadSafeDataset
740 : // instance.
741 1497740 : std::unique_lock oLock(poCache->m_oMutex);
742 749336 : if (poCache->m_oCache.tryGet(this, poTLSDS))
743 : {
744 : // If so, return it, but before returning, make sure to creates a
745 : // "hard" reference to the thread-local dataset, in case it would
746 : // get evicted from poCache->m_oCache (by other threads that would
747 : // access lots of datasets in between)
748 746145 : CPLAssert(!cpl::contains(poCache->m_oMapReferencedDS, this));
749 746093 : auto poDSRet = poTLSDS.get();
750 : poCache->m_oMapReferencedDS.insert(
751 : {this, GDALThreadLocalDatasetCache::
752 746340 : SharedPtrDatasetThreadLocalConfigOptionsPair(
753 1491220 : poTLSDS, std::move(aosTLConfigOptionsBackup))});
754 747220 : return poDSRet;
755 : }
756 :
757 : // "Clone" the prototype dataset, which in 99% of the cases, involves
758 : // doing a GDALDataset::Open() call to re-open it. Do that by temporarily
759 : // dropping the lock that protects poCache->m_oCache.
760 2048 : oLock.unlock();
761 2045 : poTLSDS = m_poPrototypeDS->Clone(GDAL_OF_RASTER, /* bCanShareState=*/true);
762 2045 : if (poTLSDS)
763 : {
764 4088 : CPLDebug("GDAL", "GDALOpen(%s, this=%p) for thread " CPL_FRMT_GIB,
765 2044 : GetDescription(), poTLSDS.get(), CPLGetPID());
766 :
767 : // Check that the re-openeded dataset has the same characteristics
768 : // as "this" / m_poPrototypeDS
769 2044 : if (poTLSDS->GetRasterXSize() != nRasterXSize ||
770 4086 : poTLSDS->GetRasterYSize() != nRasterYSize ||
771 2042 : poTLSDS->GetRasterCount() != nBands)
772 : {
773 3 : poTLSDS.reset();
774 3 : CPLError(CE_Failure, CPLE_AppDefined,
775 : "Re-opened dataset for %s does not share the same "
776 : "characteristics has the master dataset",
777 3 : GetDescription());
778 : }
779 : }
780 :
781 : // Re-acquire the lok
782 2045 : oLock.lock();
783 :
784 : // In case of failed closing, restore the thread-local config options that
785 : // were valid at the beginning of this method, and return in error.
786 2045 : if (!poTLSDS)
787 : {
788 4 : CPLSetThreadLocalConfigOptions(aosTLConfigOptionsBackup.List());
789 4 : return nullptr;
790 : }
791 :
792 : // We have managed to get a thread-local dataset. Insert it into the
793 : // LRU cache and the m_oMapReferencedDS map that holds strong references.
794 2041 : auto poDSRet = poTLSDS.get();
795 : {
796 2041 : poCache->m_oCache.insert(this, poTLSDS);
797 2041 : CPLAssert(!cpl::contains(poCache->m_oMapReferencedDS, this));
798 : poCache->m_oMapReferencedDS.insert(
799 : {this, GDALThreadLocalDatasetCache::
800 2041 : SharedPtrDatasetThreadLocalConfigOptionsPair(
801 4082 : poTLSDS, std::move(aosTLConfigOptionsBackup))});
802 : }
803 2041 : return poDSRet;
804 : }
805 :
806 : /************************************************************************/
807 : /* UnrefUnderlyingDataset() */
808 : /************************************************************************/
809 :
810 : /** Implements GDALProxyDataset::UnrefUnderlyingDataset.
811 : *
812 : * This is called by GDALProxyDataset overridden methods of GDALDataset, when
813 : * they no longer need to access the underlying dataset.
814 : *
815 : * This method actually delegates most of the work to the other
816 : * UnrefUnderlyingDataset() method that takes an explicit GDALThreadLocalDatasetCache*
817 : * instance.
818 : */
819 531 : void GDALThreadSafeDataset::UnrefUnderlyingDataset(
820 : GDALDataset *poUnderlyingDataset) const
821 : {
822 531 : GDALThreadLocalDatasetCache *poCache = tl_poCache.get();
823 531 : CPLAssert(poCache);
824 1062 : std::unique_lock oLock(poCache->m_oMutex);
825 531 : UnrefUnderlyingDataset(poUnderlyingDataset, poCache);
826 531 : }
827 :
828 : /************************************************************************/
829 : /* UnrefUnderlyingDataset() */
830 : /************************************************************************/
831 :
832 : /** Takes care of removing the strong reference to a thread-local dataset
833 : * from the TLS cache of datasets.
834 : */
835 750466 : void GDALThreadSafeDataset::UnrefUnderlyingDataset(
836 : [[maybe_unused]] GDALDataset *poUnderlyingDataset,
837 : GDALThreadLocalDatasetCache *poCache) const
838 : {
839 750466 : auto oIter = poCache->m_oMapReferencedDS.find(this);
840 750011 : CPLAssert(oIter != poCache->m_oMapReferencedDS.end());
841 749974 : CPLAssert(oIter->second.poDS.get() == poUnderlyingDataset);
842 750133 : CPLSetThreadLocalConfigOptions(oIter->second.aosTLConfigOptions.List());
843 749511 : poCache->m_oMapReferencedDS.erase(oIter);
844 750201 : }
845 :
846 : /************************************************************************/
847 : /* GDALThreadSafeRasterBand() */
848 : /************************************************************************/
849 :
850 510 : GDALThreadSafeRasterBand::GDALThreadSafeRasterBand(
851 : GDALThreadSafeDataset *poTSDS, GDALDataset *poParentDS, int nBandIn,
852 510 : GDALRasterBand *poPrototypeBand, int nBaseBandOfMaskBand, int nOvrIdx)
853 : : m_poTSDS(poTSDS), m_poPrototypeBand(poPrototypeBand),
854 510 : m_nBaseBandOfMaskBand(nBaseBandOfMaskBand), m_nOvrIdx(nOvrIdx)
855 : {
856 : // Replicates characteristics of the prototype band.
857 510 : poDS = poParentDS;
858 510 : nBand = nBandIn;
859 510 : eDataType = poPrototypeBand->GetRasterDataType();
860 510 : nRasterXSize = poPrototypeBand->GetXSize();
861 510 : nRasterYSize = poPrototypeBand->GetYSize();
862 510 : poPrototypeBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
863 :
864 510 : if (nBandIn > 0)
865 : {
866 : // For regular bands instantiates a (thread-safe) mask band and
867 : // as many overviews as needed.
868 :
869 170 : m_poMaskBand = std::make_unique<GDALThreadSafeRasterBand>(
870 170 : poTSDS, nullptr, 0, poPrototypeBand->GetMaskBand(), nBandIn,
871 170 : nOvrIdx);
872 170 : if (nOvrIdx < 0)
873 : {
874 166 : const int nOvrCount = poPrototypeBand->GetOverviewCount();
875 170 : for (int iOvrIdx = 0; iOvrIdx < nOvrCount; ++iOvrIdx)
876 : {
877 : m_apoOverviews.emplace_back(
878 4 : std::make_unique<GDALThreadSafeRasterBand>(
879 0 : poTSDS, nullptr, nBandIn,
880 8 : poPrototypeBand->GetOverview(iOvrIdx),
881 8 : nBaseBandOfMaskBand, iOvrIdx));
882 : }
883 : }
884 : }
885 340 : else if (nBaseBandOfMaskBand > 0)
886 : {
887 : // If we are a mask band, nstanciates a (thread-safe) mask band of
888 : // ourselves, but with the trick of negating nBaseBandOfMaskBand to
889 : // avoid infinite recursion...
890 170 : m_poMaskBand = std::make_unique<GDALThreadSafeRasterBand>(
891 170 : poTSDS, nullptr, 0, poPrototypeBand->GetMaskBand(),
892 510 : -nBaseBandOfMaskBand, nOvrIdx);
893 : }
894 510 : }
895 :
896 : /************************************************************************/
897 : /* RefUnderlyingRasterBand() */
898 : /************************************************************************/
899 :
900 : /** Implements GDALProxyRasterBand::RefUnderlyingDataset.
901 : *
902 : * This method is called by all virtual methods of GDALRasterBand overridden by
903 : * RefUnderlyingRasterBand() when it delegates the calls to the underlying
904 : * band.
905 : */
906 : GDALRasterBand *
907 750132 : GDALThreadSafeRasterBand::RefUnderlyingRasterBand(bool /*bForceOpen*/) const
908 : {
909 : // Get a thread-local dataset
910 750132 : auto poTLDS = m_poTSDS->RefUnderlyingDataset();
911 749958 : if (!poTLDS)
912 4 : return nullptr;
913 :
914 : // Get corresponding thread-local band. If m_nBaseBandOfMaskBand is not
915 : // zero, then the base band is indicated into it, otherwise use nBand.
916 749954 : const int nTLSBandIdx =
917 749954 : m_nBaseBandOfMaskBand ? std::abs(m_nBaseBandOfMaskBand) : nBand;
918 749954 : auto poTLRasterBand = poTLDS->GetRasterBand(nTLSBandIdx);
919 749097 : if (!poTLRasterBand)
920 : {
921 0 : CPLError(CE_Failure, CPLE_AppDefined,
922 : "GDALThreadSafeRasterBand::RefUnderlyingRasterBand(): "
923 : "GetRasterBand(%d) failed",
924 : nTLSBandIdx);
925 0 : m_poTSDS->UnrefUnderlyingDataset(poTLDS);
926 0 : return nullptr;
927 : }
928 :
929 : // Get the overview level if needed.
930 749097 : if (m_nOvrIdx >= 0)
931 : {
932 63950 : poTLRasterBand = poTLRasterBand->GetOverview(m_nOvrIdx);
933 63925 : if (!poTLRasterBand)
934 : {
935 1 : CPLError(CE_Failure, CPLE_AppDefined,
936 : "GDALThreadSafeRasterBand::RefUnderlyingRasterBand(): "
937 : "GetOverview(%d) failed",
938 1 : m_nOvrIdx);
939 1 : m_poTSDS->UnrefUnderlyingDataset(poTLDS);
940 1 : return nullptr;
941 : }
942 : }
943 :
944 : // Get the mask band (or the mask band of the mask band) if needed.
945 749071 : if (m_nBaseBandOfMaskBand)
946 : {
947 675640 : poTLRasterBand = poTLRasterBand->GetMaskBand();
948 676291 : if (m_nBaseBandOfMaskBand < 0)
949 25961 : poTLRasterBand = poTLRasterBand->GetMaskBand();
950 : }
951 :
952 : // Check that the thread-local band characteristics are identical to the
953 : // ones of the prototype band.
954 749718 : if (m_poPrototypeBand->GetXSize() != poTLRasterBand->GetXSize() ||
955 1497140 : m_poPrototypeBand->GetYSize() != poTLRasterBand->GetYSize() ||
956 748485 : m_poPrototypeBand->GetRasterDataType() !=
957 748450 : poTLRasterBand->GetRasterDataType()
958 : // m_poPrototypeBand->GetMaskFlags() is not thread-safe
959 : // || m_poPrototypeBand->GetMaskFlags() != poTLRasterBand->GetMaskFlags()
960 : )
961 : {
962 1 : CPLError(CE_Failure, CPLE_AppDefined,
963 : "GDALThreadSafeRasterBand::RefUnderlyingRasterBand(): TLS "
964 : "band has not expected characteristics");
965 1 : m_poTSDS->UnrefUnderlyingDataset(poTLDS);
966 1 : return nullptr;
967 : }
968 : int nThisBlockXSize;
969 : int nThisBlockYSize;
970 : int nTLSBlockXSize;
971 : int nTLSBlockYSize;
972 747845 : m_poPrototypeBand->GetBlockSize(&nThisBlockXSize, &nThisBlockYSize);
973 747803 : poTLRasterBand->GetBlockSize(&nTLSBlockXSize, &nTLSBlockYSize);
974 746961 : if (nThisBlockXSize != nTLSBlockXSize || nThisBlockYSize != nTLSBlockYSize)
975 : {
976 377 : CPLError(CE_Failure, CPLE_AppDefined,
977 : "GDALThreadSafeRasterBand::RefUnderlyingRasterBand(): TLS "
978 : "band has not expected characteristics");
979 1 : m_poTSDS->UnrefUnderlyingDataset(poTLDS);
980 1 : return nullptr;
981 : }
982 :
983 : // Registers the association between the thread-local band and the
984 : // thread-local dataset
985 : {
986 : GDALThreadLocalDatasetCache *poCache =
987 746584 : GDALThreadSafeDataset::tl_poCache.get();
988 748114 : CPLAssert(poCache);
989 748114 : std::unique_lock oLock(poCache->m_oMutex);
990 748401 : CPLAssert(!cpl::contains(poCache->m_oMapReferencedDSFromBand,
991 : poTLRasterBand));
992 748611 : poCache->m_oMapReferencedDSFromBand[poTLRasterBand] = poTLDS;
993 : }
994 : // CPLDebug("GDAL", "%p->RefUnderlyingRasterBand() return %p", this, poTLRasterBand);
995 748509 : return poTLRasterBand;
996 : }
997 :
998 : /************************************************************************/
999 : /* UnrefUnderlyingRasterBand() */
1000 : /************************************************************************/
1001 :
1002 : /** Implements GDALProxyRasterBand::UnrefUnderlyingRasterBand.
1003 : *
1004 : * This is called by GDALProxyRasterBand overridden methods of GDALRasterBand,
1005 : * when they no longer need to access the underlying dataset.
1006 : */
1007 750784 : void GDALThreadSafeRasterBand::UnrefUnderlyingRasterBand(
1008 : GDALRasterBand *poUnderlyingRasterBand) const
1009 : {
1010 : // CPLDebug("GDAL", "%p->UnrefUnderlyingRasterBand(%p)", this, poUnderlyingRasterBand);
1011 :
1012 : // Unregisters the association between the thread-local band and the
1013 : // thread-local dataset
1014 : {
1015 : GDALThreadLocalDatasetCache *poCache =
1016 750784 : GDALThreadSafeDataset::tl_poCache.get();
1017 750451 : CPLAssert(poCache);
1018 1499550 : std::unique_lock oLock(poCache->m_oMutex);
1019 : auto oIter =
1020 750587 : poCache->m_oMapReferencedDSFromBand.find(poUnderlyingRasterBand);
1021 749949 : CPLAssert(oIter != poCache->m_oMapReferencedDSFromBand.end());
1022 :
1023 749913 : m_poTSDS->UnrefUnderlyingDataset(oIter->second, poCache);
1024 749714 : poCache->m_oMapReferencedDSFromBand.erase(oIter);
1025 : }
1026 749250 : }
1027 :
1028 : /************************************************************************/
1029 : /* GetMaskBand() */
1030 : /************************************************************************/
1031 :
1032 : /** Implements GDALRasterBand::GetMaskBand
1033 : */
1034 10001 : GDALRasterBand *GDALThreadSafeRasterBand::GetMaskBand()
1035 : {
1036 10001 : return m_poMaskBand ? m_poMaskBand.get() : this;
1037 : }
1038 :
1039 : /************************************************************************/
1040 : /* GetOverviewCount() */
1041 : /************************************************************************/
1042 :
1043 : /** Implements GDALRasterBand::GetOverviewCount
1044 : */
1045 4003 : int GDALThreadSafeRasterBand::GetOverviewCount()
1046 : {
1047 4003 : return static_cast<int>(m_apoOverviews.size());
1048 : }
1049 :
1050 : /************************************************************************/
1051 : /* GetOverview() */
1052 : /************************************************************************/
1053 :
1054 : /** Implements GDALRasterBand::GetOverview
1055 : */
1056 6005 : GDALRasterBand *GDALThreadSafeRasterBand::GetOverview(int nIdx)
1057 : {
1058 6005 : if (nIdx < 0 || nIdx >= static_cast<int>(m_apoOverviews.size()))
1059 2 : return nullptr;
1060 6003 : return m_apoOverviews[nIdx].get();
1061 : }
1062 :
1063 : /************************************************************************/
1064 : /* GetRasterSampleOverview() */
1065 : /************************************************************************/
1066 :
1067 : /** Implements GDALRasterBand::GetRasterSampleOverview
1068 : */
1069 : GDALRasterBand *
1070 2000 : GDALThreadSafeRasterBand::GetRasterSampleOverview(GUIntBig nDesiredSamples)
1071 :
1072 : {
1073 : // Call the base implementation, and do not forward to proxy
1074 2000 : return GDALRasterBand::GetRasterSampleOverview(nDesiredSamples);
1075 : }
1076 :
1077 : /************************************************************************/
1078 : /* GetDefaultRAT() */
1079 : /************************************************************************/
1080 :
1081 : /** Implements GDALRasterBand::GetDefaultRAT
1082 : *
1083 : * This is a bit tricky to do as GDALRasterAttributeTable has virtual methods
1084 : * with potential (non thread-safe) side-effects. The clean solution would be
1085 : * to implement a GDALThreadSafeRAT wrapper class, but this is a bit too much
1086 : * effort. So for now, we check if the RAT returned by the prototype band is
1087 : * an instance of GDALDefaultRasterAttributeTable. If it is, given that this
1088 : * class has thread-safe getters, we can directly return it.
1089 : * Otherwise return in error.
1090 : */
1091 3 : GDALRasterAttributeTable *GDALThreadSafeRasterBand::GetDefaultRAT()
1092 : {
1093 6 : std::lock_guard oGuard(m_poTSDS->m_oPrototypeDSMutex);
1094 : const auto poRAT =
1095 3 : const_cast<GDALRasterBand *>(m_poPrototypeBand)->GetDefaultRAT();
1096 3 : if (!poRAT)
1097 1 : return nullptr;
1098 :
1099 2 : if (dynamic_cast<GDALDefaultRasterAttributeTable *>(poRAT))
1100 1 : return poRAT;
1101 :
1102 1 : CPLError(CE_Failure, CPLE_AppDefined,
1103 : "GDALThreadSafeRasterBand::GetDefaultRAT() not supporting a "
1104 : "non-GDALDefaultRasterAttributeTable implementation");
1105 1 : return nullptr;
1106 : }
1107 :
1108 : #endif // DOXYGEN_SKIP
1109 :
1110 : /************************************************************************/
1111 : /* GDALDataset::IsThreadSafe() */
1112 : /************************************************************************/
1113 :
1114 : /** Return whether this dataset, and its related objects (typically raster
1115 : * bands), can be called for the intended scope.
1116 : *
1117 : * Note that in the current implementation, nScopeFlags should be set to
1118 : * GDAL_OF_RASTER, as thread-safety is limited to read-only operations and
1119 : * excludes operations on vector layers (OGRLayer) or multidimensional API
1120 : * (GDALGroup, GDALMDArray, etc.)
1121 : *
1122 : * This is the same as the C function GDALDatasetIsThreadSafe().
1123 : *
1124 : * @since 3.10
1125 : */
1126 8111160 : bool GDALDataset::IsThreadSafe(int nScopeFlags) const
1127 : {
1128 29910 : return (nOpenFlags & GDAL_OF_THREAD_SAFE) != 0 &&
1129 8141070 : nScopeFlags == GDAL_OF_RASTER && (nOpenFlags & GDAL_OF_RASTER) != 0;
1130 : }
1131 :
1132 : /************************************************************************/
1133 : /* GDALDatasetIsThreadSafe() */
1134 : /************************************************************************/
1135 :
1136 : /** Return whether this dataset, and its related objects (typically raster
1137 : * bands), can be called for the intended scope.
1138 : *
1139 : * Note that in the current implementation, nScopeFlags should be set to
1140 : * GDAL_OF_RASTER, as thread-safety is limited to read-only operations and
1141 : * excludes operations on vector layers (OGRLayer) or multidimensional API
1142 : * (GDALGroup, GDALMDArray, etc.)
1143 : *
1144 : * This is the same as the C++ method GDALDataset::IsThreadSafe().
1145 : *
1146 : * @param hDS Source dataset
1147 : * @param nScopeFlags Intended scope of use.
1148 : * Only GDAL_OF_RASTER is supported currently.
1149 : * @param papszOptions Options. None currently.
1150 : *
1151 : * @since 3.10
1152 : */
1153 5 : bool GDALDatasetIsThreadSafe(GDALDatasetH hDS, int nScopeFlags,
1154 : CSLConstList papszOptions)
1155 : {
1156 5 : VALIDATE_POINTER1(hDS, __func__, false);
1157 :
1158 5 : CPL_IGNORE_RET_VAL(papszOptions);
1159 :
1160 5 : return GDALDataset::FromHandle(hDS)->IsThreadSafe(nScopeFlags);
1161 : }
1162 :
1163 : /************************************************************************/
1164 : /* GDALGetThreadSafeDataset() */
1165 : /************************************************************************/
1166 :
1167 : /** Return a thread-safe dataset.
1168 : *
1169 : * In the general case, this thread-safe dataset will open a
1170 : * behind-the-scenes per-thread dataset (re-using the name and open options of poDS),
1171 : * the first time a thread calls a method on the thread-safe dataset, and will
1172 : * transparently redirect calls from the calling thread to this behind-the-scenes
1173 : * per-thread dataset. Hence there is an initial setup cost per thread.
1174 : * Datasets of the MEM driver cannot be opened by name, but this function will
1175 : * take care of "cloning" them, using the same backing memory, when needed.
1176 : *
1177 : * Ownership of the passed dataset is transferred to the thread-safe dataset.
1178 : *
1179 : * The function may also return the passed dataset if it is already thread-safe.
1180 : *
1181 : * @param poDS Source dataset
1182 : * @param nScopeFlags Intended scope of use.
1183 : * Only GDAL_OF_RASTER is supported currently.
1184 : *
1185 : * @return a new thread-safe dataset, or nullptr in case of error.
1186 : *
1187 : * @since 3.10
1188 : */
1189 : std::unique_ptr<GDALDataset>
1190 124 : GDALGetThreadSafeDataset(std::unique_ptr<GDALDataset> poDS, int nScopeFlags)
1191 : {
1192 124 : return GDALThreadSafeDataset::Create(std::move(poDS), nScopeFlags);
1193 : }
1194 :
1195 : /************************************************************************/
1196 : /* GDALGetThreadSafeDataset() */
1197 : /************************************************************************/
1198 :
1199 : /** Return a thread-safe dataset.
1200 : *
1201 : * In the general case, this thread-safe dataset will open a
1202 : * behind-the-scenes per-thread dataset (re-using the name and open options of poDS),
1203 : * the first time a thread calls a method on the thread-safe dataset, and will
1204 : * transparently redirect calls from the calling thread to this behind-the-scenes
1205 : * per-thread dataset. Hence there is an initial setup cost per thread.
1206 : * Datasets of the MEM driver cannot be opened by name, but this function will
1207 : * take care of "cloning" them, using the same backing memory, when needed.
1208 : *
1209 : * The life-time of the passed dataset must be longer than the one of
1210 : * the returned thread-safe dataset.
1211 : *
1212 : * Note that this function does increase the reference count on poDS while
1213 : * it is being used
1214 : *
1215 : * The function may also return the passed dataset if it is already thread-safe.
1216 : * A non-nullptr returned dataset must be released with ReleaseRef().
1217 : *
1218 : * Patterns like the following one are valid:
1219 : * \code{.cpp}
1220 : * auto poDS = GDALDataset::Open(...);
1221 : * auto poThreadSafeDS = GDALGetThreadSafeDataset(poDS, GDAL_OF_RASTER | GDAL_OF_THREAD_SAFE);
1222 : * poDS->ReleaseRef();
1223 : * if (poThreadSafeDS )
1224 : * {
1225 : * // ... do something with poThreadSafeDS ...
1226 : * poThreadSafeDS->ReleaseRef();
1227 : * }
1228 : * \endcode
1229 : *
1230 : * @param poDS Source dataset
1231 : * @param nScopeFlags Intended scope of use.
1232 : * Only GDAL_OF_RASTER is supported currently.
1233 : *
1234 : * @return a new thread-safe dataset or poDS, or nullptr in case of error.
1235 :
1236 : * @since 3.10
1237 : */
1238 16 : GDALDataset *GDALGetThreadSafeDataset(GDALDataset *poDS, int nScopeFlags)
1239 : {
1240 16 : return GDALThreadSafeDataset::Create(poDS, nScopeFlags);
1241 : }
1242 :
1243 : /************************************************************************/
1244 : /* GDALGetThreadSafeDataset() */
1245 : /************************************************************************/
1246 :
1247 : /** Return a thread-safe dataset.
1248 : *
1249 : * In the general case, this thread-safe dataset will open a
1250 : * behind-the-scenes per-thread dataset (re-using the name and open options of hDS),
1251 : * the first time a thread calls a method on the thread-safe dataset, and will
1252 : * transparently redirect calls from the calling thread to this behind-the-scenes
1253 : * per-thread dataset. Hence there is an initial setup cost per thread.
1254 : * Datasets of the MEM driver cannot be opened by name, but this function will
1255 : * take care of "cloning" them, using the same backing memory, when needed.
1256 : *
1257 : * The life-time of the passed dataset must be longer than the one of
1258 : * the returned thread-safe dataset.
1259 : *
1260 : * Note that this function does increase the reference count on poDS while
1261 : * it is being used
1262 : *
1263 : * The function may also return the passed dataset if it is already thread-safe.
1264 : * A non-nullptr returned dataset must be released with GDALReleaseDataset().
1265 : *
1266 : * \code{.cpp}
1267 : * hDS = GDALOpenEx(...);
1268 : * hThreadSafeDS = GDALGetThreadSafeDataset(hDS, GDAL_OF_RASTER | GDAL_OF_THREAD_SAFE, NULL);
1269 : * GDALReleaseDataset(hDS);
1270 : * if( hThreadSafeDS )
1271 : * {
1272 : * // ... do something with hThreadSafeDS ...
1273 : * GDALReleaseDataset(hThreadSafeDS);
1274 : * }
1275 : * \endcode
1276 : *
1277 : * @param hDS Source dataset
1278 : * @param nScopeFlags Intended scope of use.
1279 : * Only GDAL_OF_RASTER is supported currently.
1280 : * @param papszOptions Options. None currently.
1281 : *
1282 : * @since 3.10
1283 : */
1284 8 : GDALDatasetH GDALGetThreadSafeDataset(GDALDatasetH hDS, int nScopeFlags,
1285 : CSLConstList papszOptions)
1286 : {
1287 8 : VALIDATE_POINTER1(hDS, __func__, nullptr);
1288 :
1289 8 : CPL_IGNORE_RET_VAL(papszOptions);
1290 8 : return GDALDataset::ToHandle(
1291 8 : GDALGetThreadSafeDataset(GDALDataset::FromHandle(hDS), nScopeFlags));
1292 : }
|