LCOV - code coverage report
Current view: top level - gcore - gdalthreadsafedataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 306 324 94.4 %
Date: 2025-12-13 23:48:27 Functions: 43 43 100.0 %

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

Generated by: LCOV version 1.14