diff --git a/autotest/cpp/test_gdal.cpp b/autotest/cpp/test_gdal.cpp index 8c8398294c2e..80247e00fcd9 100644 --- a/autotest/cpp/test_gdal.cpp +++ b/autotest/cpp/test_gdal.cpp @@ -3491,4 +3491,48 @@ TEST_F(test_gdal, open_shared_open_options) } } +// Test DropCache() to check that no data is saved on disk +TEST_F(test_gdal, drop_cache) +{ + CPLErrorReset(); + { + GDALDriverManager *gdalDriverManager = GetGDALDriverManager(); + GDALDriver *enviDriver = + !gdalDriverManager ? nullptr + : gdalDriverManager->GetDriverByName("ENVI"); + const char *enviOptions[] = {"SUFFIX=ADD", "INTERLEAVE=BIL", nullptr}; + + const char *filename = GCORE_DATA_DIR "test_drop_cache.bil"; + + GDALDataset *poDS1 = + !enviDriver + ? nullptr + : enviDriver->Create(filename, 1, 1, 1, + GDALDataType::GDT_Float32, enviOptions); + if (poDS1) + { + GDALRasterBand *rasterBand = + !poDS1 ? nullptr : poDS1->GetRasterBand(1); + if (rasterBand) + rasterBand->Fill(1); + poDS1->DropCache(); + GDALClose(poDS1); + poDS1 = nullptr; + } + + GDALDataset *poDS2 = + GDALDataset::Open(filename, GDAL_OF_SHARED, nullptr, nullptr); + + if (poDS2) + { + GDALRasterBand *rasterBand = + !poDS2 ? nullptr : poDS2->GetRasterBand(1); + EXPECT_EQ(GDALChecksumImage(rasterBand, 0, 0, 1, 1), 0); + poDS2->MarkSuppressOnClose(); + GDALClose(poDS2); + poDS2 = nullptr; + } + } +} + } // namespace diff --git a/gcore/gdal.h b/gcore/gdal.h index 8d3725752cc4..8ce27a5a2431 100644 --- a/gcore/gdal.h +++ b/gcore/gdal.h @@ -1191,6 +1191,7 @@ CPLErr CPL_DLL CPL_STDCALL GDALBuildOverviewsEx( void CPL_DLL CPL_STDCALL GDALGetOpenDatasets(GDALDatasetH **hDS, int *pnCount); int CPL_DLL CPL_STDCALL GDALGetAccess(GDALDatasetH hDS); CPLErr CPL_DLL CPL_STDCALL GDALFlushCache(GDALDatasetH hDS); +CPLErr CPL_DLL CPL_STDCALL GDALDropCache(GDALDatasetH hDS); CPLErr CPL_DLL CPL_STDCALL GDALCreateDatasetMaskBand(GDALDatasetH hDS, int nFlags); @@ -1545,6 +1546,7 @@ CPLErr CPL_DLL CPL_STDCALL GDALComputeRasterMinMax(GDALRasterBandH hBand, int bApproxOK, double adfMinMax[2]); CPLErr CPL_DLL CPL_STDCALL GDALFlushRasterCache(GDALRasterBandH hBand); +CPLErr CPL_DLL CPL_STDCALL GDALDropRasterCache(GDALRasterBandH hBand); CPLErr CPL_DLL CPL_STDCALL GDALGetRasterHistogram( GDALRasterBandH hBand, double dfMin, double dfMax, int nBuckets, int *panHistogram, int bIncludeOutOfRange, int bApproxOK, diff --git a/gcore/gdal_priv.h b/gcore/gdal_priv.h index 78d4f7e1d734..238141e8cc8a 100644 --- a/gcore/gdal_priv.h +++ b/gcore/gdal_priv.h @@ -537,6 +537,7 @@ class CPL_DLL GDALDataset : public GDALMajorObject Bands GetBands(); virtual CPLErr FlushCache(bool bAtClosing = false); + virtual CPLErr DropCache(); virtual GIntBig GetEstimatedRAMUsage(); @@ -1188,7 +1189,7 @@ class GDALAbstractBandBlockCache int m_nInitialDirtyBlocksInFlushCache = 0; int m_nLastTick = -1; - bool m_bWriteDirtyBlocks = true; + size_t m_nWriteDirtyBlocksDisabled = 0; void FreeDanglingBlocks(); void UnreferenceBlockBase(); @@ -1205,9 +1206,13 @@ class GDALAbstractBandBlockCache void AddBlockToFreeList(GDALRasterBlock *); void IncDirtyBlocks(int nInc); void WaitCompletionPendingTasks(); + void EnableDirtyBlockWriting() + { + --m_nWriteDirtyBlocksDisabled; + } void DisableDirtyBlockWriting() { - m_bWriteDirtyBlocks = false; + ++m_nWriteDirtyBlocksDisabled; } bool HasDirtyBlocks() const { @@ -1412,6 +1417,7 @@ class CPL_DLL GDALRasterBand : public GDALMajorObject // New OpengIS CV_SampleDimension stuff. virtual CPLErr FlushCache(bool bAtClosing = false); + virtual CPLErr DropCache(); virtual char **GetCategoryNames(); virtual double GetNoDataValue(int *pbSuccess = nullptr); virtual int64_t GetNoDataValueAsInt64(int *pbSuccess = nullptr); diff --git a/gcore/gdalarraybandblockcache.cpp b/gcore/gdalarraybandblockcache.cpp index 94f3442cbd44..94f5d490bdae 100644 --- a/gcore/gdalarraybandblockcache.cpp +++ b/gcore/gdalarraybandblockcache.cpp @@ -442,7 +442,7 @@ CPLErr GDALArrayBandBlockCache::FlushBlock(int nXBlockOff, int nYBlockOff, CPLErr eErr = CE_None; - if (m_bWriteDirtyBlocks && bWriteDirtyBlock && poBlock->GetDirty()) + if (!m_nWriteDirtyBlocksDisabled && bWriteDirtyBlock && poBlock->GetDirty()) { UpdateDirtyBlockFlushingLog(); diff --git a/gcore/gdaldataset.cpp b/gcore/gdaldataset.cpp index 888ae00d2451..8131ecbc3bbc 100644 --- a/gcore/gdaldataset.cpp +++ b/gcore/gdaldataset.cpp @@ -613,6 +613,59 @@ CPLErr CPL_STDCALL GDALFlushCache(GDALDatasetH hDS) return GDALDataset::FromHandle(hDS)->FlushCache(false); } +/************************************************************************/ +/* DropCache() */ +/************************************************************************/ + +/** +* \brief Drop all write cached data +* +* This method is the same as the C function GDALDropCache(). +* +* @return CE_None in case of success +* @since 3.9 +*/ + +CPLErr GDALDataset::DropCache() + +{ + CPLErr eErr = CE_None; + + if (papoBands) + { + for (int i = 0; i < nBands; ++i) + { + if (papoBands[i]) + { + if (papoBands[i]->DropCache() != CE_None) + eErr = CE_Failure; + } + } + } + + return eErr; +} + +/************************************************************************/ +/* GDALDropCache() */ +/************************************************************************/ + +/** +* \brief Drop all write cached data +* +* @see GDALDataset::DropCache(). +* @return CE_None in case of success +* @since 3.9 +*/ + +CPLErr CPL_STDCALL GDALDropCache(GDALDatasetH hDS) + +{ + VALIDATE_POINTER1(hDS, "GDALDropCache", CE_Failure); + + return GDALDataset::FromHandle(hDS)->DropCache(); +} + /************************************************************************/ /* GetEstimatedRAMUsage() */ /************************************************************************/ diff --git a/gcore/gdalhashsetbandblockcache.cpp b/gcore/gdalhashsetbandblockcache.cpp index 8e26c0409217..6216eff40789 100644 --- a/gcore/gdalhashsetbandblockcache.cpp +++ b/gcore/gdalhashsetbandblockcache.cpp @@ -170,7 +170,7 @@ CPLErr GDALHashSetBandBlockCache::FlushCache() { CPLErr eErr = CE_None; - if (m_bWriteDirtyBlocks && eGlobalErr == CE_None && + if (!m_nWriteDirtyBlocksDisabled && eGlobalErr == CE_None && poBlock->GetDirty()) { UpdateDirtyBlockFlushingLog(); @@ -227,7 +227,7 @@ CPLErr GDALHashSetBandBlockCache::FlushBlock(int nXBlockOff, int nYBlockOff, CPLErr eErr = CE_None; - if (m_bWriteDirtyBlocks && bWriteDirtyBlock && poBlock->GetDirty()) + if (!m_nWriteDirtyBlocksDisabled && bWriteDirtyBlock && poBlock->GetDirty()) eErr = poBlock->Write(); delete poBlock; diff --git a/gcore/gdalrasterband.cpp b/gcore/gdalrasterband.cpp index 897d1561a565..31c9811ac545 100644 --- a/gcore/gdalrasterband.cpp +++ b/gcore/gdalrasterband.cpp @@ -1140,6 +1140,70 @@ CPLErr CPL_STDCALL GDALFlushRasterCache(GDALRasterBandH hBand) return GDALRasterBand::FromHandle(hBand)->FlushCache(false); } +/************************************************************************/ +/* DropCache() */ +/************************************************************************/ + +/** +* \brief Drop raster data cache : data in cache will be lost. +* +* This call will recover memory used to cache data blocks for this raster +* band, and ensure that new requests are referred to the underlying driver. +* +* This method is the same as the C function GDALDropRasterCache(). +* +* @return CE_None on success. +* @since 3.9 +*/ + +CPLErr GDALRasterBand::DropCache() + +{ + CPLErr result = CE_None; + + if (poBandBlockCache) + poBandBlockCache->DisableDirtyBlockWriting(); + + CPLErr eGlobalErr = eFlushBlockErr; + + if (eFlushBlockErr != CE_None) + { + ReportError( + eFlushBlockErr, CPLE_AppDefined, + "An error occurred while writing a dirty block from DropCache"); + eFlushBlockErr = CE_None; + } + + if (poBandBlockCache == nullptr || !poBandBlockCache->IsInitOK()) + result = eGlobalErr; + else + result = poBandBlockCache->FlushCache(); + + if (poBandBlockCache) + poBandBlockCache->EnableDirtyBlockWriting(); + + return result; +} + +/************************************************************************/ +/* GDALDropRasterCache() */ +/************************************************************************/ + +/** +* \brief Drop raster data cache. +* +* @see GDALRasterBand::DropCache() +* @since 3.9 +*/ + +CPLErr CPL_STDCALL GDALDropRasterCache(GDALRasterBandH hBand) + +{ + VALIDATE_POINTER1(hBand, "GDALDropRasterCache", CE_Failure); + + return GDALRasterBand::FromHandle(hBand)->DropCache(); +} + /************************************************************************/ /* UnreferenceBlock() */ /* */