Coverage Report

Created: 2025-11-16 06:25

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