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