Coverage Report

Created: 2025-06-13 06:18

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