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

Generated by: LCOV version 1.14