1 /******************************************************************************
2  *
3  * Project:  GDAL Core
4  * Purpose:  Store cached blocks in a hash set
5  * Author:   Even Rouault, <even dot rouault at spatialys dot org>
6  *
7  ******************************************************************************
8  * Copyright (c) 2010, Tamas Szekeres
9  * Copyright (c) 2015, Even Rouault <even dot rouault at spatialys dot org>
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included
19  * in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  ****************************************************************************/
29 
30 #include "cpl_port.h"
31 #include "gdal_priv.h"
32 
33 #include <cstddef>
34 #include <algorithm>
35 #include <set>
36 #include <vector>
37 
38 #include "cpl_config.h"
39 #include "cpl_error.h"
40 #include "cpl_multiproc.h"
41 
42 CPL_CVSID("$Id: gdalhashsetbandblockcache.cpp 2cef454ab06723a32379690b46581a40eb2e5af2 2019-11-13 16:36:03 +0100 Even Rouault $")
43 
44 //! @cond Doxygen_Suppress
45 
46 /* ******************************************************************** */
47 /*                        GDALHashSetBandBlockCache                     */
48 /* ******************************************************************** */
49 
50 class GDALHashSetBandBlockCache final : public GDALAbstractBandBlockCache
51 {
52     struct BlockComparator
53     {
54         // Do not change this comparator, because this order is assumed by
55         // tests like tiff_write_133 for flushing from top to bottom, left
56         // to right.
operator ()GDALHashSetBandBlockCache::BlockComparator57         bool operator() (const GDALRasterBlock* const& lhs,
58                          const GDALRasterBlock* const& rhs) const
59         {
60             if( lhs->GetYOff() < rhs->GetYOff() )
61                 return true;
62             if( lhs->GetYOff() > rhs->GetYOff() )
63                 return false;
64             return lhs->GetXOff() < rhs->GetXOff();
65         }
66     };
67 
68     std::set<GDALRasterBlock*, BlockComparator> m_oSet{};
69     CPLLock        *hLock = nullptr;
70 
71     CPL_DISALLOW_COPY_ASSIGN(GDALHashSetBandBlockCache)
72 
73   public:
74     explicit GDALHashSetBandBlockCache( GDALRasterBand* poBand );
75     ~GDALHashSetBandBlockCache() override;
76 
77     bool Init() override;
78     bool IsInitOK() override;
79     CPLErr FlushCache() override;
80     CPLErr AdoptBlock( GDALRasterBlock * ) override;
81     GDALRasterBlock *TryGetLockedBlockRef( int nXBlockOff,
82                                            int nYBlockYOff ) override;
83     CPLErr UnreferenceBlock( GDALRasterBlock* poBlock ) override;
84     CPLErr FlushBlock( int nXBlockOff, int nYBlockOff,
85                        int bWriteDirtyBlock ) override;
86 };
87 
88 /************************************************************************/
89 /*                     GDALHashSetBandBlockCacheCreate()                */
90 /************************************************************************/
91 
GDALHashSetBandBlockCacheCreate(GDALRasterBand * poBand)92 GDALAbstractBandBlockCache* GDALHashSetBandBlockCacheCreate(
93     GDALRasterBand* poBand )
94 {
95     return new GDALHashSetBandBlockCache(poBand);
96 }
97 
98 /************************************************************************/
99 /*                       GDALHashSetBandBlockCache()                    */
100 /************************************************************************/
101 
GDALHashSetBandBlockCache(GDALRasterBand * poBandIn)102 GDALHashSetBandBlockCache::GDALHashSetBandBlockCache(
103     GDALRasterBand* poBandIn ) :
104     GDALAbstractBandBlockCache(poBandIn),
105 
106     hLock(CPLCreateLock(LOCK_ADAPTIVE_MUTEX))
107 {}
108 
109 /************************************************************************/
110 /*                      ~GDALHashSetBandBlockCache()                    */
111 /************************************************************************/
112 
~GDALHashSetBandBlockCache()113 GDALHashSetBandBlockCache::~GDALHashSetBandBlockCache()
114 {
115     GDALHashSetBandBlockCache::FlushCache();
116     CPLDestroyLock(hLock);
117 }
118 
119 /************************************************************************/
120 /*                                  Init()                              */
121 /************************************************************************/
122 
Init()123 bool GDALHashSetBandBlockCache::Init()
124 {
125     return true;
126 }
127 
128 /************************************************************************/
129 /*                             IsInitOK()                               */
130 /************************************************************************/
131 
IsInitOK()132 bool GDALHashSetBandBlockCache::IsInitOK()
133 {
134     return true;
135 }
136 
137 /************************************************************************/
138 /*                            AdoptBlock()                              */
139 /************************************************************************/
140 
AdoptBlock(GDALRasterBlock * poBlock)141 CPLErr GDALHashSetBandBlockCache::AdoptBlock( GDALRasterBlock * poBlock )
142 
143 {
144     FreeDanglingBlocks();
145 
146     CPLLockHolderOptionalLockD( hLock );
147     m_oSet.insert(poBlock);
148 
149     return CE_None;
150 }
151 
152 /************************************************************************/
153 /*                            FlushCache()                              */
154 /************************************************************************/
155 
FlushCache()156 CPLErr GDALHashSetBandBlockCache::FlushCache()
157 {
158     FreeDanglingBlocks();
159 
160     CPLErr eGlobalErr = poBand->eFlushBlockErr;
161 
162     std::set<GDALRasterBlock*, BlockComparator> oOldSet;
163     {
164         CPLLockHolderOptionalLockD( hLock );
165         oOldSet = std::move(m_oSet);
166     }
167 
168     StartDirtyBlockFlushingLog();
169     for( auto& poBlock: oOldSet )
170     {
171         if( poBlock->DropLockForRemovalFromStorage() )
172         {
173             CPLErr eErr = CE_None;
174 
175             if( eGlobalErr == CE_None && poBlock->GetDirty() )
176             {
177                 UpdateDirtyBlockFlushingLog();
178                 eErr = poBlock->Write();
179             }
180 
181             delete poBlock;
182 
183             if( eErr != CE_None )
184                 eGlobalErr = eErr;
185         }
186     }
187     EndDirtyBlockFlushingLog();
188 
189     WaitCompletionPendingTasks();
190 
191     return( eGlobalErr );
192 }
193 
194 /************************************************************************/
195 /*                        UnreferenceBlock()                            */
196 /************************************************************************/
197 
UnreferenceBlock(GDALRasterBlock * poBlock)198 CPLErr GDALHashSetBandBlockCache::UnreferenceBlock( GDALRasterBlock* poBlock )
199 {
200     UnreferenceBlockBase();
201 
202     CPLLockHolderOptionalLockD( hLock );
203     m_oSet.erase(poBlock);
204     return CE_None;
205 }
206 
207 /************************************************************************/
208 /*                            FlushBlock()                              */
209 /************************************************************************/
210 
FlushBlock(int nXBlockOff,int nYBlockOff,int bWriteDirtyBlock)211 CPLErr GDALHashSetBandBlockCache::FlushBlock( int nXBlockOff, int nYBlockOff,
212                                               int bWriteDirtyBlock )
213 
214 {
215     GDALRasterBlock oBlockForLookup(nXBlockOff, nYBlockOff);
216     GDALRasterBlock* poBlock = nullptr;
217     {
218         CPLLockHolderOptionalLockD( hLock );
219         auto oIter = m_oSet.find(&oBlockForLookup);
220         if( oIter == m_oSet.end() )
221             return CE_None;
222         poBlock = *oIter;
223         m_oSet.erase(oIter);
224     }
225 
226     if( !poBlock->DropLockForRemovalFromStorage() )
227         return CE_None;
228 
229     CPLErr eErr = CE_None;
230 
231     if( bWriteDirtyBlock && poBlock->GetDirty() )
232         eErr = poBlock->Write();
233 
234     delete poBlock;
235 
236     return eErr;
237 }
238 
239 /************************************************************************/
240 /*                        TryGetLockedBlockRef()                        */
241 /************************************************************************/
242 
TryGetLockedBlockRef(int nXBlockOff,int nYBlockOff)243 GDALRasterBlock *GDALHashSetBandBlockCache::TryGetLockedBlockRef(
244     int nXBlockOff, int nYBlockOff )
245 
246 {
247     GDALRasterBlock oBlockForLookup(nXBlockOff, nYBlockOff);
248     GDALRasterBlock* poBlock;
249     {
250         CPLLockHolderOptionalLockD( hLock );
251         auto oIter = m_oSet.find(&oBlockForLookup);
252         if( oIter == m_oSet.end() )
253             return nullptr;
254         poBlock = *oIter;
255     }
256     if( !poBlock->TakeLock()  )
257         return nullptr;
258     return poBlock;
259 }
260 
261 //! @endcond
262