Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL Core
4 : * Purpose: Store cached blocks
5 : * Author: Even Rouault, <even dot rouault at spatialys dot org>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2015, Even Rouault <even dot rouault at spatialys dot org>
9 : *
10 : * Permission is hereby granted, free of charge, to any person obtaining a
11 : * copy of this software and associated documentation files (the "Software"),
12 : * to deal in the Software without restriction, including without limitation
13 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 : * and/or sell copies of the Software, and to permit persons to whom the
15 : * Software is furnished to do so, subject to the following conditions:
16 : *
17 : * The above copyright notice and this permission notice shall be included
18 : * in all copies or substantial portions of the Software.
19 : *
20 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 : * DEALINGS IN THE SOFTWARE.
27 : ****************************************************************************/
28 :
29 : #include "cpl_port.h"
30 : #include "gdal_priv.h"
31 :
32 : #include <algorithm>
33 : #include <cstddef>
34 : #include <new>
35 :
36 : #include "cpl_atomic_ops.h"
37 : #include "cpl_error.h"
38 : #include "cpl_multiproc.h"
39 :
40 : //! @cond Doxygen_Suppress
41 :
42 : #ifdef DEBUG_VERBOSE_ABBC
43 : static int nAllBandsKeptAlivedBlocks = 0;
44 : #endif
45 :
46 : /************************************************************************/
47 : /* GDALArrayBandBlockCache() */
48 : /************************************************************************/
49 :
50 29059 : GDALAbstractBandBlockCache::GDALAbstractBandBlockCache(GDALRasterBand *poBandIn)
51 87177 : : hSpinLock(CPLCreateLock(LOCK_SPIN)), hCond(CPLCreateCond()),
52 29059 : hCondMutex(CPLCreateMutex()), poBand(poBandIn)
53 : {
54 29059 : if (hCondMutex)
55 29059 : CPLReleaseMutex(hCondMutex);
56 29059 : }
57 :
58 : /************************************************************************/
59 : /* ~GDALAbstractBandBlockCache() */
60 : /************************************************************************/
61 :
62 58119 : GDALAbstractBandBlockCache::~GDALAbstractBandBlockCache()
63 : {
64 29059 : CPLAssert(nKeepAliveCounter == 0);
65 29059 : FreeDanglingBlocks();
66 29059 : if (hSpinLock)
67 29058 : CPLDestroyLock(hSpinLock);
68 29060 : if (hCondMutex)
69 29059 : CPLDestroyMutex(hCondMutex);
70 29060 : if (hCond)
71 29059 : CPLDestroyCond(hCond);
72 29060 : }
73 :
74 : /************************************************************************/
75 : /* UnreferenceBlockBase() */
76 : /* */
77 : /* This is called by GDALRasterBlock::Internalize() and */
78 : /* FlushCacheBlock() when they remove a block from the linked list */
79 : /* but haven't yet flushed it to disk or recovered its pData member*/
80 : /* We must be aware that they are blocks in that state, since the */
81 : /* band must be kept alive while AddBlockToFreeList() hasn't been */
82 : /* called (in case a block is being flushed while the final */
83 : /* FlushCache() of the main thread of the dataset is running). */
84 : /************************************************************************/
85 :
86 29608 : void GDALAbstractBandBlockCache::UnreferenceBlockBase()
87 : {
88 29608 : CPLAtomicInc(&nKeepAliveCounter);
89 29608 : }
90 :
91 : /************************************************************************/
92 : /* AddBlockToFreeList() */
93 : /* */
94 : /* This is called by GDALRasterBlock::Internalize() and */
95 : /* FlushCacheBlock() after they have been finished with a block. */
96 : /************************************************************************/
97 :
98 29608 : void GDALAbstractBandBlockCache::AddBlockToFreeList(GDALRasterBlock *poBlock)
99 : {
100 29608 : CPLAssert(poBlock->poPrevious == nullptr);
101 29608 : CPLAssert(poBlock->poNext == nullptr);
102 : {
103 : #ifdef DEBUG_VERBOSE_ABBC
104 : CPLAtomicInc(&nAllBandsKeptAlivedBlocks);
105 : fprintf(/*ok*/ stderr,
106 : "AddBlockToFreeList(): nAllBandsKeptAlivedBlocks=%d\n",
107 : nAllBandsKeptAlivedBlocks);
108 : #endif
109 29608 : CPLLockHolderOptionalLockD(hSpinLock);
110 29608 : poBlock->poNext = psListBlocksToFree;
111 29608 : psListBlocksToFree = poBlock;
112 : }
113 :
114 : // If no more blocks in transient state, then warn
115 : // WaitCompletionPendingTasks()
116 29608 : CPLAcquireMutex(hCondMutex, 1000);
117 29608 : if (CPLAtomicDec(&nKeepAliveCounter) == 0)
118 : {
119 29562 : CPLCondSignal(hCond);
120 : }
121 29607 : CPLReleaseMutex(hCondMutex);
122 29608 : }
123 :
124 : /************************************************************************/
125 : /* WaitCompletionPendingTasks() */
126 : /************************************************************************/
127 :
128 582303 : void GDALAbstractBandBlockCache::WaitCompletionPendingTasks()
129 : {
130 : #ifdef DEBUG_VERBOSE
131 : CPLDebug("GDAL", "WaitCompletionPendingTasks()");
132 : #endif
133 :
134 582303 : CPLAcquireMutex(hCondMutex, 1000);
135 582305 : while (nKeepAliveCounter != 0)
136 : {
137 2 : CPLDebug("GDAL", "Waiting for other thread to finish working with our "
138 : "blocks");
139 2 : CPLCondWait(hCond, hCondMutex);
140 : }
141 582303 : CPLReleaseMutex(hCondMutex);
142 582304 : }
143 :
144 : /************************************************************************/
145 : /* FreeDanglingBlocks() */
146 : /************************************************************************/
147 :
148 3164340 : void GDALAbstractBandBlockCache::FreeDanglingBlocks()
149 : {
150 : GDALRasterBlock *poList;
151 : {
152 3164340 : CPLLockHolderOptionalLockD(hSpinLock);
153 3164350 : poList = psListBlocksToFree;
154 3164350 : psListBlocksToFree = nullptr;
155 : }
156 3182550 : while (poList)
157 : {
158 : #ifdef DEBUG_VERBOSE_ABBC
159 : CPLAtomicDec(&nAllBandsKeptAlivedBlocks);
160 : fprintf(/*ok*/ stderr,
161 : "FreeDanglingBlocks(): nAllBandsKeptAlivedBlocks=%d\n",
162 : nAllBandsKeptAlivedBlocks);
163 : #endif
164 18206 : GDALRasterBlock *poNext = poList->poNext;
165 18206 : poList->poNext = nullptr;
166 18206 : delete poList;
167 18202 : poList = poNext;
168 : }
169 3164340 : }
170 :
171 : /************************************************************************/
172 : /* CreateBlock() */
173 : /************************************************************************/
174 :
175 2967590 : GDALRasterBlock *GDALAbstractBandBlockCache::CreateBlock(int nXBlockOff,
176 : int nYBlockOff)
177 : {
178 : GDALRasterBlock *poBlock;
179 : {
180 5935190 : CPLLockHolderOptionalLockD(hSpinLock);
181 2967600 : poBlock = psListBlocksToFree;
182 2967600 : if (poBlock)
183 : {
184 : #ifdef DEBUG_VERBOSE_ABBC
185 : CPLAtomicDec(&nAllBandsKeptAlivedBlocks);
186 : fprintf(/*ok*/ stderr,
187 : "CreateBlock(): nAllBandsKeptAlivedBlocks=%d\n",
188 : nAllBandsKeptAlivedBlocks);
189 : #endif
190 11402 : psListBlocksToFree = poBlock->poNext;
191 : }
192 : }
193 2967600 : if (poBlock)
194 11402 : poBlock->RecycleFor(nXBlockOff, nYBlockOff);
195 : else
196 2956190 : poBlock =
197 2956190 : new (std::nothrow) GDALRasterBlock(poBand, nXBlockOff, nYBlockOff);
198 2967590 : return poBlock;
199 : }
200 :
201 : /************************************************************************/
202 : /* IncDirtyBlocks() */
203 : /************************************************************************/
204 :
205 : /**
206 : * \brief Increment/decrement the number of dirty blocks
207 : */
208 :
209 488711 : void GDALAbstractBandBlockCache::IncDirtyBlocks(int nInc)
210 : {
211 488711 : CPLAtomicAdd(&m_nDirtyBlocks, nInc);
212 488711 : }
213 :
214 : /************************************************************************/
215 : /* StartDirtyBlockFlushingLog() */
216 : /************************************************************************/
217 :
218 167689 : void GDALAbstractBandBlockCache::StartDirtyBlockFlushingLog()
219 : {
220 167689 : m_nInitialDirtyBlocksInFlushCache = 0;
221 167689 : if (m_nDirtyBlocks > 0 && CPLIsDefaultErrorHandlerAndCatchDebug())
222 : {
223 1775 : const char *pszDebug = CPLGetConfigOption("CPL_DEBUG", nullptr);
224 1776 : if (pszDebug && (EQUAL(pszDebug, "ON") || EQUAL(pszDebug, "GDAL")) &&
225 1 : CPLGetConfigOption("GDAL_REPORT_DIRTY_BLOCK_FLUSHING", nullptr) ==
226 : nullptr)
227 : {
228 1 : m_nInitialDirtyBlocksInFlushCache = m_nDirtyBlocks;
229 1 : m_nLastTick = -1;
230 : }
231 : }
232 167689 : }
233 :
234 : /************************************************************************/
235 : /* UpdateDirtyBlockFlushingLog() */
236 : /************************************************************************/
237 :
238 162819 : void GDALAbstractBandBlockCache::UpdateDirtyBlockFlushingLog()
239 : {
240 : // Poor man progress report for console applications
241 162819 : if (m_nInitialDirtyBlocksInFlushCache)
242 : {
243 1 : const auto nRemainingDirtyBlocks = m_nDirtyBlocks;
244 1 : const auto nFlushedBlocks =
245 1 : m_nInitialDirtyBlocksInFlushCache - nRemainingDirtyBlocks + 1;
246 1 : const double dfComplete =
247 1 : double(nFlushedBlocks) / m_nInitialDirtyBlocksInFlushCache;
248 : const int nThisTick =
249 1 : std::min(40, std::max(0, static_cast<int>(dfComplete * 40.0)));
250 1 : if (nThisTick > m_nLastTick)
251 : {
252 1 : if (m_nLastTick < 0)
253 : {
254 1 : fprintf(stderr, "GDAL: Flushing dirty blocks: "); /*ok*/
255 1 : fflush(stderr); /*ok*/
256 : }
257 42 : while (nThisTick > m_nLastTick)
258 : {
259 41 : ++m_nLastTick;
260 41 : if (m_nLastTick % 4 == 0)
261 11 : fprintf(stderr, "%d", (m_nLastTick / 4) * 10); /*ok*/
262 : else
263 30 : fprintf(stderr, "."); /*ok*/
264 : }
265 :
266 1 : if (nThisTick == 40)
267 1 : fprintf(stderr, " - done.\n"); /*ok*/
268 : else
269 0 : fflush(stderr); /*ok*/
270 : }
271 : }
272 162819 : }
273 :
274 : /************************************************************************/
275 : /* EndDirtyBlockFlushingLog() */
276 : /************************************************************************/
277 :
278 167690 : void GDALAbstractBandBlockCache::EndDirtyBlockFlushingLog()
279 : {
280 167690 : m_nInitialDirtyBlocksInFlushCache = 0;
281 167690 : m_nLastTick = -1;
282 167690 : }
283 :
284 : //! @endcond
|