Skip to content

Commit

Permalink
Catch read failures on the Series level
Browse files Browse the repository at this point in the history
i.e. skip entire Iterations if they cannot be parsed
  • Loading branch information
franzpoeschel committed Apr 5, 2022
1 parent df47c94 commit c7a4b41
Showing 1 changed file with 118 additions and 28 deletions.
146 changes: 118 additions & 28 deletions src/Series.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -510,21 +510,30 @@ Given file pattern: ')END"
auto newType = const_cast<Access *>(&IOHandler()->m_frontendAccess);
*newType = Access::READ_WRITE;

if (input->iterationEncoding == IterationEncoding::fileBased)
readFileBased();
else
readGorVBased();

if (series.iterations.empty())
try
{
/* Access::READ_WRITE can be used to create a new Series
* allow setting attributes in that case */
written() = false;

initDefaults(input->iterationEncoding);
setIterationEncoding(input->iterationEncoding);
if (input->iterationEncoding == IterationEncoding::fileBased)
readFileBased();
else
readGorVBased();

written() = true;
if (series.iterations.empty())
{
/* Access::READ_WRITE can be used to create a new Series
* allow setting attributes in that case */
written() = false;

initDefaults(input->iterationEncoding);
setIterationEncoding(input->iterationEncoding);

written() = true;
}
}
catch (...)
{
*newType = oldType;
throw;
}

*newType = oldType;
Expand Down Expand Up @@ -844,34 +853,89 @@ void Series::readFileBased()
<< std::endl;
}

auto readIterationEagerly = [](Iteration &iteration) {
iteration.runDeferredParseAccess();
/*
* Return true if parsing was successful
*/
auto readIterationEagerly =
[](Iteration &iteration) -> std::optional<std::string> {
try
{
iteration.runDeferredParseAccess();
}
catch (error::ReadError const &err)
{
return err.what();
}
Parameter<Operation::CLOSE_FILE> fClose;
iteration.IOHandler()->enqueue(IOTask(&iteration, fClose));
iteration.IOHandler()->flush(internal::defaultFlushParams);
iteration.get().m_closed = internal::CloseStatus::ClosedTemporarily;
return {};
};
std::vector<decltype(Series::iterations)::key_type> unparseableIterations;
if (series.m_parseLazily)
{
for (auto &iteration : series.iterations)
{
iteration.second.get().m_closed =
internal::CloseStatus::ParseAccessDeferred;
}
// open the last iteration, just to parse Series attributes
auto getLastIteration = series.iterations.end();
getLastIteration--;
auto &lastIteration = getLastIteration->second;
readIterationEagerly(lastIteration);
// open the first iteration, just to parse Series attributes
bool atLeastOneIterationSuccessful = false;
for (auto &pair : series.iterations)
{
if (auto error = readIterationEagerly(pair.second); error)
{
std::cerr << "Cannot read iteration '" << pair.first
<< "' and will skip it due to read error:\n"
<< *error << std::endl;
unparseableIterations.push_back(pair.first);
}
else
{
atLeastOneIterationSuccessful = true;
break;
}
}
if (!atLeastOneIterationSuccessful)
{
throw error::ParseError(
"Not a single iteration can be successfully parsed (see above "
"errors). Need to access at least one iteration even in "
"deferred parsing mode in order to read global Series "
"attributes.");
}
}
else
{
bool atLeastOneIterationSuccessful = false;
for (auto &iteration : series.iterations)
{
readIterationEagerly(iteration.second);
if (auto error = readIterationEagerly(iteration.second); error)
{
std::cerr << "Cannot read iteration '" << iteration.first
<< "' and will skip it due to read error:\n"
<< *error << std::endl;
unparseableIterations.push_back(iteration.first);
}
else
{
atLeastOneIterationSuccessful = true;
}
}
if (!atLeastOneIterationSuccessful)
{
throw error::ParseError(
"Not a single iteration can be successfully parsed (see above "
"warnings).");
}
}

for (auto index : unparseableIterations)
{
series.iterations.container().erase(index);
}

if (paddings.size() == 1u)
series.m_filenamePadding = *paddings.begin();

Expand Down Expand Up @@ -1043,11 +1107,15 @@ void Series::readGorVBased(bool do_init)
IOHandler()->enqueue(IOTask(&series.iterations, pList));
IOHandler()->flush(internal::defaultFlushParams);

auto readSingleIteration = [&series, &pOpen, this](
uint64_t index,
std::string path,
bool guardAgainstRereading,
bool beginStep) {
/*
* Return error if one is caught.
*/
auto readSingleIteration =
[&series, &pOpen, this](
uint64_t index,
std::string path,
bool guardAgainstRereading,
bool beginStep) -> std::optional<error::ReadError> {
if (series.iterations.contains(index))
{
// maybe re-read
Expand All @@ -1056,12 +1124,13 @@ void Series::readGorVBased(bool do_init)
// reparsing is not needed
if (guardAgainstRereading && i.written())
{
return;
return {};
}
if (i.get().m_closed != internal::CloseStatus::ParseAccessDeferred)
{
pOpen.path = path;
IOHandler()->enqueue(IOTask(&i, pOpen));
// @todo catch stuff from here too
i.reread(path);
}
}
Expand All @@ -1072,14 +1141,26 @@ void Series::readGorVBased(bool do_init)
i.deferParseAccess({path, index, false, "", beginStep});
if (!series.m_parseLazily)
{
i.runDeferredParseAccess();
try
{
i.runDeferredParseAccess();
}
catch (error::ReadError const &err)
{
std::cerr << "Cannot read iteration '" << index
<< "' and will skip it due to read error:\n"
<< err.what() << std::endl;
series.iterations.container().erase(index);
return {err};
}
i.get().m_closed = internal::CloseStatus::Open;
}
else
{
i.get().m_closed = internal::CloseStatus::ParseAccessDeferred;
}
}
return {};
};

switch (iterationEncoding())
Expand Down Expand Up @@ -1110,7 +1191,16 @@ void Series::readGorVBased(bool do_init)
* Variable-based iteration encoding relies on steps, so parsing must
* happen after opening the first step.
*/
readSingleIteration(index, "", false, true);
if (auto err = readSingleIteration(index, "", false, true); err)
{
/*
* Cannot recover from errors in this place.
* If there is an error in the first iteration, the Series cannot
* be read in variable-based encoding.
* The read API will try to skip other iterations that have errors.
*/
throw *err;
}
break;
}
}
Expand Down

0 comments on commit c7a4b41

Please sign in to comment.