Coverage Report

Created: 2025-06-13 06:18

/src/gdal/gcore/gdalabstractbandblockcache.cpp
Line
Count
Source (jump to first uncovered line)
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
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "cpl_port.h"
14
#include "gdal_priv.h"
15
16
#include <algorithm>
17
#include <cstddef>
18
#include <new>
19
20
#include "cpl_atomic_ops.h"
21
#include "cpl_error.h"
22
#include "cpl_multiproc.h"
23
24
//! @cond Doxygen_Suppress
25
26
#ifdef DEBUG_VERBOSE_ABBC
27
static int nAllBandsKeptAlivedBlocks = 0;
28
#endif
29
30
/************************************************************************/
31
/*                       GDALArrayBandBlockCache()                      */
32
/************************************************************************/
33
34
GDALAbstractBandBlockCache::GDALAbstractBandBlockCache(GDALRasterBand *poBandIn)
35
0
    : hSpinLock(CPLCreateLock(LOCK_SPIN)), hCond(CPLCreateCond()),
36
0
      hCondMutex(CPLCreateMutex()), poBand(poBandIn)
37
0
{
38
0
    if (hCondMutex)
39
0
        CPLReleaseMutex(hCondMutex);
40
0
}
41
42
/************************************************************************/
43
/*                      ~GDALAbstractBandBlockCache()                   */
44
/************************************************************************/
45
46
GDALAbstractBandBlockCache::~GDALAbstractBandBlockCache()
47
0
{
48
0
    CPLAssert(nKeepAliveCounter == 0);
49
0
    FreeDanglingBlocks();
50
0
    if (hSpinLock)
51
0
        CPLDestroyLock(hSpinLock);
52
0
    if (hCondMutex)
53
0
        CPLDestroyMutex(hCondMutex);
54
0
    if (hCond)
55
0
        CPLDestroyCond(hCond);
56
0
}
57
58
/************************************************************************/
59
/*                            UnreferenceBlockBase()                    */
60
/*                                                                      */
61
/*      This is called by GDALRasterBlock::Internalize() and            */
62
/*      FlushCacheBlock() when they remove a block from the linked list */
63
/*      but haven't yet flushed it to disk or recovered its pData member*/
64
/*      We must be aware that they are blocks in that state, since the  */
65
/*      band must be kept alive while AddBlockToFreeList() hasn't been  */
66
/*      called (in case a block is being flushed while the final        */
67
/*      FlushCache() of the main thread of the dataset is running).     */
68
/************************************************************************/
69
70
void GDALAbstractBandBlockCache::UnreferenceBlockBase()
71
0
{
72
0
    CPLAtomicInc(&nKeepAliveCounter);
73
0
}
74
75
/************************************************************************/
76
/*                          AddBlockToFreeList()                        */
77
/*                                                                      */
78
/*      This is called by GDALRasterBlock::Internalize() and            */
79
/*      FlushCacheBlock() after they have been finished with a block.   */
80
/************************************************************************/
81
82
void GDALAbstractBandBlockCache::AddBlockToFreeList(GDALRasterBlock *poBlock)
83
0
{
84
0
    CPLAssert(poBlock->poPrevious == nullptr);
85
0
    CPLAssert(poBlock->poNext == nullptr);
86
0
    {
87
#ifdef DEBUG_VERBOSE_ABBC
88
        CPLAtomicInc(&nAllBandsKeptAlivedBlocks);
89
        fprintf(/*ok*/ stderr,
90
                "AddBlockToFreeList(): nAllBandsKeptAlivedBlocks=%d\n",
91
                nAllBandsKeptAlivedBlocks);
92
#endif
93
0
        CPLLockHolderOptionalLockD(hSpinLock);
94
0
        poBlock->poNext = psListBlocksToFree;
95
0
        psListBlocksToFree = poBlock;
96
0
    }
97
98
    // If no more blocks in transient state, then warn
99
    // WaitCompletionPendingTasks()
100
0
    CPLAcquireMutex(hCondMutex, 1000);
101
0
    if (CPLAtomicDec(&nKeepAliveCounter) == 0)
102
0
    {
103
0
        CPLCondSignal(hCond);
104
0
    }
105
0
    CPLReleaseMutex(hCondMutex);
106
0
}
107
108
/************************************************************************/
109
/*                      WaitCompletionPendingTasks()                    */
110
/************************************************************************/
111
112
void GDALAbstractBandBlockCache::WaitCompletionPendingTasks()
113
0
{
114
#ifdef DEBUG_VERBOSE
115
    CPLDebug("GDAL", "WaitCompletionPendingTasks()");
116
#endif
117
118
0
    CPLAcquireMutex(hCondMutex, 1000);
119
0
    while (nKeepAliveCounter != 0)
120
0
    {
121
0
        CPLDebug("GDAL", "Waiting for other thread to finish working with our "
122
0
                         "blocks");
123
0
        CPLCondWait(hCond, hCondMutex);
124
0
    }
125
0
    CPLReleaseMutex(hCondMutex);
126
0
}
127
128
/************************************************************************/
129
/*                           FreeDanglingBlocks()                       */
130
/************************************************************************/
131
132
void GDALAbstractBandBlockCache::FreeDanglingBlocks()
133
0
{
134
0
    GDALRasterBlock *poList;
135
0
    {
136
0
        CPLLockHolderOptionalLockD(hSpinLock);
137
0
        poList = psListBlocksToFree;
138
0
        psListBlocksToFree = nullptr;
139
0
    }
140
0
    while (poList)
141
0
    {
142
#ifdef DEBUG_VERBOSE_ABBC
143
        CPLAtomicDec(&nAllBandsKeptAlivedBlocks);
144
        fprintf(/*ok*/ stderr,
145
                "FreeDanglingBlocks(): nAllBandsKeptAlivedBlocks=%d\n",
146
                nAllBandsKeptAlivedBlocks);
147
#endif
148
0
        GDALRasterBlock *poNext = poList->poNext;
149
0
        poList->poNext = nullptr;
150
0
        delete poList;
151
0
        poList = poNext;
152
0
    }
153
0
}
154
155
/************************************************************************/
156
/*                            CreateBlock()                             */
157
/************************************************************************/
158
159
GDALRasterBlock *GDALAbstractBandBlockCache::CreateBlock(int nXBlockOff,
160
                                                         int nYBlockOff)
