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