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