From 8c4b5b6a70c97614460dd069496e30dad7f2414e Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Wed, 28 Mar 2018 15:51:07 +0300 Subject: [PATCH 1/2] core(test): parallel_for test with exception --- modules/core/test/test_misc.cpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/modules/core/test/test_misc.cpp b/modules/core/test/test_misc.cpp index 4c120c9ef3..3f62ed199d 100644 --- a/modules/core/test/test_misc.cpp +++ b/modules/core/test/test_misc.cpp @@ -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 From 6f69800d0cbdd07fe41b31574c8a62f89a710bf1 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Fri, 30 Mar 2018 14:57:51 +0300 Subject: [PATCH 2/2] core: propagate parallel_for() exceptions --- modules/core/src/parallel.cpp | 88 +++++++++++++++++++++++++++++++++-- 1 file changed, 85 insertions(+), 3 deletions(-) diff --git a/modules/core/src/parallel.cpp b/modules/core/src/parallel.cpp index 5b5e859689..82ca50411a 100644 --- a/modules/core/src/parallel.cpp +++ b/modules/core/src/parallel.cpp @@ -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 // 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 {