161
0
{
162
0
    GDALRasterBlock *poBlock;
163
0
    {
164
0
        CPLLockHolderOptionalLockD(hSpinLock);
165
0
        poBlock = psListBlocksToFree;
166
0
        if (poBlock)
167
0
        {
168
#ifdef DEBUG_VERBOSE_ABBC
169
            CPLAtomicDec(&nAllBandsKeptAlivedBlocks);
170
            fprintf(/*ok*/ stderr,
171
                    "CreateBlock(): nAllBandsKeptAlivedBlocks=%d\n",
172
                    nAllBandsKeptAlivedBlocks);
173
#endif
174
0
            psListBlocksToFree = poBlock->poNext;
175
0
        }
176
0
    }
177
0
    if (poBlock)
178
0
        poBlock->RecycleFor(nXBlockOff, nYBlockOff);
179
0
    else
180
0
        poBlock =
181
0
            new (std::nothrow) GDALRasterBlock(poBand, nXBlockOff, nYBlockOff);
182
0
    return poBlock;
183
0
}
184
185
/************************************************************************/
186
/*                         IncDirtyBlocks()                             */
187
/************************************************************************/
188
189
/**
190
 * \brief Increment/decrement the number of dirty blocks
191
 */
192
193
void GDALAbstractBandBlockCache::IncDirtyBlocks(int nInc)
194
0
{
195
0
    CPLAtomicAdd(&m_nDirtyBlocks, nInc);
196
0
}
197
198
/************************************************************************/
199
/*                      StartDirtyBlockFlushingLog()                    */
200
/************************************************************************/
201
202
void GDALAbstractBandBlockCache::StartDirtyBlockFlushingLog()
203
0
{
204
0
    m_nInitialDirtyBlocksInFlushCache = 0;
205
0
    if (m_nDirtyBlocks > 0 && CPLIsDefaultErrorHandlerAndCatchDebug())
206
0
    {
207
0
        if (CPLIsDebugEnabled() &&
208
0
            CPLGetConfigOption("GDAL_REPORT_DIRTY_BLOCK_FLUSHING", nullptr) ==
209
0
                nullptr)
210
0
        {
211
0
            m_nInitialDirtyBlocksInFlushCache = m_nDirtyBlocks;
212
0
            m_nLastTick = -1;
213
0
        }
214
0
    }
215
0
}
216
217
/************************************************************************/
218
/*                      UpdateDirtyBlockFlushingLog()                   */
219
/************************************************************************/
220
221
void GDALAbstractBandBlockCache::UpdateDirtyBlockFlushingLog()
222
0
{
223
    // Poor man progress report for console applications
224
0
    if (m_nInitialDirtyBlocksInFlushCache)
225
0
    {
226
0
        const auto nRemainingDirtyBlocks = m_nDirtyBlocks;
227
0
        const auto nFlushedBlocks =
228
0
            m_nInitialDirtyBlocksInFlushCache - nRemainingDirtyBlocks + 1;
229
0
        const double dfComplete =
230
0
            double(nFlushedBlocks) / m_nInitialDirtyBlocksInFlushCache;
231
0
        const int nThisTick =
232
0
            std::min(40, std::max(0, static_cast<int>(dfComplete * 40.0)));
233
0
        if (nThisTick > m_nLastTick)
234
0
        {
235
0
            if (m_nLastTick < 0)
236
0
            {
237
0
                fprintf(stderr, "GDAL: Flushing dirty blocks: "); /*ok*/
238
0
                fflush(stderr);                                   /*ok*/
239
0
            }
240
0
            while (nThisTick > m_nLastTick)
241
0
            {
242
0
                ++m_nLastTick;
243
0
                if (m_nLastTick % 4 == 0)
244
0
                    fprintf(stderr, "%d", (m_nLastTick / 4) * 10); /*ok*/
245
0
                else
246
0
                    fprintf(stderr, "."); /*ok*/
247
0
            }
248
249
0
            if (nThisTick == 40)
250
0
                fprintf(stderr, " - done.\n"); /*ok*/
251
0
            else
252
0
                fflush(stderr); /*ok*/
253
0
        }
254
0
    }
255
0
}
256
257
/************************************************************************/
258
/*                       EndDirtyBlockFlushingLog()                     */
259
/************************************************************************/
260
261
void GDALAbstractBandBlockCache::EndDirtyBlockFlushingLog()
262
0
{
263
0
    m_nInitialDirtyBlocksInFlushCache = 0;
264
0
    m_nLastTick = -1;
265
0
}
266
267
//! @endcond