Coverage Report

Created: 2025-11-16 06:25

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