Coverage Report

Created: 2025-12-31 06:48

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