LCOV - code coverage report
Current view: top level - gcore - gdalthreadsafedataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 296 312 94.9 %
Date: 2024-11-21 22:18:42 Functions: 40 40 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 "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      743359 :         SharedPtrDatasetThreadLocalConfigOptionsPair(
     108             :             const std::shared_ptr<GDALDataset> &poDSIn,
     109             :             CPLStringList &&aosTLConfigOptionsIn)
     110      743359 :             : poDS(poDSIn), aosTLConfigOptions(std::move(aosTLConfigOptionsIn))
     111             :         {
     112      742201 :         }
     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         800 :     const OGRSpatialReference *GetSpatialRef() const override
     170             :     {
     171         800 :         std::lock_guard oGuard(m_oPrototypeDSMutex);
     172         800 :         if (m_oSRS.IsEmpty())
     173             :         {
     174         401 :             auto poSRS = m_poPrototypeDS->GetSpatialRef();
     175         401 :             if (poSRS)
     176             :             {
     177           1 :                 m_oSRS.AssignAndSetThreadSafe(*poSRS);
     178             :             }
     179             :         }
     180        1600 :         return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
     181             :     }
     182             : 
     183         800 :     const OGRSpatialReference *GetGCPSpatialRef() const override
     184             :     {
     185         800 :         std::lock_guard oGuard(m_oPrototypeDSMutex);
     186         800 :         if (m_oGCPSRS.IsEmpty())
     187             :         {
     188         401 :             auto poSRS = m_poPrototypeDS->GetGCPSpatialRef();
     189         401 :             if (poSRS)
     190             :             {
     191           1 :                 m_oGCPSRS.AssignAndSetThreadSafe(*poSRS);
     192             :             }
     193             :         }
     194        1600 :         return m_oGCPSRS.IsEmpty() ? nullptr : &m_oGCPSRS;
     195             :     }
     196             : 
     197         400 :     const GDAL_GCP *GetGCPs() override
     198             :     {
     199         800 :         std::lock_guard oGuard(m_oPrototypeDSMutex);
     200         800 :         return const_cast<GDALDataset *>(m_poPrototypeDS)->GetGCPs();
     201             :     }
     202             : 
     203           3 :     const char *GetMetadataItem(const char *pszName,
     204             :                                 const char *pszDomain = "") override
     205             :     {
     206           6 :         std::lock_guard oGuard(m_oPrototypeDSMutex);
     207           3 :         return const_cast<GDALDataset *>(m_poPrototypeDS)
     208           6 :             ->GetMetadataItem(pszName, pszDomain);
     209             :     }
     210             : 
     211           2 :     char **GetMetadata(const char *pszDomain = "") override
     212             :     {
     213           4 :         std::lock_guard oGuard(m_oPrototypeDSMutex);
     214           2 :         return const_cast<GDALDataset *>(m_poPrototypeDS)
     215           4 :             ->GetMetadata(pszDomain);
     216             :     }
     217             : 
     218             :     /* End of methods that forward on the prototype dataset */
     219             : 
     220           1 :     GDALAsyncReader *BeginAsyncReader(int, int, int, int, void *, int, int,
     221             :                                       GDALDataType, int, int *, int, int, int,
     222             :                                       char **) override
     223             :     {
     224           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     225             :                  "GDALThreadSafeDataset::BeginAsyncReader() not supported");
     226           1 :         return nullptr;
     227             :     }
     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           1 :         GlobalCache()
     280           1 :         {
     281           1 :             bGlobalCacheValid = true;
     282           1 :         }
     283             : 
     284           1 :         ~GlobalCache()
     285           2 :         {
     286           1 :             bGlobalCacheValid = false;
     287           1 :         }
     288             :     };
     289             : 
     290             :     /** Returns a singleton for a GlobalCache instance that references all
     291             :      * GDALThreadLocalDatasetCache* instances.
     292             :      */
     293         196 :     static GlobalCache &GetSetOfCache()
     294             :     {
     295         196 :         static GlobalCache cache;
     296         196 :         return cache;
     297             :     }
     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           2 :     const char *GetMetadataItem(const char *pszName,
     339             :                                 const char *pszDomain = "") override
     340             :     {
     341           4 :         std::lock_guard oGuard(m_poTSDS->m_oPrototypeDSMutex);
     342           2 :         return const_cast<GDALRasterBand *>(m_poPrototypeBand)
     343           4 :             ->GetMetadataItem(pszName, pszDomain);
     344             :     }
     345             : 
     346           2 :     char **GetMetadata(const char *pszDomain = "") override
     347             :     {
     348           4 :         std::lock_guard oGuard(m_poTSDS->m_oPrototypeDSMutex);
     349           2 :         return const_cast<GDALRasterBand *>(m_poPrototypeBand)
     350           4 :             ->GetMetadata(pszDomain);
     351             :     }
     352             : 
     353           1 :     const char *GetUnitType() override
     354             :     {
     355           2 :         std::lock_guard oGuard(m_poTSDS->m_oPrototypeDSMutex);
     356           2 :         return const_cast<GDALRasterBand *>(m_poPrototypeBand)->GetUnitType();
     357             :     }
     358             : 
     359           2 :     GDALColorTable *GetColorTable() override
     360             :     {
     361           4 :         std::lock_guard oGuard(m_poTSDS->m_oPrototypeDSMutex);
     362           4 :         return const_cast<GDALRasterBand *>(m_poPrototypeBand)->GetColorTable();
     363             :     }
     364             : 
     365             :     /* End of methods that forward on the prototype band */
     366             : 
     367           1 :     CPLVirtualMem *GetVirtualMemAuto(GDALRWFlag, int *, GIntBig *,
     368             :                                      char **) override
     369             :     {
     370           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     371             :                  "GDALThreadSafeRasterBand::GetVirtualMemAuto() not supported");
     372           1 :         return nullptr;
     373             :     }
     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          33 : GDALThreadLocalDatasetCache::GDALThreadLocalDatasetCache()
     430             :     : m_poCache(std::make_unique<lru11::Cache<const GDALThreadSafeDataset *,
     431             :                                               std::shared_ptr<GDALDataset>>>()),
     432          33 :       m_nThreadID(CPLGetPID()), m_oCache(*m_poCache.get())
     433             : {
     434          33 :     CPLDebug("GDAL",
     435             :              "Registering thread-safe dataset cache for thread " CPL_FRMT_GIB,
     436             :              m_nThreadID);
     437             : 
     438             :     // We reference ourselves to the GDALThreadSafeDataset set-of-cache singleton
     439          33 :     auto &oSetOfCache = GDALThreadSafeDataset::GetSetOfCache();
     440          33 :     std::lock_guard oLock(oSetOfCache.oMutex);
     441          33 :     oSetOfCache.oSetOfCache.insert(this);
     442          33 : }
     443             : 
     444             : /************************************************************************/
     445             : /*                   ~GDALThreadLocalDatasetCache()                     */
     446             : /************************************************************************/
     447             : 
     448             : /** Destructor of GDALThreadLocalDatasetCache. This is called implicitly when a
     449             :  * thread is terminated.
     450             :  */
     451          33 : GDALThreadLocalDatasetCache::~GDALThreadLocalDatasetCache()
     452             : {
     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          33 :     const bool bDriverManagerDestroyed = *GDALGetphDMMutex() == nullptr;
     459          33 :     if (bDriverManagerDestroyed || !bGlobalCacheValid)
     460             :     {
     461             :         // Leak datasets when GDAL has been de-initialized
     462           0 :         if (!m_poCache->empty())
     463             :         {
     464             :             // coverity[leaked_storage]
     465           0 :             CPL_IGNORE_RET_VAL(m_poCache.release());
     466             :         }
     467           0 :         return;
     468             :     }
     469             : 
     470             :     // Unreference ourselves from the GDALThreadSafeDataset set-of-cache singleton
     471          33 :     CPLDebug("GDAL",
     472             :              "Unregistering thread-safe dataset cache for thread " CPL_FRMT_GIB,
     473             :              m_nThreadID);
     474             :     {
     475          33 :         auto &oSetOfCache = GDALThreadSafeDataset::GetSetOfCache();
     476          33 :         std::lock_guard oLock(oSetOfCache.oMutex);
     477          33 :         oSetOfCache.oSetOfCache.erase(this);
     478             :     }
     479             : 
     480             :     // Below code is just for debugging purposes and show which internal
     481             :     // thread-local datasets are released at thread termination.
     482             :     const auto lambda =
     483         155 :         [this](const lru11::KeyValuePair<const GDALThreadSafeDataset *,
     484         155 :                                          std::shared_ptr<GDALDataset>> &kv)
     485             :     {
     486         310 :         CPLDebug("GDAL",
     487             :                  "~GDALThreadLocalDatasetCache(): GDALClose(%s, this=%p) "
     488             :                  "for thread " CPL_FRMT_GIB,
     489         155 :                  kv.value->GetDescription(), kv.value.get(), m_nThreadID);
     490         188 :     };
     491          33 :     m_oCache.cwalk(lambda);
     492          33 : }
     493             : 
     494             : /************************************************************************/
     495             : /*                     GDALThreadSafeDataset()                          */
     496             : /************************************************************************/
     497             : 
     498             : /** Constructor of GDALThreadSafeDataset.
     499             :  * It may be called with poPrototypeDSUniquePtr set to null, in the situations
     500             :  * where GDALThreadSafeDataset doesn't own the prototype dataset.
     501             :  * poPrototypeDS should always be not-null, and if poPrototypeDSUniquePtr is
     502             :  * not null, then poPrototypeDS should be equal to poPrototypeDSUniquePtr.get()
     503             :  */
     504         130 : GDALThreadSafeDataset::GDALThreadSafeDataset(
     505             :     std::unique_ptr<GDALDataset> poPrototypeDSUniquePtr,
     506         130 :     GDALDataset *poPrototypeDS)
     507             :     : m_poPrototypeDS(poPrototypeDS),
     508         130 :       m_aosThreadLocalConfigOptions(CPLGetThreadLocalConfigOptions())
     509             : {
     510         130 :     CPLAssert(poPrototypeDS != nullptr);
     511         130 :     if (poPrototypeDSUniquePtr)
     512             :     {
     513         123 :         CPLAssert(poPrototypeDS == poPrototypeDSUniquePtr.get());
     514             :     }
     515             : 
     516             :     // Replicate the characteristics of the prototype dataset onto ourselves
     517         130 :     nRasterXSize = poPrototypeDS->GetRasterXSize();
     518         130 :     nRasterYSize = poPrototypeDS->GetRasterYSize();
     519         272 :     for (int i = 1; i <= poPrototypeDS->GetRasterCount(); ++i)
     520             :     {
     521         142 :         SetBand(i, std::make_unique<GDALThreadSafeRasterBand>(
     522         284 :                        this, this, i, poPrototypeDS->GetRasterBand(i), 0, -1));
     523             :     }
     524         130 :     nOpenFlags = GDAL_OF_RASTER | GDAL_OF_THREAD_SAFE;
     525         130 :     SetDescription(poPrototypeDS->GetDescription());
     526         130 :     papszOpenOptions = CSLDuplicate(poPrototypeDS->GetOpenOptions());
     527             : 
     528         130 :     m_poPrototypeDSUniquePtr = std::move(poPrototypeDSUniquePtr);
     529             : 
     530             :     // In the case where we are constructed without owning the prototype
     531             :     // dataset, let's increase its reference counter though.
     532         130 :     if (!m_poPrototypeDSUniquePtr)
     533           7 :         const_cast<GDALDataset *>(m_poPrototypeDS)->Reference();
     534         130 : }
     535             : 
     536             : /************************************************************************/
     537             : /*                             Create()                                 */
     538             : /************************************************************************/
     539             : 
     540             : /** Utility method used by GDALGetThreadSafeDataset() to construct a
     541             :  * GDALThreadSafeDataset instance in the case where the GDALThreadSafeDataset
     542             :  * instance owns the prototype dataset.
     543             :  */
     544             : 
     545             : /* static */ std::unique_ptr<GDALDataset>
     546         124 : GDALThreadSafeDataset::Create(std::unique_ptr<GDALDataset> poPrototypeDS,
     547             :                               int nScopeFlags)
     548             : {
     549         124 :     if (nScopeFlags != GDAL_OF_RASTER)
     550             :     {
     551           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     552             :                  "GDALGetThreadSafeDataset(): Only nScopeFlags == "
     553             :                  "GDAL_OF_RASTER is supported");
     554           0 :         return nullptr;
     555             :     }
     556         124 :     if (poPrototypeDS->IsThreadSafe(nScopeFlags))
     557             :     {
     558           1 :         return poPrototypeDS;
     559             :     }
     560         123 :     if (!poPrototypeDS->CanBeCloned(nScopeFlags, /* bCanShareState = */ true))
     561             :     {
     562           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     563             :                  "GDALGetThreadSafeDataset(): Source dataset cannot be "
     564             :                  "cloned");
     565           0 :         return nullptr;
     566             :     }
     567         123 :     auto poPrototypeDSRaw = poPrototypeDS.get();
     568         246 :     return std::make_unique<GDALThreadSafeDataset>(std::move(poPrototypeDS),
     569         123 :                                                    poPrototypeDSRaw);
     570             : }
     571             : 
     572             : /************************************************************************/
     573             : /*                             Create()                                 */
     574             : /************************************************************************/
     575             : 
     576             : /** Utility method used by GDALGetThreadSafeDataset() to construct a
     577             :  * GDALThreadSafeDataset instance in the case where the GDALThreadSafeDataset
     578             :  * instance does not own the prototype dataset.
     579             :  */
     580             : 
     581             : /* static */ GDALDataset *
     582           8 : GDALThreadSafeDataset::Create(GDALDataset *poPrototypeDS, int nScopeFlags)
     583             : {
     584           8 :     if (nScopeFlags != GDAL_OF_RASTER)
     585             :     {
     586           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     587             :                  "GDALGetThreadSafeDataset(): Only nScopeFlags == "
     588             :                  "GDAL_OF_RASTER is supported");
     589           0 :         return nullptr;
     590             :     }
     591           8 :     if (poPrototypeDS->IsThreadSafe(nScopeFlags))
     592             :     {
     593           1 :         poPrototypeDS->Reference();
     594           1 :         return poPrototypeDS;
     595             :     }
     596           7 :     if (!poPrototypeDS->CanBeCloned(nScopeFlags, /* bCanShareState = */ true))
     597             :     {
     598           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     599             :                  "GDALGetThreadSafeDataset(): Source dataset cannot be "
     600             :                  "cloned");
     601           0 :         return nullptr;
     602             :     }
     603          14 :     return std::make_unique<GDALThreadSafeDataset>(nullptr, poPrototypeDS)
     604           7 :         .release();
     605             : }
     606             : 
     607             : /************************************************************************/
     608             : /*                    ~GDALThreadSafeDataset()                          */
     609             : /************************************************************************/
     610             : 
     611         260 : GDALThreadSafeDataset::~GDALThreadSafeDataset()
     612             : {
     613             :     // Collect TLS datasets in a vector, and free them after releasing
     614             :     // g_nInDestructorCounter to limit contention
     615         260 :     std::vector<std::pair<std::shared_ptr<GDALDataset>, GIntBig>> aoDSToFree;
     616             :     {
     617         130 :         auto &oSetOfCache = GetSetOfCache();
     618         260 :         std::lock_guard oLock(oSetOfCache.oMutex);
     619         262 :         for (auto *poCache : oSetOfCache.oSetOfCache)
     620             :         {
     621         264 :             std::unique_lock oLockCache(poCache->m_oMutex);
     622         132 :             std::shared_ptr<GDALDataset> poDS;
     623         132 :             if (poCache->m_oCache.tryGet(this, poDS))
     624             :             {
     625           8 :                 aoDSToFree.emplace_back(std::move(poDS), poCache->m_nThreadID);
     626           8 :                 poCache->m_oCache.remove(this);
     627             :             }
     628             :         }
     629             :     }
     630             : 
     631         138 :     for (const auto &oEntry : aoDSToFree)
     632             :     {
     633           8 :         CPLDebug("GDAL",
     634             :                  "~GDALThreadSafeDataset(): GDALClose(%s, this=%p) for "
     635             :                  "thread " CPL_FRMT_GIB,
     636           8 :                  GetDescription(), oEntry.first.get(), oEntry.second);
     637             :     }
     638             :     // Actually release TLS datasets
     639         130 :     aoDSToFree.clear();
     640             : 
     641         130 :     GDALThreadSafeDataset::CloseDependentDatasets();
     642         260 : }
     643             : 
     644             : /************************************************************************/
     645             : /*                      CloseDependentDatasets()                        */
     646             : /************************************************************************/
     647             : 
     648             : /** Implements GDALDataset::CloseDependentDatasets()
     649             :  *
     650             :  * Takes care of releasing the prototype dataset.
     651             :  *
     652             :  * As implied by the contract of CloseDependentDatasets(), returns true if
     653             :  * the prototype dataset has actually been released (or false if
     654             :  * CloseDependentDatasets() has already been closed)
     655             :  */
     656         130 : int GDALThreadSafeDataset::CloseDependentDatasets()
     657             : {
     658         130 :     int bRet = false;
     659         130 :     if (m_poPrototypeDSUniquePtr)
     660             :     {
     661         123 :         bRet = true;
     662             :     }
     663           7 :     else if (m_poPrototypeDS)
     664             :     {
     665           7 :         if (const_cast<GDALDataset *>(m_poPrototypeDS)->ReleaseRef())
     666             :         {
     667           0 :             bRet = true;
     668             :         }
     669             :     }
     670             : 
     671         130 :     m_poPrototypeDSUniquePtr.reset();
     672         130 :     m_poPrototypeDS = nullptr;
     673             : 
     674         130 :     return bRet;
     675             : }
     676             : 
     677             : /************************************************************************/
     678             : /*                       RefUnderlyingDataset()                         */
     679             : /************************************************************************/
     680             : 
     681             : /** Implements GDALProxyDataset::RefUnderlyingDataset.
     682             :  *
     683             :  * This method is called by all virtual methods of GDALDataset overridden by
     684             :  * RefUnderlyingDataset() when it delegates the calls to the underlying
     685             :  * dataset.
     686             :  *
     687             :  * Our implementation takes care of opening a thread-local dataset, on the
     688             :  * same underlying dataset of m_poPrototypeDS, if needed, and to insert it
     689             :  * into a cache for fast later uses by the same thread.
     690             :  */
     691      744596 : GDALDataset *GDALThreadSafeDataset::RefUnderlyingDataset() const
     692             : {
     693             :     // Back-up thread-local config options at the time we are called
     694     1486920 :     CPLStringList aosTLConfigOptionsBackup(CPLGetThreadLocalConfigOptions());
     695             : 
     696             :     // Now merge the thread-local config options at the time where this
     697             :     // instance has been created with the current ones.
     698             :     const CPLStringList aosMerged(
     699             :         CSLMerge(CSLDuplicate(m_aosThreadLocalConfigOptions.List()),
     700     1481590 :                  aosTLConfigOptionsBackup.List()));
     701             : 
     702             :     // And make that merged list active
     703      741231 :     CPLSetThreadLocalConfigOptions(aosMerged.List());
     704             : 
     705      739228 :     std::shared_ptr<GDALDataset> poTLSDS;
     706             : 
     707             :     // Get the thread-local dataset cache for this thread.
     708      745527 :     GDALThreadLocalDatasetCache *poCache = tl_poCache.get();
     709      740993 :     if (!poCache)
     710             :     {
     711          66 :         auto poCacheUniquePtr = std::make_unique<GDALThreadLocalDatasetCache>();
     712          33 :         poCache = poCacheUniquePtr.get();
     713          33 :         tl_poCache = std::move(poCacheUniquePtr);
     714             :     }
     715             : 
     716             :     // Check if there's an entry in this cache for our current GDALThreadSafeDataset
     717             :     // instance.
     718     1484120 :     std::unique_lock oLock(poCache->m_oMutex);
     719      743925 :     if (poCache->m_oCache.tryGet(this, poTLSDS))
     720             :     {
     721             :         // If so, return it, but before returning, make sure to creates a
     722             :         // "hard" reference to the thread-local dataset, in case it would
     723             :         // get evicted from poCache->m_oCache (by other threads that would
     724             :         // access lots of datasets in between)
     725      737872 :         CPLAssert(!cpl::contains(poCache->m_oMapReferencedDS, this));
     726      736186 :         auto poDSRet = poTLSDS.get();
     727             :         poCache->m_oMapReferencedDS.insert(
     728             :             {this, GDALThreadLocalDatasetCache::
     729      740804 :                        SharedPtrDatasetThreadLocalConfigOptionsPair(
     730     1478710 :                            poTLSDS, std::move(aosTLConfigOptionsBackup))});
     731      741090 :         return poDSRet;
     732             :     }
     733             : 
     734             :     // "Clone" the prototype dataset, which in 99% of the cases, involves
     735             :     // doing a GDALDataset::Open() call to re-open it. Do that by temporarily
     736             :     // dropping the lock that protects poCache->m_oCache.
     737             :     // coverity[uninit_use_in_call]
     738        3502 :     oLock.unlock();
     739        2037 :     poTLSDS = m_poPrototypeDS->Clone(GDAL_OF_RASTER, /* bCanShareState=*/true);
     740        2037 :     if (poTLSDS)
     741             :     {
     742        4072 :         CPLDebug("GDAL", "GDALOpen(%s, this=%p) for thread " CPL_FRMT_GIB,
     743        2036 :                  GetDescription(), poTLSDS.get(), CPLGetPID());
     744             : 
     745             :         // Check that the re-openeded dataset has the same characteristics
     746             :         // as "this" / m_poPrototypeDS
     747        2036 :         if (poTLSDS->GetRasterXSize() != nRasterXSize ||
     748        4070 :             poTLSDS->GetRasterYSize() != nRasterYSize ||
     749        2034 :             poTLSDS->GetRasterCount() != nBands)
     750             :         {
     751           3 :             poTLSDS.reset();
     752           3 :             CPLError(CE_Failure, CPLE_AppDefined,
     753             :                      "Re-opened dataset for %s does not share the same "
     754             :                      "characteristics has the master dataset",
     755           3 :                      GetDescription());
     756             :         }
     757             :     }
     758             : 
     759             :     // Re-acquire the lok
     760        2037 :     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        2037 :     if (!poTLSDS)
     765             :     {
     766           4 :         CPLSetThreadLocalConfigOptions(aosTLConfigOptionsBackup.List());
     767           4 :         return nullptr;
     768             :     }
     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        2033 :     auto poDSRet = poTLSDS.get();
     773             :     {
     774        2033 :         poCache->m_oCache.insert(this, poTLSDS);
     775        2033 :         CPLAssert(!cpl::contains(poCache->m_oMapReferencedDS, this));
     776             :         poCache->m_oMapReferencedDS.insert(
     777             :             {this, GDALThreadLocalDatasetCache::
     778        2033 :                        SharedPtrDatasetThreadLocalConfigOptionsPair(
     779        4066 :                            poTLSDS, std::move(aosTLConfigOptionsBackup))});
     780             :     }
     781        2033 :     return poDSRet;
     782             : }
     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         403 : void GDALThreadSafeDataset::UnrefUnderlyingDataset(
     798             :     GDALDataset *poUnderlyingDataset) const
     799             : {
     800         403 :     GDALThreadLocalDatasetCache *poCache = tl_poCache.get();
     801         403 :     CPLAssert(poCache);
     802         806 :     std::unique_lock oLock(poCache->m_oMutex);
     803         403 :     UnrefUnderlyingDataset(poUnderlyingDataset, poCache);
     804         403 : }
     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      745218 : void GDALThreadSafeDataset::UnrefUnderlyingDataset(
     814             :     [[maybe_unused]] GDALDataset *poUnderlyingDataset,
     815             :     GDALThreadLocalDatasetCache *poCache) const
     816             : {
     817      745218 :     auto oIter = poCache->m_oMapReferencedDS.find(this);
     818      742653 :     CPLAssert(oIter != poCache->m_oMapReferencedDS.end());
     819      742988 :     CPLAssert(oIter->second.poDS.get() == poUnderlyingDataset);
     820      744771 :     CPLSetThreadLocalConfigOptions(oIter->second.aosTLConfigOptions.List());
     821      743040 :     poCache->m_oMapReferencedDS.erase(oIter);
     822      744999 : }
     823             : 
     824             : /************************************************************************/
     825             : /*                      GDALThreadSafeRasterBand()                      */
     826             : /************************************************************************/
     827             : 
     828         438 : GDALThreadSafeRasterBand::GDALThreadSafeRasterBand(
     829             :     GDALThreadSafeDataset *poTSDS, GDALDataset *poParentDS, int nBandIn,
     830         438 :     GDALRasterBand *poPrototypeBand, int nBaseBandOfMaskBand, int nOvrIdx)
     831             :     : m_poTSDS(poTSDS), m_poPrototypeBand(poPrototypeBand),
     832         438 :       m_nBaseBandOfMaskBand(nBaseBandOfMaskBand), m_nOvrIdx(nOvrIdx)
     833             : {
     834             :     // Replicates characteristics of the prototype band.
     835         438 :     poDS = poParentDS;
     836         438 :     nBand = nBandIn;
     837         438 :     eDataType = poPrototypeBand->GetRasterDataType();
     838         438 :     nRasterXSize = poPrototypeBand->GetXSize();
     839         438 :     nRasterYSize = poPrototypeBand->GetYSize();
     840         438 :     poPrototypeBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
     841             : 
     842         438 :     if (nBandIn > 0)
     843             :     {
     844             :         // For regular bands instantiates a (thread-safe) mask band and
     845             :         // as many overviews as needed.
     846             : 
     847         146 :         m_poMaskBand = std::make_unique<GDALThreadSafeRasterBand>(
     848         146 :             poTSDS, nullptr, 0, poPrototypeBand->GetMaskBand(), nBandIn,
     849         146 :             nOvrIdx);
     850         146 :         if (nOvrIdx < 0)
     851             :         {
     852         142 :             const int nOvrCount = poPrototypeBand->GetOverviewCount();
     853         146 :             for (int iOvrIdx = 0; iOvrIdx < nOvrCount; ++iOvrIdx)
     854             :             {
     855             :                 m_apoOverviews.emplace_back(
     856           4 :                     std::make_unique<GDALThreadSafeRasterBand>(
     857           0 :                         poTSDS, nullptr, nBandIn,
     858           8 :                         poPrototypeBand->GetOverview(iOvrIdx),
     859           8 :                         nBaseBandOfMaskBand, iOvrIdx));
     860             :             }
     861             :         }
     862             :     }
     863         292 :     else if (nBaseBandOfMaskBand > 0)
     864             :     {
     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         146 :         m_poMaskBand = std::make_unique<GDALThreadSafeRasterBand>(
     869         146 :             poTSDS, nullptr, 0, poPrototypeBand->GetMaskBand(),
     870         438 :             -nBaseBandOfMaskBand, nOvrIdx);
     871             :     }
     872         438 : }
     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      745915 : GDALThreadSafeRasterBand::RefUnderlyingRasterBand(bool /*bForceOpen*/) const
     886             : {
     887             :     // Get a thread-local dataset
     888      745915 :     auto poTLDS = m_poTSDS->RefUnderlyingDataset();
     889      742703 :     if (!poTLDS)
     890           4 :         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      742699 :     const int nTLSBandIdx =
     895      742699 :         m_nBaseBandOfMaskBand ? std::abs(m_nBaseBandOfMaskBand) : nBand;
     896      742699 :     auto poTLRasterBand = poTLDS->GetRasterBand(nTLSBandIdx);
     897      745593 :     if (!poTLRasterBand)
     898             :     {
     899           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     900             :                  "GDALThreadSafeRasterBand::RefUnderlyingRasterBand(): "
     901             :                  "GetRasterBand(%d) failed",
     902             :                  nTLSBandIdx);
     903           0 :         m_poTSDS->UnrefUnderlyingDataset(poTLDS);
     904           0 :         return nullptr;
     905             :     }
     906             : 
     907             :     // Get the overview level if needed.
     908      745593 :     if (m_nOvrIdx >= 0)
     909             :     {
     910       63928 :         poTLRasterBand = poTLRasterBand->GetOverview(m_nOvrIdx);
     911       63945 :         if (!poTLRasterBand)
     912             :         {
     913           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     914             :                      "GDALThreadSafeRasterBand::RefUnderlyingRasterBand(): "
     915             :                      "GetOverview(%d) failed",
     916           1 :                      m_nOvrIdx);
     917           1 :             m_poTSDS->UnrefUnderlyingDataset(poTLDS);
     918           1 :             return nullptr;
     919             :         }
     920             :     }
     921             : 
     922             :     // Get the mask band (or the mask band of the mask band) if needed.
     923      745609 :     if (m_nBaseBandOfMaskBand)
     924             :     {
     925      668489 :         poTLRasterBand = poTLRasterBand->GetMaskBand();
     926      672610 :         if (m_nBaseBandOfMaskBand < 0)
     927       24713 :             poTLRasterBand = poTLRasterBand->GetMaskBand();
     928             :     }
     929             : 
     930             :     // Check that the thread-local band characteristics are identical to the
     931             :     // ones of the prototype band.
     932      749810 :     if (m_poPrototypeBand->GetXSize() != poTLRasterBand->GetXSize() ||
     933     1483870 :         m_poPrototypeBand->GetYSize() != poTLRasterBand->GetYSize() ||
     934      740547 :         m_poPrototypeBand->GetRasterDataType() !=
     935      746429 :             poTLRasterBand->GetRasterDataType()
     936             :         // m_poPrototypeBand->GetMaskFlags() is not thread-safe
     937             :         // || m_poPrototypeBand->GetMaskFlags() != poTLRasterBand->GetMaskFlags()
     938             :     )
     939             :     {
     940           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     941             :                  "GDALThreadSafeRasterBand::RefUnderlyingRasterBand(): TLS "
     942             :                  "band has not expected characteristics");
     943           1 :         m_poTSDS->UnrefUnderlyingDataset(poTLDS);
     944           1 :         return nullptr;
     945             :     }
     946             :     int nThisBlockXSize;
     947             :     int nThisBlockYSize;
     948             :     int nTLSBlockXSize;
     949             :     int nTLSBlockYSize;
     950      739281 :     m_poPrototypeBand->GetBlockSize(&nThisBlockXSize, &nThisBlockYSize);
     951      742647 :     poTLRasterBand->GetBlockSize(&nTLSBlockXSize, &nTLSBlockYSize);
     952      740700 :     if (nThisBlockXSize != nTLSBlockXSize || nThisBlockYSize != nTLSBlockYSize)
     953             :     {
     954        1692 :         CPLError(CE_Failure, CPLE_AppDefined,
     955             :                  "GDALThreadSafeRasterBand::RefUnderlyingRasterBand(): TLS "
     956             :                  "band has not expected characteristics");
     957           1 :         m_poTSDS->UnrefUnderlyingDataset(poTLDS);
     958           1 :         return nullptr;
     959             :     }
     960             : 
     961             :     // Registers the association between the thread-local band and the
     962             :     // thread-local dataset
     963             :     {
     964             :         GDALThreadLocalDatasetCache *poCache =
     965      739008 :             GDALThreadSafeDataset::tl_poCache.get();
     966      738580 :         CPLAssert(poCache);
     967      738580 :         std::unique_lock oLock(poCache->m_oMutex);
     968      741252 :         CPLAssert(!cpl::contains(poCache->m_oMapReferencedDSFromBand,
     969             :                                  poTLRasterBand));
     970      739878 :         poCache->m_oMapReferencedDSFromBand[poTLRasterBand] = poTLDS;
     971             :     }
     972             :     // CPLDebug("GDAL", "%p->RefUnderlyingRasterBand() return %p", this, poTLRasterBand);
     973      737325 :     return poTLRasterBand;
     974             : }
     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      748147 : void GDALThreadSafeRasterBand::UnrefUnderlyingRasterBand(
     986             :     GDALRasterBand *poUnderlyingRasterBand) const
     987             : {
     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             :     {
     993             :         GDALThreadLocalDatasetCache *poCache =
     994      748147 :             GDALThreadSafeDataset::tl_poCache.get();
     995      747155 :         CPLAssert(poCache);
     996     1487980 :         std::unique_lock oLock(poCache->m_oMutex);
     997             :         auto oIter =
     998      747384 :             poCache->m_oMapReferencedDSFromBand.find(poUnderlyingRasterBand);
     999      745315 :         CPLAssert(oIter != poCache->m_oMapReferencedDSFromBand.end());
    1000             : 
    1001      745306 :         m_poTSDS->UnrefUnderlyingDataset(oIter->second, poCache);
    1002      746019 :         poCache->m_oMapReferencedDSFromBand.erase(oIter);
    1003             :     }
    1004      740259 : }
    1005             : 
    1006             : /************************************************************************/
    1007             : /*                           GetMaskBand()                              */
    1008             : /************************************************************************/
    1009             : 
    1010             : /** Implements GDALRasterBand::GetMaskBand
    1011             :  */
    1012       10001 : GDALRasterBand *GDALThreadSafeRasterBand::GetMaskBand()
    1013             : {
    1014       10001 :     return m_poMaskBand ? m_poMaskBand.get() : this;
    1015             : }
    1016             : 
    1017             : /************************************************************************/
    1018             : /*                         GetOverviewCount()                           */
    1019             : /************************************************************************/
    1020             : 
    1021             : /** Implements GDALRasterBand::GetOverviewCount
    1022             :  */
    1023        4003 : int GDALThreadSafeRasterBand::GetOverviewCount()
    1024             : {
    1025        4003 :     return static_cast<int>(m_apoOverviews.size());
    1026             : }
    1027             : 
    1028             : /************************************************************************/
    1029             : /*                           GetOverview()                              */
    1030             : /************************************************************************/
    1031             : 
    1032             : /** Implements GDALRasterBand::GetOverview
    1033             :  */
    1034        6005 : GDALRasterBand *GDALThreadSafeRasterBand::GetOverview(int nIdx)
    1035             : {
    1036        6005 :     if (nIdx < 0 || nIdx >= static_cast<int>(m_apoOverviews.size()))
    1037           2 :         return nullptr;
    1038        6003 :     return m_apoOverviews[nIdx].get();
    1039             : }
    1040             : 
    1041             : /************************************************************************/
    1042             : /*                      GetRasterSampleOverview()                       */
    1043             : /************************************************************************/
    1044             : 
    1045             : /** Implements GDALRasterBand::GetRasterSampleOverview
    1046             :  */
    1047             : GDALRasterBand *
    1048        2000 : GDALThreadSafeRasterBand::GetRasterSampleOverview(GUIntBig nDesiredSamples)
    1049             : 
    1050             : {
    1051             :     // Call the base implementation, and do not forward to proxy
    1052        2000 :     return GDALRasterBand::GetRasterSampleOverview(nDesiredSamples);
    1053             : }
    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           3 : GDALRasterAttributeTable *GDALThreadSafeRasterBand::GetDefaultRAT()
    1070             : {
    1071           6 :     std::lock_guard oGuard(m_poTSDS->m_oPrototypeDSMutex);
    1072             :     const auto poRAT =
    1073           3 :         const_cast<GDALRasterBand *>(m_poPrototypeBand)->GetDefaultRAT();
    1074           3 :     if (!poRAT)
    1075           1 :         return nullptr;
    1076             : 
    1077           2 :     if (dynamic_cast<GDALDefaultRasterAttributeTable *>(poRAT))
    1078           1 :         return poRAT;
    1079             : 
    1080           1 :     CPLError(CE_Failure, CPLE_AppDefined,
    1081             :              "GDALThreadSafeRasterBand::GetDefaultRAT() not supporting a "
    1082             :              "non-GDALDefaultRasterAttributeTable implementation");
    1083           1 :     return nullptr;
    1084             : }
    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     6792120 : bool GDALDataset::IsThreadSafe(int nScopeFlags) const
    1105             : {
    1106       27178 :     return (nOpenFlags & GDAL_OF_THREAD_SAFE) != 0 &&
    1107     6819300 :            nScopeFlags == GDAL_OF_RASTER && (nOpenFlags & GDAL_OF_RASTER) != 0;
    1108             : }
    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           4 : bool GDALDatasetIsThreadSafe(GDALDatasetH hDS, int nScopeFlags,
    1132             :                              CSLConstList papszOptions)
    1133             : {
    1134           4 :     VALIDATE_POINTER1(hDS, __func__, false);
    1135             : 
    1136           4 :     CPL_IGNORE_RET_VAL(papszOptions);
    1137             : 
    1138           4 :     return GDALDataset::FromHandle(hDS)->IsThreadSafe(nScopeFlags);
    1139             : }
    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         124 : GDALGetThreadSafeDataset(std::unique_ptr<GDALDataset> poDS, int nScopeFlags)
    1169             : {
    1170         124 :     return GDALThreadSafeDataset::Create(std::move(poDS), nScopeFlags);
    1171             : }
    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           8 : GDALDataset *GDALGetThreadSafeDataset(GDALDataset *poDS, int nScopeFlags)
    1217             : {
    1218           8 :     return GDALThreadSafeDataset::Create(poDS, nScopeFlags);
    1219             : }
    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           8 : GDALDatasetH GDALGetThreadSafeDataset(GDALDatasetH hDS, int nScopeFlags,
    1263             :                                       CSLConstList papszOptions)
    1264             : {
    1265           8 :     VALIDATE_POINTER1(hDS, __func__, nullptr);
    1266             : 
    1267           8 :     CPL_IGNORE_RET_VAL(papszOptions);
    1268           8 :     return GDALDataset::ToHandle(
    1269           8 :         GDALGetThreadSafeDataset(GDALDataset::FromHandle(hDS), nScopeFlags));
    1270             : }

Generated by: LCOV version 1.14