Skip to content

Commit ffe3b93

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

File tree

2 files changed

+87
-53
lines changed

2 files changed

+87
-53
lines changed

src/nwp_emulator/grib_file_reader.cc

+49-51
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,9 @@
1010
*/
1111
#include <algorithm>
1212
#include <cstring>
13-
#include <sstream>
1413
#include <iostream>
14+
#include <sstream>
1515
#include <typeinfo>
16-
#include <variant>
1716

1817
#include "eckit/exception/Exceptions.h"
1918
#include "eckit/log/Log.h"
@@ -23,6 +22,18 @@
2322
#include "nwp_definitions.h"
2423

2524
namespace nwp_emulator {
25+
void codesHandleDeleter::operator()(codes_handle* h) {
26+
if (h) {
27+
codes_handle_delete(h);
28+
}
29+
}
30+
31+
void fileDeleter::operator()(FILE* f) {
32+
if (f) {
33+
fclose(f);
34+
}
35+
}
36+
2637
GRIBFileReader::GRIBFileReader(const eckit::PathName& inputPath, size_t rank, size_t root, int stepCountLimit) :
2738
rank_(rank), root_(root), DataReader(stepCountLimit) {
2839
if (rank_ != root_) {
@@ -45,10 +56,10 @@ GRIBFileReader::GRIBFileReader(const eckit::PathName& inputPath, size_t rank, si
4556
// 2. Field names & metadata (parameters)
4657
std::string paramBuffer;
4758
if (rank_ == root_) {
48-
for (const auto& param: params_) {
59+
for (const auto& param : params_) {
4960
paramBuffer += param + ";";
5061
}
51-
paramBuffer.pop_back(); // remove last ';' separator
62+
paramBuffer.pop_back(); // remove last ';' separator
5263
}
5364
size_t paramSize = paramBuffer.size();
5465
eckit::mpi::comm().broadcast(paramSize, root_);
@@ -125,7 +136,7 @@ void GRIBFileReader::setupReader(const eckit::PathName& inputPath, bool isRoot)
125136
char buffer[64];
126137
size_t size = sizeof(buffer);
127138
// 1. Lock the grid name from the passed GRIB file
128-
if (codes_get_string(grib_, "gridName", buffer, &size) == GRIB_NOT_FOUND) {
139+
if (codes_get_string(grib(), "gridName", buffer, &size) == GRIB_NOT_FOUND) {
129140
eckit::Log::error() << "Grid type unsupported at the moment, exit" << std::endl;
130141
eckit::mpi::comm().abort(1);
131142
}
@@ -135,15 +146,11 @@ void GRIBFileReader::setupReader(const eckit::PathName& inputPath, bool isRoot)
135146
std::string paramMd, _;
136147
eckit::Log::info() << "Params (name,levtype,level) : ";
137148
for (int i = 0; i < count_ - 1; ++i) {
138-
int err;
139149
readMsgMetadata(_, paramMd);
140150
params_.push_back(paramMd);
141151
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;
152+
if (!resetGribHandle()) {
153+
eckit::Log::error() << " Check source file " << srcPath << std::endl;
147154
eckit::mpi::comm().abort(1);
148155
}
149156
}
@@ -172,26 +179,22 @@ void GRIBFileReader::validateSrcFiles(bool isRoot) {
172179
fileParams.clear();
173180
openGribFile(filename, true);
174181
for (int i = 0; i < count_ - 1; ++i) {
175-
int err;
176182
readMsgMetadata(gridName, paramMd);
177183
if (gridName != gridName_) {
178-
eckit::Log::error() << "Grid in " << filename << " is different from setup ("
179-
<< gridName_ << "), exit..." << std::endl;
184+
eckit::Log::error() << "Grid in " << filename << " is different from setup (" << gridName_
185+
<< "), exit..." << std::endl;
180186
eckit::mpi::comm().abort(1);
181187
}
182188
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;
189+
if (!resetGribHandle()) {
190+
eckit::Log::error() << " Check source file " << filename << std::endl;
188191
eckit::mpi::comm().abort(1);
189192
}
190193
}
191-
readMsgMetadata(gridName, paramMd); // read the last message in the file
194+
readMsgMetadata(gridName, paramMd); // read the last message in the file
192195
if (gridName != gridName_) {
193-
eckit::Log::error() << "Grid in " << filename << " is different from setup ("
194-
<< gridName_ << "), exit..." << std::endl;
196+
eckit::Log::error() << "Grid in " << filename << " is different from setup (" << gridName_ << "), exit..."
197+
<< std::endl;
195198
eckit::mpi::comm().abort(1);
196199
}
197200
fileParams.push_back(paramMd);
@@ -210,7 +213,7 @@ void GRIBFileReader::readMsgMetadata(std::string& gridName, std::string& paramMd
210213
char buffer[64];
211214
size_t size = sizeof(buffer);
212215
gridName = "";
213-
int gridStatus = codes_get_string(grib_, "gridName", buffer, &size);
216+
int gridStatus = codes_get_string(grib(), "gridName", buffer, &size);
214217
if (gridStatus != GRIB_NOT_FOUND) {
215218
gridName = std::string(buffer);
216219
}
@@ -219,7 +222,7 @@ void GRIBFileReader::readMsgMetadata(std::string& gridName, std::string& paramMd
219222
for (const auto& keyword : keywords) {
220223
std::memset(buffer, 0, 64);
221224
size = sizeof(buffer);
222-
codes_get_string(grib_, keyword, buffer, &size);
225+
codes_get_string(grib(), keyword, buffer, &size);
223226
paramMd.append(buffer);
224227
paramMd.append(","); // separator
225228
}
@@ -234,7 +237,7 @@ bool GRIBFileReader::openGribFile(const eckit::PathName& path, bool exit) {
234237
// No check for existence here because the paths passed belong to the src dir
235238
// It is assumed they will not be deleted during runtime
236239
// No check for file format as checked during construction
237-
currentFile_ = fopen(path.asString().c_str(), "rb");
240+
currentFile_.reset(fopen(path.asString().c_str(), "rb"));
238241
if (!currentFile_) {
239242
eckit::Log::error() << "Could not open file " << path << std::endl;
240243
if (exit) {
@@ -243,31 +246,33 @@ bool GRIBFileReader::openGribFile(const eckit::PathName& path, bool exit) {
243246
return false; // defer error handling
244247
}
245248
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;
249+
codes_count_in_file(nullptr, currentFile(), &count_);
250+
if (!resetGribHandle()) {
251251
if (exit) {
252252
eckit::mpi::comm().abort(1);
253253
}
254-
return false; // defer error handling
254+
return false;
255255
}
256256
return true;
257257
}
258258

259-
void GRIBFileReader::closeGribFile() {
260-
if (grib_) {
261-
codes_handle_delete(grib_);
262-
}
263-
if (currentFile_) {
264-
fclose(currentFile_);
259+
bool GRIBFileReader::resetGribHandle() {
260+
int err;
261+
// This calls the deleter of the previous handle first
262+
grib_.reset(codes_handle_new_from_file(nullptr, currentFile(), PRODUCT_GRIB, &err));
263+
if (!grib_) {
264+
eckit::Log::error() << "Could not create grib handle. Error: " << err << std::endl;
265+
return false;
265266
}
267+
return true;
268+
}
269+
270+
void GRIBFileReader::closeGribFile() {
266271
count_ = 0;
267272
index_ = 0;
268-
// To avoid segfault at destruction if they have already been dereferenced
269-
currentFile_ = nullptr;
270-
grib_ = nullptr;
273+
// Calls the custom destructors
274+
currentFile_.reset(nullptr);
275+
grib_.reset(nullptr);
271276
}
272277

273278
bool GRIBFileReader::done() {
@@ -307,28 +312,21 @@ bool GRIBFileReader::nextMessage(std::string& shortName, std::string& levtype, s
307312
std::getline(ss, level, ',');
308313

309314
size_t nbOfValues;
310-
codes_get_size(grib_, "values", &nbOfValues);
315+
codes_get_size(grib(), "values", &nbOfValues);
311316
data.resize(nbOfValues);
312317
if constexpr (std::is_same_v<FIELD_TYPE_REAL, double>) {
313318
ASSERT((std::is_same_v<FIELD_TYPE_REAL, double>));
314-
codes_get_double_array(grib_, "values", reinterpret_cast<double*>(data.data()), &nbOfValues);
319+
codes_get_double_array(grib(), "values", reinterpret_cast<double*>(data.data()), &nbOfValues);
315320
}
316321
else {
317322
ASSERT((std::is_same_v<FIELD_TYPE_REAL, float>));
318-
codes_get_float_array(grib_, "values", reinterpret_cast<float*>(data.data()), &nbOfValues);
323+
codes_get_float_array(grib(), "values", reinterpret_cast<float*>(data.data()), &nbOfValues);
319324
}
320325
}
321326
++index_;
322327
eckit::mpi::comm().broadcast(count_, root_);
323328
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-
}
329+
return resetGribHandle(); // Load the next message
332330
}
333331
return true;
334332
}

src/nwp_emulator/grib_file_reader.h

+38-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
*/
1111
#pragma once
1212

13+
#include <memory>
1314
#include <string>
1415
#include <vector>
1516

@@ -20,6 +21,25 @@
2021

2122
namespace nwp_emulator {
2223

24+
/**
25+
* @brief A structure for a custom deleter to manage codes handles.
26+
*
27+
* This deleter allows to use codes handles pointers as unique pointers for safer memory usage.
28+
*/
29+
struct codesHandleDeleter {
30+
void operator()(codes_handle* h);
31+
};
32+
33+
/**
34+
* @brief A structure for a custom deleter to manage files.
35+
*
36+
* This deleter allows to use C-style file pointers as unique pointers for safer memory usage.
37+
* `std::fstream` is not compatible with eccodes, hence the need to stick to `FILE`.
38+
*/
39+
struct fileDeleter {
40+
void operator()(FILE* f);
41+
};
42+
2343
/**
2444
* @class GRIBFileReader
2545
* @brief Handles reading of GRIB data and serves them as Atlas fields. Supports parallel environments.
@@ -34,8 +54,8 @@ namespace nwp_emulator {
3454
class GRIBFileReader final : public DataReader {
3555
private:
3656
std::vector<eckit::PathName> srcFilenames_;
37-
FILE* currentFile_ = nullptr;
38-
codes_handle* grib_ = nullptr;
57+
std::unique_ptr<FILE, fileDeleter> currentFile_ = nullptr;
58+
std::unique_ptr<codes_handle, codesHandleDeleter> grib_ = nullptr;
3959

4060
size_t rank_;
4161
size_t root_;
@@ -55,6 +75,22 @@ class GRIBFileReader final : public DataReader {
5575
/// Closes current file, and resets pointers.
5676
void closeGribFile();
5777

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

0 commit comments

Comments
 (0)