LCOV - code coverage report
Current view: top level - gcore - gdalthreadsafedataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 301 317 95.0 %
Date: 2025-10-21 22:35:35 Functions: 42 42 100.0 %

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

Generated by: LCOV version 1.14