Commit 71c1ad23 authored by Paal Kvamme's avatar Paal Kvamme
Browse files

Improved and consolidated handling of exceptions inside OpenMP loops.

parent 302d98c7
......@@ -103,6 +103,7 @@
#include "compression.h"
#include "environment.h"
#include "fancy_timers.h"
#include "mtguard.h"
#include "../exception.h"
#include <algorithm>
......@@ -1670,10 +1671,10 @@ ZgyInternalBulk::_writeAlignedRegion(
std::vector<std::shared_ptr<const WriteBrickArgPack>> const_queue(worksize);
std::vector<std::shared_ptr<const WriteNowArgPack>> normal_queue(worksize);
std::atomic<int> errors(0);
MTGuard guard;
#pragma omp parallel for if(enable_compress_mt() && worksize > 1)
for (std::int64_t ix = 0; ix < static_cast<std::int64_t>(worksize); ++ix) {
try {
guard.run([&](){
const index3_t surveypos = work[ix]; // user's start i0,j0,k0 rounded down
const index3_t brickpos = work[ix] / bs; // as above, but in brick coords
std::shared_ptr<DataBuffer> brick = constbrick;
......@@ -1699,17 +1700,9 @@ ZgyInternalBulk::_writeAlignedRegion(
#pragma omp critical // paranoia?
normal_queue[ix] = now;
}
}
catch (const std::exception&)
{
// No effort to log the actual error or to get the loop to
// end earlier. An exception here really shouldn't happen.
errors.fetch_add(1);
}
}
if (errors.load() != 0)
throw OpenZGY::Errors::ZgyInternalError("Exception preparing buffers");
});
} // end parallel for loop
guard.finished();
// Note errorhandling:
// If there are any errors during actual writing this probably
......
......@@ -19,6 +19,7 @@
#include "timer.h"
#include "fancy_timers.h"
#include "environment.h"
#include "mtguard.h"
#include "file_performance.h"
#include <vector>
......@@ -281,38 +282,20 @@ LocalFileLinux::xx_readv(const ReadList& requests, bool parallel_ok, bool immuta
// 2**31 bricks.
const int threadcount = std::min(std::min(std::numeric_limits<int>::max(), static_cast<int>(requestcount)), omp_get_max_threads());
// Exceptions thrown out of an OpenMP loop are fatal, so I need to
// handle them here.
std::atomic<int> errors(0);
std::string first_error;
MTGuard guard;
#pragma omp parallel num_threads(threadcount)
{
std::unique_ptr<char[]> data(new char[maxsize]);
#pragma omp for
for (std::int64_t ii=0; ii<requestcount; ++ii) {
if (errors.load() != 0)
continue;
const ReadRequest& r = requests[ii];
try {
guard.run([&](){
this->LocalFileLinux::xx_read(data.get(), r.offset, r.size, usagehint);
r.delivery(data.get(), r.size);
}
catch (const std::exception& ex) {
if (errors.fetch_add(1) == 0) {
auto what = ex.what();
first_error = std::string(what && what[0] ? what : "EXCEPTION");
}
continue;
}
} // end omp for
}
// end parallel
if (errors.load() != 0) {
// The distinction between UserError, EndOfFile, and InternalError
// (and maybe even others) is lost. If it matters I can handle code
// for this as well.
throw OpenZGY::Errors::ZgyInternalError(first_error);
});
}
}
guard.finished();
}
}
......
......@@ -20,6 +20,7 @@
#include "databuffer.h"
#include "fancy_timers.h"
#include "environment.h"
#include "mtguard.h"
#include "../exception.h"
#include <memory.h>
......@@ -985,8 +986,7 @@ void createLodMT(const std::shared_ptr<DataBuffer>& result,
// 4 or 8 bytes. So, always slice the slowest dim.
if (input_slice_dim != -1 && input_slice_dim == output_slice_dim) {
SimpleTimerEx tt(timerMT);
std::atomic<int> errors(0);
std::string first_error;
MTGuard guard;
#pragma omp parallel
{
std::int64_t slices_per_thread = (isize[input_slice_dim]-1) / omp_get_num_threads() + 1;
......@@ -1000,20 +1000,15 @@ void createLodMT(const std::shared_ptr<DataBuffer>& result,
<< " slices per thread."
<< std::endl;
#endif
try {
guard.run([&](){
createLodPart(result, input, algorithm,
hist, bincount, histogram_min, histogram_max,
input_slice_dim,
omp_get_thread_num() * slices_per_thread,
slices_per_thread);
}
catch (const std::exception& ex) {
if (errors.fetch_add(1) == 0)
first_error = std::string(ex.what());
}
});
}
if (errors != 0)
throw OpenZGY::Errors::ZgyInternalError(first_error);
guard.finished();
}
else {
SimpleTimerEx tt(timerST);
......
......@@ -34,6 +34,21 @@ namespace InternalZGY {
* If the OpenMP loop has an ordered section then you probably need
* protect that section explicitly. Using a second call to run()
* on the same instance.
*
* Example usage:
*
* \code
* void example() {
* MTGuard guard;
* #pragma omp parallel for
* for (std::int64_t ii = 0; ii < loopcount; ++ii) {
* guard.run([&](){
* // ACTUAL CODE GOES HERE
* });
* }
* guard.finished();
* }
* \endcode
*/
class OPENZGY_TEST_API MTGuard
{
......@@ -57,6 +72,23 @@ protected:
/**
* Helper class that extends MTGuard to also handle progress reports
* and cancellation from inside an OpenMP parallel region.
*
* Example usage:
*
* \code
* void examplewithprogress() {
* ProgressWithDots p1;
* MTGuardWithProgress guard(std::ref(p1), total);
* #pragma omp parallel for
* for (std::int64_t ii = 0; ii < loopcount; ++ii) {
* guard.run([&](){
* // ACTUAL CODE GOES HERE
* guard.progress();
* });
* }
* guard.finished();
* }
*\endcode
*/
class OPENZGY_TEST_API MTGuardWithProgress: public MTGuard
{
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment