Merge pull request #11197 from alalek:parallel_propagate_exception

This commit is contained in:
Alexander Alekhin 2018-04-05 20:24:22 +00:00
commit e20fb7f429
2 changed files with 117 additions and 3 deletions

View File

@ -129,6 +129,28 @@
#include "parallel_impl.hpp"
#ifndef CV__EXCEPTION_PTR
# ifdef CV_CXX11
# define CV__EXCEPTION_PTR 1
# elif defined(CV_ICC)
# define CV__EXCEPTION_PTR 1
# elif defined(_MSC_VER)
# define CV__EXCEPTION_PTR (_MSC_VER >= 1600)
# elif defined(__clang__)
# define CV__EXCEPTION_PTR 0 // C++11 only (see above)
# elif defined(__GNUC__) && defined(__GXX_EXPERIMENTAL_CXX0X__)
# define CV__EXCEPTION_PTR (__GXX_EXPERIMENTAL_CXX0X__ > 0)
# endif
#endif
#ifndef CV__EXCEPTION_PTR
# define CV__EXCEPTION_PTR 0
#else
# include <exception> // std::exception_ptr
#endif
using namespace cv;
namespace cv
@ -169,7 +191,7 @@ namespace
{
public:
ParallelLoopBodyWrapperContext(const cv::ParallelLoopBody& _body, const cv::Range& _r, double _nstripes) :
is_rng_used(false)
is_rng_used(false), hasException(false)
{
body = &_body;
@ -189,7 +211,7 @@ namespace
pThreadRoot = cv::instr::getInstrumentTLSStruct().pCurrentNode;
#endif
}
~ParallelLoopBodyWrapperContext()
void finalize()
{
#ifdef ENABLE_INSTRUMENTATION
for(size_t i = 0; i < pThreadRoot->m_childs.size(); i++)
@ -209,7 +231,17 @@ namespace
if (traceRootRegion)
CV_TRACE_NS::details::parallelForFinalize(*traceRootRegion);
#endif
if (hasException)
{
#if CV__EXCEPTION_PTR
std::rethrow_exception(pException);
#else
CV_ErrorNoReturn(Error::StsError, "Exception in parallel_for() body: " + exception_message);
#endif
}
}
~ParallelLoopBodyWrapperContext() {}
const cv::ParallelLoopBody* body;
cv::Range wholeRange;
@ -223,6 +255,32 @@ namespace
#ifdef ENABLE_INSTRUMENTATION
cv::instr::InstrNode *pThreadRoot;
#endif
bool hasException;
#if CV__EXCEPTION_PTR
std::exception_ptr pException;
#else
cv::String exception_message;
#endif
#if CV__EXCEPTION_PTR
void recordException()
#else
void recordException(const cv::String& msg)
#endif
{
if (!hasException)
{
cv::AutoLock lock(cv::getInitializationMutex());
if (!hasException)
{
hasException = true;
#if CV__EXCEPTION_PTR
pException = std::current_exception();
#else
exception_message = msg;
#endif
}
}
}
private:
ParallelLoopBodyWrapperContext(const ParallelLoopBodyWrapperContext&); // disabled
ParallelLoopBodyWrapperContext& operator=(const ParallelLoopBodyWrapperContext&); // disabled
@ -273,7 +331,29 @@ namespace
CV_TRACE_ARG_VALUE(range_end, "range.end", (int64)r.end);
#endif
(*ctx.body)(r);
try
{
(*ctx.body)(r);
}
#if CV__EXCEPTION_PTR
catch (...)
{
ctx.recordException();
}
#else
catch (const cv::Exception& e)
{
ctx.recordException(e.what());
}
catch (const std::exception& e)
{
ctx.recordException(e.what());
}
catch (...)
{
ctx.recordException("Unknown exception");
}
#endif
if (!ctx.is_rng_used && !(cv::theRNG() == ctx.rng))
ctx.is_rng_used = true;
@ -486,6 +566,8 @@ static void parallel_for_impl(const cv::Range& range, const cv::ParallelLoopBody
#error You have hacked and compiling with unsupported parallel framework
#endif
ctx.finalize(); // propagate exceptions if exists
}
else
{

View File

@ -157,4 +157,36 @@ TEST(Core_Copy, repeat_regression_8972)
});
}
class ThrowErrorParallelLoopBody : public cv::ParallelLoopBody
{
public:
ThrowErrorParallelLoopBody(cv::Mat& dst, int i) : dst_(dst), i_(i) {}
~ThrowErrorParallelLoopBody() {}
void operator()(const cv::Range& r) const
{
for (int i = r.start; i < r.end; i++)
{
CV_Assert(i != i_);
dst_.row(i).setTo(1);
}
}
protected:
Mat dst_;
int i_;
};
TEST(Core_Parallel, propagate_exceptions)
{
Mat dst1(1000, 100, CV_8SC1, Scalar::all(0));
ASSERT_NO_THROW({
parallel_for_(cv::Range(0, dst1.rows), ThrowErrorParallelLoopBody(dst1, -1));
});
Mat dst2(1000, 100, CV_8SC1, Scalar::all(0));
ASSERT_THROW({
parallel_for_(cv::Range(0, dst2.rows), ThrowErrorParallelLoopBody(dst2, dst2.rows / 2));
}, cv::Exception);
}
}} // namespace