Skip to content

Commit ad30730

Browse files
committed
Managing codes handle and file with unique ptrs
1 parent e000a83 commit ad30730

File tree

2 files changed

+68
-45
lines changed

2 files changed

+68
-45
lines changed

src/nwp_emulator/grib_file_reader.cc

+29-43
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
#include <sstream>
1414
#include <iostream>
1515
#include <typeinfo>
16-
#include <variant>
1716

1817
#include "eckit/exception/Exceptions.h"
1918
#include "eckit/log/Log.h"
@@ -125,7 +124,7 @@ void GRIBFileReader::setupReader(const eckit::PathName& inputPath, bool isRoot)
125124
char buffer[64];
126125
size_t size = sizeof(buffer);
127126
// 1. Lock the grid name from the passed GRIB file
128-
if (codes_get_string(grib_, "gridName", buffer, &size) == GRIB_NOT_FOUND) {
127+
if (codes_get_string(grib(), "gridName", buffer, &size) == GRIB_NOT_FOUND) {
129128
eckit::Log::error() << "Grid type unsupported at the moment, exit" << std::endl;
130129
eckit::mpi::comm().abort(1);
131130
}
@@ -135,15 +134,11 @@ void GRIBFileReader::setupReader(const eckit::PathName& inputPath, bool isRoot)
135134
std::string paramMd, _;
136135
eckit::Log::info() << "Params (name,levtype,level) : ";
137136
for (int i = 0; i < count_ - 1; ++i) {
138-
int err;
139137
readMsgMetadata(_, paramMd);
140138
params_.push_back(paramMd);
141139
eckit::Log::info() << params_[i] << "; ";
142-
codes_handle_delete(grib_);
143-
grib_ = codes_handle_new_from_file(nullptr, currentFile_, PRODUCT_GRIB, &err);
144-
if (!grib_) {
145-
eckit::Log::error() << "Could not create grib handle. Error: " << err << " Check source file " << srcPath
146-
<< std::endl;
140+
if (!resetGribHandle()) {
141+
eckit::Log::error() << " Check source file " << srcPath << std::endl;
147142
eckit::mpi::comm().abort(1);
148143
}
149144
}
@@ -172,19 +167,15 @@ void GRIBFileReader::validateSrcFiles(bool isRoot) {
172167
fileParams.clear();
173168
openGribFile(filename, true);
174169
for (int i = 0; i < count_ - 1; ++i) {
175-
int err;
176170
readMsgMetadata(gridName, paramMd);
177171
if (gridName != gridName_) {
178172
eckit::Log::error() << "Grid in " << filename << " is different from setup ("
179173
<< gridName_ << "), exit..." << std::endl;
180174
eckit::mpi::comm().abort(1);
181175
}
182176
fileParams.push_back(paramMd);
183-
codes_handle_delete(grib_);
184-
grib_ = codes_handle_new_from_file(nullptr, currentFile_, PRODUCT_GRIB, &err);
185-
if (!grib_) {
186-
eckit::Log::error() << "Could not create grib handle. Error: " << err << " Check source file "
187-
<< filename << std::endl;
177+
if (!resetGribHandle()) {
178+
eckit::Log::error() << " Check source file " << filename << std::endl;
188179
eckit::mpi::comm().abort(1);
189180
}
190181
}
@@ -210,7 +201,7 @@ void GRIBFileReader::readMsgMetadata(std::string& gridName, std::string& paramMd
210201
char buffer[64];
211202
size_t size = sizeof(buffer);
212203
gridName = "";
213-
int gridStatus = codes_get_string(grib_, "gridName", buffer, &size);
204+
int gridStatus = codes_get_string(grib(), "gridName", buffer, &size);
214205
if (gridStatus != GRIB_NOT_FOUND) {
215206
gridName = std::string(buffer);
216207
}
@@ -219,7 +210,7 @@ void GRIBFileReader::readMsgMetadata(std::string& gridName, std::string& paramMd
219210
for (const auto& keyword : keywords) {
220211
std::memset(buffer, 0, 64);
221212
size = sizeof(buffer);
222-
codes_get_string(grib_, keyword, buffer, &size);
213+
codes_get_string(grib(), keyword, buffer, &size);
223214
paramMd.append(buffer);
224215
paramMd.append(","); // separator
225216
}
@@ -234,7 +225,7 @@ bool GRIBFileReader::openGribFile(const eckit::PathName& path, bool exit) {
234225
// No check for existence here because the paths passed belong to the src dir
235226
// It is assumed they will not be deleted during runtime
236227
// No check for file format as checked during construction
237-
currentFile_ = fopen(path.asString().c_str(), "rb");
228+
currentFile_.reset(fopen(path.asString().c_str(), "rb"));
238229
if (!currentFile_) {
239230
eckit::Log::error() << "Could not open file " << path << std::endl;
240231
if (exit) {
@@ -243,31 +234,33 @@ bool GRIBFileReader::openGribFile(const eckit::PathName& path, bool exit) {
243234
return false; // defer error handling
244235
}
245236
index_ = 0;
246-
codes_count_in_file(nullptr, currentFile_, &count_);
247-
int err;
248-
grib_ = codes_handle_new_from_file(nullptr, currentFile_, PRODUCT_GRIB, &err);
249-
if (!grib_) {
250-
eckit::Log::error() << "Could not create grib handle. Error: " << err << std::endl;
237+
codes_count_in_file(nullptr, currentFile(), &count_);
238+
if (!resetGribHandle()) {
251239
if (exit) {
252240
eckit::mpi::comm().abort(1);
253241
}
254-
return false; // defer error handling
242+
return false;
255243
}
256244
return true;
257245
}
258246

259-
void GRIBFileReader::closeGribFile() {
260-
if (grib_) {
261-
codes_handle_delete(grib_);
262-
}
263-
if (currentFile_) {
264-
fclose(currentFile_);
247+
bool GRIBFileReader::resetGribHandle() {
248+
int err;
249+
// This calls the deleter of the previous handle first
250+
grib_.reset(codes_handle_new_from_file(nullptr, currentFile(), PRODUCT_GRIB, &err));
251+
if (!grib_) {
252+
eckit::Log::error() << "Could not create grib handle. Error: " << err << std::endl;
253+
return false;
265254
}
255+
return true;
256+
}
257+
258+
void GRIBFileReader::closeGribFile() {
266259
count_ = 0;
267260
index_ = 0;
268-
// To avoid segfault at destruction if they have already been dereferenced
269-
currentFile_ = nullptr;
270-
grib_ = nullptr;
261+
// Calls the custom destructors
262+
currentFile_.reset(nullptr);
263+
grib_.reset(nullptr);
271264
}
272265

273266
bool GRIBFileReader::done() {
@@ -307,28 +300,21 @@ bool GRIBFileReader::nextMessage(std::string& shortName, std::string& levtype, s
307300
std::getline(ss, level, ',');
308301

309302
size_t nbOfValues;
310-
codes_get_size(grib_, "values", &nbOfValues);
303+
codes_get_size(grib(), "values", &nbOfValues);
311304
data.resize(nbOfValues);
312305
if constexpr (std::is_same_v<FIELD_TYPE_REAL, double>) {
313306
ASSERT((std::is_same_v<FIELD_TYPE_REAL, double>));
314-
codes_get_double_array(grib_, "values", reinterpret_cast<double*>(data.data()), &nbOfValues);
307+
codes_get_double_array(grib(), "values", reinterpret_cast<double*>(data.data()), &nbOfValues);
315308
}
316309
else {
317310
ASSERT((std::is_same_v<FIELD_TYPE_REAL, float>));
318-
codes_get_float_array(grib_, "values", reinterpret_cast<float*>(data.data()), &nbOfValues);
311+
codes_get_float_array(grib(), "values", reinterpret_cast<float*>(data.data()), &nbOfValues);
319312
}
320313
}
321314
++index_;
322315
eckit::mpi::comm().broadcast(count_, root_);
323316
if (rank_ == root_ && index_ < count_) {
324-
// Load the next message
325-
int err;
326-
codes_handle_delete(grib_);
327-
grib_ = codes_handle_new_from_file(nullptr, currentFile_, PRODUCT_GRIB, &err);
328-
if (!grib_) {
329-
eckit::Log::error() << "Could not create grib handle. Error: " << err << " Skipping..." << std::endl;
330-
return false;
331-
}
317+
return resetGribHandle(); // Load the next message
332318
}
333319
return true;
334320
}

src/nwp_emulator/grib_file_reader.h

+39-2
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,33 @@
1212

1313
#include <string>
1414
#include <vector>
15+
#include <cstdio>
16+
#include <memory>
1517

1618
#include "eccodes.h"
1719
#include "eckit/filesystem/PathName.h"
1820

1921
#include "data_reader.h"
2022

23+
/**
24+
* @brief A structure for a custom deleter to manage codes handles.
25+
*
26+
* This deleter allows to use codes handles pointers as unique pointers for safer memory usage.
27+
*/
28+
struct codesHandleDeleter {
29+
void operator()(codes_handle* h) { if (h) {codes_handle_delete(h);} }
30+
};
31+
32+
/**
33+
* @brief A structure for a custom deleter to manage files.
34+
*
35+
* This deleter allows to use C-style file pointers as unique pointers for safer memory usage.
36+
* `std::fstream` is not compatible with eccodes, hence the need to stick to `FILE`.
37+
*/
38+
struct fileDeleter {
39+
void operator()(FILE* f) { if (f) {fclose(f);} }
40+
};
41+
2142
namespace nwp_emulator {
2243

2344
/**
@@ -34,8 +55,8 @@ namespace nwp_emulator {
3455
class GRIBFileReader final : public DataReader {
3556
private:
3657
std::vector<eckit::PathName> srcFilenames_;
37-
FILE* currentFile_ = nullptr;
38-
codes_handle* grib_ = nullptr;
58+
std::unique_ptr<FILE, fileDeleter> currentFile_{nullptr, fileDeleter()};
59+
std::unique_ptr<codes_handle, codesHandleDeleter> grib_{nullptr, codesHandleDeleter()};
3960

4061
size_t rank_;
4162
size_t root_;
@@ -55,6 +76,22 @@ class GRIBFileReader final : public DataReader {
5576
/// Closes current file, and resets pointers.
5677
void closeGribFile();
5778

79+
/// Gets the grib handle pointer.
80+
codes_handle* grib() { return grib_.get(); }
81+
82+
/// Gets the file pointer.
83+
FILE* currentFile() { return currentFile_.get(); }
84+
85+
/**
86+
* @brief Resets the GRIB handle. Decodes the next message in the current file.
87+
*
88+
* This method first resets the handle pointer using the deleter, making sure memory is properly deallocated.
89+
*
90+
* @return True if successful, false if there was an error loading the next message or if the current file
91+
* has had all its messages decoded already.
92+
*/
93+
bool resetGribHandle();
94+
5895
/// Called from the constructor by the main reader
5996
void setupReader(const eckit::PathName& path, bool isRoot);
6097

0 commit comments

Comments
 (0)