Coverage Report

Created: 2025-06-09 08:44

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