diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -865,6 +865,7 @@ __utility/cmp.h __utility/convert_to_integral.h __utility/declval.h + __utility/empty.h __utility/exception_guard.h __utility/exchange.h __utility/forward.h diff --git a/libcxx/include/__algorithm/pstl_any_all_none_of.h b/libcxx/include/__algorithm/pstl_any_all_none_of.h --- a/libcxx/include/__algorithm/pstl_any_all_none_of.h +++ b/libcxx/include/__algorithm/pstl_any_all_none_of.h @@ -38,14 +38,17 @@ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI bool any_of(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Predicate __pred) { _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator); - return std::__pstl_frontend_dispatch( + auto __res = std::__pstl_frontend_dispatch( _LIBCPP_PSTL_CUSTOMIZATION_POINT(__pstl_any_of), - [&](_ForwardIterator __g_first, _ForwardIterator __g_last, _Predicate __g_pred) { + [&](_ForwardIterator __g_first, _ForwardIterator __g_last, _Predicate __g_pred) -> optional { return std::find_if(__policy, __g_first, __g_last, __g_pred) != __g_last; }, std::move(__first), std::move(__last), std::move(__pred)); + if (!__res) + std::__throw_bad_alloc(); + return *std::move(__res); } template @@ -59,9 +62,9 @@ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI bool all_of(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Pred __pred) { _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator); - return std::__pstl_frontend_dispatch( + auto __res = std::__pstl_frontend_dispatch( _LIBCPP_PSTL_CUSTOMIZATION_POINT(__pstl_all_of), - [&](_ForwardIterator __g_first, _ForwardIterator __g_last, _Pred __g_pred) { + [&](_ForwardIterator __g_first, _ForwardIterator __g_last, _Pred __g_pred) -> optional { return !std::any_of(__policy, __g_first, __g_last, [&](__iter_reference<_ForwardIterator> __value) { return !__g_pred(__value); }); @@ -69,6 +72,9 @@ std::move(__first), std::move(__last), std::move(__pred)); + if (!__res) + std::__throw_bad_alloc(); + return *std::move(__res); } template @@ -82,14 +88,17 @@ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI bool none_of(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Pred __pred) { _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator); - return std::__pstl_frontend_dispatch( + auto __res = std::__pstl_frontend_dispatch( _LIBCPP_PSTL_CUSTOMIZATION_POINT(__pstl_none_of), - [&](_ForwardIterator __g_first, _ForwardIterator __g_last, _Pred __g_pred) { + [&](_ForwardIterator __g_first, _ForwardIterator __g_last, _Pred __g_pred) -> optional { return !std::any_of(__policy, __g_first, __g_last, __g_pred); }, std::move(__first), std::move(__last), std::move(__pred)); + if (!__res) + std::__throw_bad_alloc(); + return *std::move(__res); } _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__algorithm/pstl_backend.h b/libcxx/include/__algorithm/pstl_backend.h --- a/libcxx/include/__algorithm/pstl_backend.h +++ b/libcxx/include/__algorithm/pstl_backend.h @@ -155,6 +155,12 @@ // TODO: Complete this list +Exception handling +================== + +The backend is expected to terminate on any user-thrown exceptions. This includes exceptions which are thrown by a +parallel algorithm which is invoked inside another algorithm. If any allocation fails, the backend shall return a +__pstl_unexpected, which will be turned into a bad_alloc at the frontend level. */ template diff --git a/libcxx/include/__algorithm/pstl_backends/cpu_backend.h b/libcxx/include/__algorithm/pstl_backends/cpu_backend.h --- a/libcxx/include/__algorithm/pstl_backends/cpu_backend.h +++ b/libcxx/include/__algorithm/pstl_backends/cpu_backend.h @@ -15,10 +15,11 @@ // _Functor takes a subrange for [__first, __last) that should be executed in serial template - void __parallel_for(_RandomAccessIterator __first, _RandomAccessIterator __last, _Functor __func); + optional<__empty> __parallel_for(_RandomAccessIterator __first, _RandomAccessIterator __last, _Functor __func); template - _Tp __parallel_transform_reduce(_Iterator __first, _Iterator __last, _UnaryOp, _Tp __init, _BinaryOp, _Reduction); + optional<_Tp> + __parallel_transform_reduce(_Iterator __first, _Iterator __last, _UnaryOp, _Tp __init, _BinaryOp, _Reduction); // Cancel the execution of other jobs - they aren't needed anymore void __cancel_execution(); @@ -28,7 +29,7 @@ class _RandomAccessIterator3, class _Compare, class _LeafMerge> - void __parallel_merge( + optional __parallel_merge( _RandomAccessIterator1 __first1, _RandomAccessIterator1 __last1, _RandomAccessIterator2 __first2, @@ -44,6 +45,13 @@ _LeafSort __leaf_sort); TODO: Document the parallel backend + +Exception handling +================== + +The backend is expected to terminate on any user-thrown exceptions. This includes exceptions which are thrown by a +parallel algorithm which is invoked inside another algorithm. If any allocation fails, the backend shall return a +__pstl_unexpected, which will be turned into a bad_alloc at the frontend level. */ #include <__algorithm/pstl_backends/cpu_backends/any_of.h> diff --git a/libcxx/include/__algorithm/pstl_backends/cpu_backends/any_of.h b/libcxx/include/__algorithm/pstl_backends/cpu_backends/any_of.h --- a/libcxx/include/__algorithm/pstl_backends/cpu_backends/any_of.h +++ b/libcxx/include/__algorithm/pstl_backends/cpu_backends/any_of.h @@ -21,20 +21,23 @@ #include <__utility/pair.h> #include <__utility/terminate_on_exception.h> #include +#include #if !defined(_LIBCPP_HAS_NO_INCOMPLETE_PSTL) && _LIBCPP_STD_VER >= 17 _LIBCPP_BEGIN_NAMESPACE_STD template -_LIBCPP_HIDE_FROM_ABI bool __parallel_or(_Index __first, _Index __last, _Brick __f) { +_LIBCPP_HIDE_FROM_ABI optional __parallel_or(_Index __first, _Index __last, _Brick __f) { std::atomic __found(false); - __par_backend::__parallel_for(__first, __last, [__f, &__found](_Index __i, _Index __j) { + auto __ret = __par_backend::__parallel_for(__first, __last, [__f, &__found](_Index __i, _Index __j) { if (!__found.load(std::memory_order_relaxed) && __f(__i, __j)) { __found.store(true, std::memory_order_relaxed); __par_backend::__cancel_execution(); } }); + if (!__ret) + return nullopt; return __found; } @@ -64,17 +67,15 @@ } template -_LIBCPP_HIDE_FROM_ABI bool -__pstl_any_of(__cpu_backend_tag, _ForwardIterator __first, _ForwardIterator __last, _Predicate __pred) { +_LIBCPP_HIDE_FROM_ABI optional +__pstl_any_of(__cpu_backend_tag, _ForwardIterator __first, _ForwardIterator __last, _Predicate __pred) noexcept { if constexpr (__is_parallel_execution_policy_v<_ExecutionPolicy> && __has_random_access_iterator_category_or_concept<_ForwardIterator>::value) { - return std::__terminate_on_exception([&] { - return std::__parallel_or( - __first, __last, [&__pred](_ForwardIterator __brick_first, _ForwardIterator __brick_last) { - return std::__pstl_any_of<__remove_parallel_policy_t<_ExecutionPolicy>>( - __cpu_backend_tag{}, __brick_first, __brick_last, __pred); - }); - }); + return std::__parallel_or( + __first, __last, [&__pred](_ForwardIterator __brick_first, _ForwardIterator __brick_last) { + return std::__pstl_any_of<__remove_parallel_policy_t<_ExecutionPolicy>>( + __cpu_backend_tag{}, __brick_first, __brick_last, __pred); + }); } else if constexpr (__is_unsequenced_execution_policy_v<_ExecutionPolicy> && __has_random_access_iterator_category_or_concept<_ForwardIterator>::value) { return std::__simd_or(__first, __last - __first, __pred); diff --git a/libcxx/include/__algorithm/pstl_backends/cpu_backends/fill.h b/libcxx/include/__algorithm/pstl_backends/cpu_backends/fill.h --- a/libcxx/include/__algorithm/pstl_backends/cpu_backends/fill.h +++ b/libcxx/include/__algorithm/pstl_backends/cpu_backends/fill.h @@ -34,22 +34,22 @@ } template -_LIBCPP_HIDE_FROM_ABI void -__pstl_fill(__cpu_backend_tag, _ForwardIterator __first, _ForwardIterator __last, const _Tp& __value) { +_LIBCPP_HIDE_FROM_ABI optional<__empty> +__pstl_fill(__cpu_backend_tag, _ForwardIterator __first, _ForwardIterator __last, const _Tp& __value) noexcept { if constexpr (__is_parallel_execution_policy_v<_ExecutionPolicy> && __has_random_access_iterator_category_or_concept<_ForwardIterator>::value) { - std::__terminate_on_exception([&] { - __par_backend::__parallel_for( - __first, __last, [&__value](_ForwardIterator __brick_first, _ForwardIterator __brick_last) { - std::__pstl_fill<__remove_parallel_policy_t<_ExecutionPolicy>>( - __cpu_backend_tag{}, __brick_first, __brick_last, __value); - }); - }); + return __par_backend::__parallel_for( + __first, __last, [&__value](_ForwardIterator __brick_first, _ForwardIterator __brick_last) { + std::__pstl_fill<__remove_parallel_policy_t<_ExecutionPolicy>>( + __cpu_backend_tag{}, __brick_first, __brick_last, __value); + }); } else if constexpr (__is_unsequenced_execution_policy_v<_ExecutionPolicy> && __has_random_access_iterator_category_or_concept<_ForwardIterator>::value) { std::__simd_fill_n(__first, __last - __first, __value); + return __empty{}; } else { std::fill(__first, __last, __value); + return __empty{}; } } diff --git a/libcxx/include/__algorithm/pstl_backends/cpu_backends/find_if.h b/libcxx/include/__algorithm/pstl_backends/cpu_backends/find_if.h --- a/libcxx/include/__algorithm/pstl_backends/cpu_backends/find_if.h +++ b/libcxx/include/__algorithm/pstl_backends/cpu_backends/find_if.h @@ -30,27 +30,30 @@ _LIBCPP_BEGIN_NAMESPACE_STD template -_LIBCPP_HIDE_FROM_ABI _Index -__parallel_find(_Index __first, _Index __last, _Brick __f, _Compare __comp, bool __b_first) { +_LIBCPP_HIDE_FROM_ABI optional<_Index> +__parallel_find(_Index __first, _Index __last, _Brick __f, _Compare __comp, bool __b_first) noexcept { typedef typename std::iterator_traits<_Index>::difference_type _DifferenceType; const _DifferenceType __n = __last - __first; _DifferenceType __initial_dist = __b_first ? __n : -1; std::atomic<_DifferenceType> __extremum(__initial_dist); // TODO: find out what is better here: parallel_for or parallel_reduce - __par_backend::__parallel_for(__first, __last, [__comp, __f, __first, &__extremum](_Index __i, _Index __j) { - // See "Reducing Contention Through Priority Updates", PPoPP '13, for discussion of - // why using a shared variable scales fairly well in this situation. - if (__comp(__i - __first, __extremum)) { - _Index __res = __f(__i, __j); - // If not '__last' returned then we found what we want so put this to extremum - if (__res != __j) { - const _DifferenceType __k = __res - __first; - for (_DifferenceType __old = __extremum; __comp(__k, __old); __old = __extremum) { - __extremum.compare_exchange_weak(__old, __k); + auto __res = + __par_backend::__parallel_for(__first, __last, [__comp, __f, __first, &__extremum](_Index __i, _Index __j) { + // See "Reducing Contention Through Priority Updates", PPoPP '13, for discussion of + // why using a shared variable scales fairly well in this situation. + if (__comp(__i - __first, __extremum)) { + _Index __res = __f(__i, __j); + // If not '__last' returned then we found what we want so put this to extremum + if (__res != __j) { + const _DifferenceType __k = __res - __first; + for (_DifferenceType __old = __extremum; __comp(__k, __old); __old = __extremum) { + __extremum.compare_exchange_weak(__old, __k); + } + } } - } - } - }); + }); + if (!__res) + return nullopt; return __extremum != __initial_dist ? __first + __extremum : __last; } @@ -91,21 +94,19 @@ } template -_LIBCPP_HIDE_FROM_ABI _ForwardIterator -__pstl_find_if(__cpu_backend_tag, _ForwardIterator __first, _ForwardIterator __last, _Predicate __pred) { +_LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator> +__pstl_find_if(__cpu_backend_tag, _ForwardIterator __first, _ForwardIterator __last, _Predicate __pred) noexcept { if constexpr (__is_parallel_execution_policy_v<_ExecutionPolicy> && __has_random_access_iterator_category_or_concept<_ForwardIterator>::value) { - return std::__terminate_on_exception([&] { - return std::__parallel_find( - __first, - __last, - [&__pred](_ForwardIterator __brick_first, _ForwardIterator __brick_last) { - return std::__pstl_find_if<__remove_parallel_policy_t<_ExecutionPolicy>>( - __cpu_backend_tag{}, __brick_first, __brick_last, __pred); - }, - less<>{}, - true); - }); + return std::__parallel_find( + __first, + __last, + [&__pred](_ForwardIterator __brick_first, _ForwardIterator __brick_last) { + return *std::__pstl_find_if<__remove_parallel_policy_t<_ExecutionPolicy>>( + __cpu_backend_tag{}, __brick_first, __brick_last, __pred); + }, + less<>{}, + true); } else if constexpr (__is_unsequenced_execution_policy_v<_ExecutionPolicy> && __has_random_access_iterator_category_or_concept<_ForwardIterator>::value) { using __diff_t = __iter_diff_t<_ForwardIterator>; diff --git a/libcxx/include/__algorithm/pstl_backends/cpu_backends/for_each.h b/libcxx/include/__algorithm/pstl_backends/cpu_backends/for_each.h --- a/libcxx/include/__algorithm/pstl_backends/cpu_backends/for_each.h +++ b/libcxx/include/__algorithm/pstl_backends/cpu_backends/for_each.h @@ -34,22 +34,22 @@ } template -_LIBCPP_HIDE_FROM_ABI void +_LIBCPP_HIDE_FROM_ABI optional<__empty> __pstl_for_each(__cpu_backend_tag, _ForwardIterator __first, _ForwardIterator __last, _Functor __func) { if constexpr (__is_parallel_execution_policy_v<_ExecutionPolicy> && __has_random_access_iterator_category_or_concept<_ForwardIterator>::value) { - std::__terminate_on_exception([&] { - std::__par_backend::__parallel_for( - __first, __last, [__func](_ForwardIterator __brick_first, _ForwardIterator __brick_last) { - std::__pstl_for_each<__remove_parallel_policy_t<_ExecutionPolicy>>( - __cpu_backend_tag{}, __brick_first, __brick_last, __func); - }); - }); + return std::__par_backend::__parallel_for( + __first, __last, [__func](_ForwardIterator __brick_first, _ForwardIterator __brick_last) { + std::__pstl_for_each<__remove_parallel_policy_t<_ExecutionPolicy>>( + __cpu_backend_tag{}, __brick_first, __brick_last, __func); + }); } else if constexpr (__is_unsequenced_execution_policy_v<_ExecutionPolicy> && __has_random_access_iterator_category_or_concept<_ForwardIterator>::value) { std::__simd_walk_1(__first, __last - __first, __func); + return __empty{}; } else { std::for_each(__first, __last, __func); + return __empty{}; } } diff --git a/libcxx/include/__algorithm/pstl_backends/cpu_backends/libdispatch.h b/libcxx/include/__algorithm/pstl_backends/cpu_backends/libdispatch.h --- a/libcxx/include/__algorithm/pstl_backends/cpu_backends/libdispatch.h +++ b/libcxx/include/__algorithm/pstl_backends/cpu_backends/libdispatch.h @@ -21,12 +21,14 @@ #include <__memory/construct_at.h> #include <__memory/unique_ptr.h> #include <__numeric/reduce.h> +#include <__utility/empty.h> #include <__utility/exception_guard.h> #include <__utility/move.h> #include <__utility/pair.h> #include <__utility/terminate_on_exception.h> #include #include +#include _LIBCPP_PUSH_MACROS #include <__undef_macros> @@ -60,8 +62,8 @@ [[__gnu__::__const__]] _LIBCPP_EXPORTED_FROM_ABI __chunk_partitions __partition_chunks(ptrdiff_t __size); template -_LIBCPP_HIDE_FROM_ABI void -__parallel_for(_RandomAccessIterator __first, _RandomAccessIterator __last, _Functor __func) { +_LIBCPP_HIDE_FROM_ABI optional<__empty> +__parallel_for(_RandomAccessIterator __first, _RandomAccessIterator __last, _Functor __func) noexcept { auto __partitions = __libdispatch::__partition_chunks(__last - __first); // Perform the chunked execution. @@ -73,6 +75,8 @@ : (__chunk * __partitions.__chunk_size_) + (__partitions.__first_chunk_size_ - __partitions.__chunk_size_); __func(__first + __index, __first + __index + __this_chunk_size); }); + + return __empty{}; } template @@ -90,23 +94,23 @@ typename _RandomAccessIterator3, typename _Compare, typename _LeafMerge> -_LIBCPP_HIDE_FROM_ABI void __parallel_merge( +_LIBCPP_HIDE_FROM_ABI optional<__empty> __parallel_merge( _RandomAccessIterator1 __first1, _RandomAccessIterator1 __last1, _RandomAccessIterator2 __first2, _RandomAccessIterator2 __last2, _RandomAccessIterator3 __result, _Compare __comp, - _LeafMerge __leaf_merge) { + _LeafMerge __leaf_merge) noexcept { __chunk_partitions __partitions = __libdispatch::__partition_chunks(std::max(__last1 - __first1, __last2 - __first2)); if (__partitions.__chunk_count_ == 0) - return; + return __empty{}; if (__partitions.__chunk_count_ == 1) { __leaf_merge(__first1, __last1, __first2, __last2, __result, __comp); - return; + return __empty{}; } using __merge_range_t = __merge_range<_RandomAccessIterator1, _RandomAccessIterator2, _RandomAccessIterator3>; @@ -117,67 +121,82 @@ std::destroy_n(__ptr, __n_ranges); std::allocator<__merge_range_t>().deallocate(__ptr, __n_ranges); }; + unique_ptr<__merge_range_t[], decltype(__destroy)> __ranges( - std::allocator<__merge_range_t>().allocate(__n_ranges), __destroy); + [&]() -> __merge_range_t* { +# ifndef _LIBCPP_HAS_NO_EXCEPTIONS + try { +# endif + return std::allocator<__merge_range_t>().allocate(__n_ranges); +# ifndef _LIBCPP_HAS_NO_EXCEPTIONS + } catch (const std::bad_alloc&) { + return nullptr; + } +# endif + }(), + __destroy); + + if (!__ranges) + return nullopt; // TODO: Improve the case where the smaller range is merged into just a few (or even one) chunks of the larger case - std::__terminate_on_exception([&] { - __merge_range_t* __r = __ranges.get(); - std::__construct_at(__r++, __first1, __first2, __result); - - bool __iterate_first_range = __last1 - __first1 > __last2 - __first2; - - auto __compute_chunk = [&](size_t __chunk_size) -> __merge_range_t { - auto [__mid1, __mid2] = [&] { - if (__iterate_first_range) { - auto __m1 = __first1 + __chunk_size; - auto __m2 = std::lower_bound(__first2, __last2, __m1[-1], __comp); - return std::make_pair(__m1, __m2); - } else { - auto __m2 = __first2 + __chunk_size; - auto __m1 = std::lower_bound(__first1, __last1, __m2[-1], __comp); - return std::make_pair(__m1, __m2); - } - }(); - - __result += (__mid1 - __first1) + (__mid2 - __first2); - __first1 = __mid1; - __first2 = __mid2; - return {std::move(__mid1), std::move(__mid2), __result}; - }; - - // handle first chunk - std::__construct_at(__r++, __compute_chunk(__partitions.__first_chunk_size_)); - - // handle 2 -> N - 1 chunks - for (ptrdiff_t __i = 0; __i != __partitions.__chunk_count_ - 2; ++__i) - std::__construct_at(__r++, __compute_chunk(__partitions.__chunk_size_)); - - // handle last chunk - std::__construct_at(__r, __last1, __last2, __result); - - __libdispatch::__dispatch_apply(__partitions.__chunk_count_, [&](size_t __index) { - auto __first_iters = __ranges[__index]; - auto __last_iters = __ranges[__index + 1]; - __leaf_merge( - __first_iters.__mid1_, - __last_iters.__mid1_, - __first_iters.__mid2_, - __last_iters.__mid2_, - __first_iters.__result_, - __comp); - }); + __merge_range_t* __r = __ranges.get(); + std::__construct_at(__r++, __first1, __first2, __result); + + bool __iterate_first_range = __last1 - __first1 > __last2 - __first2; + + auto __compute_chunk = [&](size_t __chunk_size) -> __merge_range_t { + auto [__mid1, __mid2] = [&] { + if (__iterate_first_range) { + auto __m1 = __first1 + __chunk_size; + auto __m2 = std::lower_bound(__first2, __last2, __m1[-1], __comp); + return std::make_pair(__m1, __m2); + } else { + auto __m2 = __first2 + __chunk_size; + auto __m1 = std::lower_bound(__first1, __last1, __m2[-1], __comp); + return std::make_pair(__m1, __m2); + } + }(); + + __result += (__mid1 - __first1) + (__mid2 - __first2); + __first1 = __mid1; + __first2 = __mid2; + return {std::move(__mid1), std::move(__mid2), __result}; + }; + + // handle first chunk + std::__construct_at(__r++, __compute_chunk(__partitions.__first_chunk_size_)); + + // handle 2 -> N - 1 chunks + for (ptrdiff_t __i = 0; __i != __partitions.__chunk_count_ - 2; ++__i) + std::__construct_at(__r++, __compute_chunk(__partitions.__chunk_size_)); + + // handle last chunk + std::__construct_at(__r, __last1, __last2, __result); + + __libdispatch::__dispatch_apply(__partitions.__chunk_count_, [&](size_t __index) { + auto __first_iters = __ranges[__index]; + auto __last_iters = __ranges[__index + 1]; + __leaf_merge( + __first_iters.__mid1_, + __last_iters.__mid1_, + __first_iters.__mid2_, + __last_iters.__mid2_, + __first_iters.__result_, + __comp); }); + + return __empty{}; } template -_LIBCPP_HIDE_FROM_ABI _Value __parallel_transform_reduce( +_LIBCPP_HIDE_FROM_ABI optional<_Value> __parallel_transform_reduce( _RandomAccessIterator __first, _RandomAccessIterator __last, _Transform __transform, _Value __init, _Combiner __combiner, - _Reduction __reduction) { + _Reduction __reduction) noexcept { if (__first == __last) return __init; @@ -222,9 +241,10 @@ // TODO: parallelize this template -_LIBCPP_HIDE_FROM_ABI void __parallel_stable_sort( - _RandomAccessIterator __first, _RandomAccessIterator __last, _Comp __comp, _LeafSort __leaf_sort) { +_LIBCPP_HIDE_FROM_ABI optional<__empty> __parallel_stable_sort( + _RandomAccessIterator __first, _RandomAccessIterator __last, _Comp __comp, _LeafSort __leaf_sort) noexcept { __leaf_sort(__first, __last, __comp); + return __empty{}; } _LIBCPP_HIDE_FROM_ABI inline void __cancel_execution() {} diff --git a/libcxx/include/__algorithm/pstl_backends/cpu_backends/merge.h b/libcxx/include/__algorithm/pstl_backends/cpu_backends/merge.h --- a/libcxx/include/__algorithm/pstl_backends/cpu_backends/merge.h +++ b/libcxx/include/__algorithm/pstl_backends/cpu_backends/merge.h @@ -16,6 +16,7 @@ #include <__type_traits/is_execution_policy.h> #include <__utility/move.h> #include <__utility/terminate_on_exception.h> +#include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header @@ -30,7 +31,7 @@ class _ForwardIterator2, class _ForwardOutIterator, class _Comp> -_LIBCPP_HIDE_FROM_ABI _ForwardOutIterator __pstl_merge( +_LIBCPP_HIDE_FROM_ABI optional<_ForwardOutIterator> __pstl_merge( __cpu_backend_tag, _ForwardIterator1 __first1, _ForwardIterator1 __last1, @@ -42,31 +43,31 @@ __has_random_access_iterator_category_or_concept<_ForwardIterator1>::value && __has_random_access_iterator_category_or_concept<_ForwardIterator2>::value && __has_random_access_iterator_category_or_concept<_ForwardOutIterator>::value) { - return std::__terminate_on_exception([&] { - __par_backend::__parallel_merge( - __first1, - __last1, - __first2, - __last2, - __result, - __comp, - [](_ForwardIterator1 __g_first1, - _ForwardIterator1 __g_last1, - _ForwardIterator2 __g_first2, - _ForwardIterator2 __g_last2, - _ForwardOutIterator __g_result, - _Comp __g_comp) { - return std::__pstl_merge<__remove_parallel_policy_t<_ExecutionPolicy>>( - __cpu_backend_tag{}, - std::move(__g_first1), - std::move(__g_last1), - std::move(__g_first2), - std::move(__g_last2), - std::move(__g_result), - std::move(__g_comp)); - }); - return __result + (__last1 - __first1) + (__last2 - __first2); - }); + auto __res = __par_backend::__parallel_merge( + __first1, + __last1, + __first2, + __last2, + __result, + __comp, + [](_ForwardIterator1 __g_first1, + _ForwardIterator1 __g_last1, + _ForwardIterator2 __g_first2, + _ForwardIterator2 __g_last2, + _ForwardOutIterator __g_result, + _Comp __g_comp) { + (void)std::__pstl_merge<__remove_parallel_policy_t<_ExecutionPolicy>>( + __cpu_backend_tag{}, + std::move(__g_first1), + std::move(__g_last1), + std::move(__g_first2), + std::move(__g_last2), + std::move(__g_result), + std::move(__g_comp)); + }); + if (!__res) + return nullopt; + return __result + (__last1 - __first1) + (__last2 - __first2); } else { return std::merge(__first1, __last1, __first2, __last2, __result, __comp); } diff --git a/libcxx/include/__algorithm/pstl_backends/cpu_backends/serial.h b/libcxx/include/__algorithm/pstl_backends/cpu_backends/serial.h --- a/libcxx/include/__algorithm/pstl_backends/cpu_backends/serial.h +++ b/libcxx/include/__algorithm/pstl_backends/cpu_backends/serial.h @@ -11,8 +11,10 @@ #define _LIBCPP___ALGORITHM_PSTL_BACKENDS_CPU_BACKENDS_SERIAL_H #include <__config> +#include <__utility/empty.h> #include <__utility/move.h> #include +#include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header @@ -26,38 +28,42 @@ inline namespace __serial_cpu_backend { template -_LIBCPP_HIDE_FROM_ABI void __parallel_for(_RandomAccessIterator __first, _RandomAccessIterator __last, _Fp __f) { +_LIBCPP_HIDE_FROM_ABI optional<__empty> +__parallel_for(_RandomAccessIterator __first, _RandomAccessIterator __last, _Fp __f) noexcept { __f(__first, __last); + return __empty{}; } template -_LIBCPP_HIDE_FROM_ABI _Tp -__parallel_transform_reduce(_Index __first, _Index __last, _UnaryOp, _Tp __init, _BinaryOp, _Reduce __reduce) { +_LIBCPP_HIDE_FROM_ABI optional<_Tp> +__parallel_transform_reduce(_Index __first, _Index __last, _UnaryOp, _Tp __init, _BinaryOp, _Reduce __reduce) noexcept { return __reduce(std::move(__first), std::move(__last), std::move(__init)); } template -_LIBCPP_HIDE_FROM_ABI void __parallel_stable_sort( - _RandomAccessIterator __first, _RandomAccessIterator __last, _Compare __comp, _LeafSort __leaf_sort) { +_LIBCPP_HIDE_FROM_ABI optional<__empty> __parallel_stable_sort( + _RandomAccessIterator __first, _RandomAccessIterator __last, _Compare __comp, _LeafSort __leaf_sort) noexcept { __leaf_sort(__first, __last, __comp); + return __empty{}; } -_LIBCPP_HIDE_FROM_ABI inline void __cancel_execution() {} +_LIBCPP_HIDE_FROM_ABI inline void __cancel_execution() noexcept {} template -_LIBCPP_HIDE_FROM_ABI void __parallel_merge( +_LIBCPP_HIDE_FROM_ABI optional<__empty> __parallel_merge( _RandomAccessIterator1 __first1, _RandomAccessIterator1 __last1, _RandomAccessIterator2 __first2, _RandomAccessIterator2 __last2, _RandomAccessIterator3 __outit, _Compare __comp, - _LeafMerge __leaf_merge) { + _LeafMerge __leaf_merge) noexcept { __leaf_merge(__first1, __last1, __first2, __last2, __outit, __comp); + return __empty{}; } // TODO: Complete this list diff --git a/libcxx/include/__algorithm/pstl_backends/cpu_backends/stable_sort.h b/libcxx/include/__algorithm/pstl_backends/cpu_backends/stable_sort.h --- a/libcxx/include/__algorithm/pstl_backends/cpu_backends/stable_sort.h +++ b/libcxx/include/__algorithm/pstl_backends/cpu_backends/stable_sort.h @@ -24,17 +24,16 @@ _LIBCPP_BEGIN_NAMESPACE_STD template -_LIBCPP_HIDE_FROM_ABI void +_LIBCPP_HIDE_FROM_ABI optional<__empty> __pstl_stable_sort(__cpu_backend_tag, _RandomAccessIterator __first, _RandomAccessIterator __last, _Comp __comp) { if constexpr (__is_parallel_execution_policy_v<_ExecutionPolicy>) { - std::__terminate_on_exception([&] { - __par_backend::__parallel_stable_sort( - __first, __last, __comp, [](_RandomAccessIterator __g_first, _RandomAccessIterator __g_last, _Comp __g_comp) { - std::stable_sort(__g_first, __g_last, __g_comp); - }); - }); + return __par_backend::__parallel_stable_sort( + __first, __last, __comp, [](_RandomAccessIterator __g_first, _RandomAccessIterator __g_last, _Comp __g_comp) { + std::stable_sort(__g_first, __g_last, __g_comp); + }); } else { std::stable_sort(__first, __last, __comp); + return __empty{}; } } diff --git a/libcxx/include/__algorithm/pstl_backends/cpu_backends/thread.h b/libcxx/include/__algorithm/pstl_backends/cpu_backends/thread.h --- a/libcxx/include/__algorithm/pstl_backends/cpu_backends/thread.h +++ b/libcxx/include/__algorithm/pstl_backends/cpu_backends/thread.h @@ -11,8 +11,10 @@ #include <__assert> #include <__config> +#include <__utility/empty.h> #include <__utility/move.h> #include +#include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header @@ -32,20 +34,23 @@ inline namespace __thread_cpu_backend { template -_LIBCPP_HIDE_FROM_ABI void __parallel_for(_RandomAccessIterator __first, _RandomAccessIterator __last, _Fp __f) { +_LIBCPP_HIDE_FROM_ABI optional<__empty> +__parallel_for(_RandomAccessIterator __first, _RandomAccessIterator __last, _Fp __f) noexcept { __f(__first, __last); + return __empty{}; } template -_LIBCPP_HIDE_FROM_ABI _Tp +_LIBCPP_HIDE_FROM_ABI optional<_Tp> __parallel_transform_reduce(_Index __first, _Index __last, _UnaryOp, _Tp __init, _BinaryOp, _Reduce __reduce) { return __reduce(std::move(__first), std::move(__last), std::move(__init)); } template -_LIBCPP_HIDE_FROM_ABI void __parallel_stable_sort( +_LIBCPP_HIDE_FROM_ABI optional<__empty> __parallel_stable_sort( _RandomAccessIterator __first, _RandomAccessIterator __last, _Compare __comp, _LeafSort __leaf_sort) { __leaf_sort(__first, __last, __comp); + return __empty{}; } _LIBCPP_HIDE_FROM_ABI inline void __cancel_execution() {} @@ -55,7 +60,7 @@ class _RandomAccessIterator3, class _Compare, class _LeafMerge> -_LIBCPP_HIDE_FROM_ABI void __parallel_merge( +_LIBCPP_HIDE_FROM_ABI optional<__empty> __parallel_merge( _RandomAccessIterator1 __first1, _RandomAccessIterator1 __last1, _RandomAccessIterator2 __first2, @@ -64,6 +69,7 @@ _Compare __comp, _LeafMerge __leaf_merge) { __leaf_merge(__first1, __last1, __first2, __last2, __outit, __comp); + return __empty{}; } } // namespace __thread_cpu_backend diff --git a/libcxx/include/__algorithm/pstl_backends/cpu_backends/transform.h b/libcxx/include/__algorithm/pstl_backends/cpu_backends/transform.h --- a/libcxx/include/__algorithm/pstl_backends/cpu_backends/transform.h +++ b/libcxx/include/__algorithm/pstl_backends/cpu_backends/transform.h @@ -37,22 +37,20 @@ } template -_LIBCPP_HIDE_FROM_ABI _ForwardOutIterator __pstl_transform( +_LIBCPP_HIDE_FROM_ABI optional<_ForwardOutIterator> __pstl_transform( __cpu_backend_tag, _ForwardIterator __first, _ForwardIterator __last, _ForwardOutIterator __result, - _UnaryOperation __op) { + _UnaryOperation __op) noexcept { if constexpr (__is_parallel_execution_policy_v<_ExecutionPolicy> && __has_random_access_iterator_category_or_concept<_ForwardIterator>::value && __has_random_access_iterator_category_or_concept<_ForwardOutIterator>::value) { - std::__terminate_on_exception([&] { - std::__par_backend::__parallel_for( - __first, __last, [__op, __first, __result](_ForwardIterator __brick_first, _ForwardIterator __brick_last) { - return std::__pstl_transform<__remove_parallel_policy_t<_ExecutionPolicy>>( - __cpu_backend_tag{}, __brick_first, __brick_last, __result + (__brick_first - __first), __op); - }); - }); + std::__par_backend::__parallel_for( + __first, __last, [__op, __first, __result](_ForwardIterator __brick_first, _ForwardIterator __brick_last) { + return std::__pstl_transform<__remove_parallel_policy_t<_ExecutionPolicy>>( + __cpu_backend_tag{}, __brick_first, __brick_last, __result + (__brick_first - __first), __op); + }); return __result + (__last - __first); } else if constexpr (__is_unsequenced_execution_policy_v<_ExecutionPolicy> && __has_random_access_iterator_category_or_concept<_ForwardIterator>::value && @@ -83,31 +81,31 @@ class _ForwardOutIterator, class _BinaryOperation, enable_if_t>, int> = 0> -_LIBCPP_HIDE_FROM_ABI _ForwardOutIterator __pstl_transform( +_LIBCPP_HIDE_FROM_ABI optional<_ForwardOutIterator> __pstl_transform( __cpu_backend_tag, _ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2, _ForwardOutIterator __result, - _BinaryOperation __op) { + _BinaryOperation __op) noexcept { if constexpr (__is_parallel_execution_policy_v<_ExecutionPolicy> && __has_random_access_iterator_category_or_concept<_ForwardIterator1>::value && __has_random_access_iterator_category_or_concept<_ForwardIterator2>::value && __has_random_access_iterator_category_or_concept<_ForwardOutIterator>::value) { - std::__terminate_on_exception([&] { - std::__par_backend::__parallel_for( - __first1, - __last1, - [__op, __first1, __first2, __result](_ForwardIterator1 __brick_first, _ForwardIterator1 __brick_last) { - return std::__pstl_transform<__remove_parallel_policy_t<_ExecutionPolicy>>( - __cpu_backend_tag{}, - __brick_first, - __brick_last, - __first2 + (__brick_first - __first1), - __result + (__brick_first - __first1), - __op); - }); - }); + auto __res = std::__par_backend::__parallel_for( + __first1, + __last1, + [__op, __first1, __first2, __result](_ForwardIterator1 __brick_first, _ForwardIterator1 __brick_last) { + return std::__pstl_transform<__remove_parallel_policy_t<_ExecutionPolicy>>( + __cpu_backend_tag{}, + __brick_first, + __brick_last, + __first2 + (__brick_first - __first1), + __result + (__brick_first - __first1), + __op); + }); + if (!__res) + return nullopt; return __result + (__last1 - __first1); } else if constexpr (__is_unsequenced_execution_policy_v<_ExecutionPolicy> && __has_random_access_iterator_category_or_concept<_ForwardIterator1>::value && diff --git a/libcxx/include/__algorithm/pstl_backends/cpu_backends/transform_reduce.h b/libcxx/include/__algorithm/pstl_backends/cpu_backends/transform_reduce.h --- a/libcxx/include/__algorithm/pstl_backends/cpu_backends/transform_reduce.h +++ b/libcxx/include/__algorithm/pstl_backends/cpu_backends/transform_reduce.h @@ -98,7 +98,7 @@ class _Tp, class _BinaryOperation1, class _BinaryOperation2> -_LIBCPP_HIDE_FROM_ABI _Tp __pstl_transform_reduce( +_LIBCPP_HIDE_FROM_ABI optional<_Tp> __pstl_transform_reduce( __cpu_backend_tag, _ForwardIterator1 __first1, _ForwardIterator1 __last1, @@ -109,27 +109,25 @@ if constexpr (__is_parallel_execution_policy_v<_ExecutionPolicy> && __has_random_access_iterator_category_or_concept<_ForwardIterator1>::value && __has_random_access_iterator_category_or_concept<_ForwardIterator2>::value) { - return std::__terminate_on_exception([&] { - return __par_backend::__parallel_transform_reduce( - __first1, - std::move(__last1), - [__first1, __first2, __transform](_ForwardIterator1 __iter) { - return __transform(*__iter, *(__first2 + (__iter - __first1))); - }, - std::move(__init), - std::move(__reduce), - [__first1, __first2, __reduce, __transform]( - _ForwardIterator1 __brick_first, _ForwardIterator1 __brick_last, _Tp __brick_init) { - return std::__pstl_transform_reduce<__remove_parallel_policy_t<_ExecutionPolicy>>( - __cpu_backend_tag{}, - __brick_first, - std::move(__brick_last), - __first2 + (__brick_first - __first1), - std::move(__brick_init), - std::move(__reduce), - std::move(__transform)); - }); - }); + return __par_backend::__parallel_transform_reduce( + __first1, + std::move(__last1), + [__first1, __first2, __transform](_ForwardIterator1 __iter) { + return __transform(*__iter, *(__first2 + (__iter - __first1))); + }, + std::move(__init), + std::move(__reduce), + [__first1, __first2, __reduce, __transform]( + _ForwardIterator1 __brick_first, _ForwardIterator1 __brick_last, _Tp __brick_init) { + return *std::__pstl_transform_reduce<__remove_parallel_policy_t<_ExecutionPolicy>>( + __cpu_backend_tag{}, + __brick_first, + std::move(__brick_last), + __first2 + (__brick_first - __first1), + std::move(__brick_init), + std::move(__reduce), + std::move(__transform)); + }); } else if constexpr (__is_unsequenced_execution_policy_v<_ExecutionPolicy> && __has_random_access_iterator_category_or_concept<_ForwardIterator1>::value && __has_random_access_iterator_category_or_concept<_ForwardIterator2>::value) { @@ -149,7 +147,7 @@ } template -_LIBCPP_HIDE_FROM_ABI _Tp __pstl_transform_reduce( +_LIBCPP_HIDE_FROM_ABI optional<_Tp> __pstl_transform_reduce( __cpu_backend_tag, _ForwardIterator __first, _ForwardIterator __last, @@ -158,23 +156,21 @@ _UnaryOperation __transform) { if constexpr (__is_parallel_execution_policy_v<_ExecutionPolicy> && __has_random_access_iterator_category_or_concept<_ForwardIterator>::value) { - return std::__terminate_on_exception([&] { - return __par_backend::__parallel_transform_reduce( - std::move(__first), - std::move(__last), - [__transform](_ForwardIterator __iter) { return __transform(*__iter); }, - std::move(__init), - __reduce, - [__transform, __reduce](auto __brick_first, auto __brick_last, _Tp __brick_init) { - return std::__pstl_transform_reduce<__remove_parallel_policy_t<_ExecutionPolicy>>( - __cpu_backend_tag{}, - std::move(__brick_first), - std::move(__brick_last), - std::move(__brick_init), - std::move(__reduce), - std::move(__transform)); - }); - }); + return __par_backend::__parallel_transform_reduce( + std::move(__first), + std::move(__last), + [__transform](_ForwardIterator __iter) { return __transform(*__iter); }, + std::move(__init), + __reduce, + [__transform, __reduce](auto __brick_first, auto __brick_last, _Tp __brick_init) { + return *std::__pstl_transform_reduce<__remove_parallel_policy_t<_ExecutionPolicy>>( + __cpu_backend_tag{}, + std::move(__brick_first), + std::move(__brick_last), + std::move(__brick_init), + std::move(__reduce), + std::move(__transform)); + }); } else if constexpr (__is_unsequenced_execution_policy_v<_ExecutionPolicy> && __has_random_access_iterator_category_or_concept<_ForwardIterator>::value) { return std::__simd_transform_reduce( diff --git a/libcxx/include/__algorithm/pstl_copy.h b/libcxx/include/__algorithm/pstl_copy.h --- a/libcxx/include/__algorithm/pstl_copy.h +++ b/libcxx/include/__algorithm/pstl_copy.h @@ -43,7 +43,7 @@ enable_if_t, int> = 0> _LIBCPP_HIDE_FROM_ABI _ForwardOutIterator copy(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _ForwardOutIterator __result) { - return std::__pstl_frontend_dispatch( + auto __res = std::__pstl_frontend_dispatch( _LIBCPP_PSTL_CUSTOMIZATION_POINT(__pstl_copy), [&__policy](_ForwardIterator __g_first, _ForwardIterator __g_last, _ForwardOutIterator __g_result) { return std::transform(__policy, __g_first, __g_last, __g_result, __identity()); @@ -51,6 +51,9 @@ std::move(__first), std::move(__last), std::move(__result)); + if (!__res) + std::__throw_bad_alloc(); + return *std::move(__res); } template @@ -64,7 +67,7 @@ enable_if_t, int> = 0> _LIBCPP_HIDE_FROM_ABI _ForwardOutIterator copy_n(_ExecutionPolicy&& __policy, _ForwardIterator __first, _Size __n, _ForwardOutIterator __result) { - return std::__pstl_frontend_dispatch( + auto __res = std::__pstl_frontend_dispatch( _LIBCPP_PSTL_CUSTOMIZATION_POINT(__pstl_copy_n), [&__policy](_ForwardIterator __g_first, _Size __g_n, _ForwardOutIterator __g_result) { if constexpr (__has_random_access_iterator_category_or_concept<_ForwardIterator>::value) @@ -75,6 +78,9 @@ std::move(__first), __n, std::move(__result)); + if (!__res) + std::__throw_bad_alloc(); + return *std::move(__res); } _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__algorithm/pstl_count.h b/libcxx/include/__algorithm/pstl_count.h --- a/libcxx/include/__algorithm/pstl_count.h +++ b/libcxx/include/__algorithm/pstl_count.h @@ -44,9 +44,10 @@ _LIBCPP_HIDE_FROM_ABI __iter_diff_t<_ForwardIterator> count_if(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Predicate __pred) { using __diff_t = __iter_diff_t<_ForwardIterator>; - return std::__pstl_frontend_dispatch( + auto __res = std::__pstl_frontend_dispatch( _LIBCPP_PSTL_CUSTOMIZATION_POINT(__pstl_count_if), - [&](_ForwardIterator __g_first, _ForwardIterator __g_last, _Predicate __g_pred) { + [&](_ForwardIterator __g_first, _ForwardIterator __g_last, _Predicate __g_pred) + -> optional<__iter_diff_t<_ForwardIterator>> { return std::transform_reduce( __policy, std::move(__g_first), @@ -58,6 +59,9 @@ std::move(__first), std::move(__last), std::move(__pred)); + if (!__res) + std::__throw_bad_alloc(); + return *std::move(__res); } template @@ -70,9 +74,10 @@ enable_if_t, int> = 0> _LIBCPP_HIDE_FROM_ABI __iter_diff_t<_ForwardIterator> count(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last, const _Tp& __value) { - return std::__pstl_frontend_dispatch( + auto __res = std::__pstl_frontend_dispatch( _LIBCPP_PSTL_CUSTOMIZATION_POINT(__pstl_count), - [&](_ForwardIterator __g_first, _ForwardIterator __g_last, const _Tp& __g_value) { + [&](_ForwardIterator __g_first, _ForwardIterator __g_last, const _Tp& __g_value) + -> optional<__iter_diff_t<_ForwardIterator>> { return std::count_if(__policy, __g_first, __g_last, [&](__iter_reference<_ForwardIterator> __v) { return __v == __g_value; }); @@ -80,6 +85,9 @@ std::move(__first), std::move(__last), __value); + if (!__res) + std::__throw_bad_alloc(); + return *__res; } _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__algorithm/pstl_fill.h b/libcxx/include/__algorithm/pstl_fill.h --- a/libcxx/include/__algorithm/pstl_fill.h +++ b/libcxx/include/__algorithm/pstl_fill.h @@ -41,7 +41,7 @@ _LIBCPP_HIDE_FROM_ABI void fill(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last, const _Tp& __value) { _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator); - std::__pstl_frontend_dispatch( + auto __res = std::__pstl_frontend_dispatch( _LIBCPP_PSTL_CUSTOMIZATION_POINT(__pstl_fill), [&](_ForwardIterator __g_first, _ForwardIterator __g_last, const _Tp& __g_value) { std::for_each(__policy, __g_first, __g_last, [&](__iter_reference<_ForwardIterator> __element) { @@ -51,6 +51,8 @@ std::move(__first), std::move(__last), __value); + if (!__res) + std::__throw_bad_alloc(); } template @@ -65,17 +67,20 @@ _LIBCPP_HIDE_FROM_ABI void fill_n(_ExecutionPolicy&& __policy, _ForwardIterator __first, _SizeT __n, const _Tp& __value) { _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator); - std::__pstl_frontend_dispatch( + auto __res = std::__pstl_frontend_dispatch( _LIBCPP_PSTL_CUSTOMIZATION_POINT(__pstl_fill_n), [&](_ForwardIterator __g_first, _SizeT __g_n, const _Tp& __g_value) { if constexpr (__has_random_access_iterator_category_or_concept<_ForwardIterator>::value) std::fill(__policy, __g_first, __g_first + __g_n, __g_value); else std::fill_n(__g_first, __g_n, __g_value); + return optional<__empty>{__empty{}}; }, std::move(__first), __n, __value); + if (!__res) + std::__throw_bad_alloc(); } _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__algorithm/pstl_find.h b/libcxx/include/__algorithm/pstl_find.h --- a/libcxx/include/__algorithm/pstl_find.h +++ b/libcxx/include/__algorithm/pstl_find.h @@ -38,7 +38,10 @@ find_if(_ExecutionPolicy&&, _ForwardIterator __first, _ForwardIterator __last, _Predicate __pred) { _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator); using _Backend = typename __select_backend<_RawPolicy>::type; - return std::__pstl_find_if<_RawPolicy>(_Backend{}, std::move(__first), std::move(__last), std::move(__pred)); + auto __res = std::__pstl_find_if<_RawPolicy>(_Backend{}, std::move(__first), std::move(__last), std::move(__pred)); + if (!__res) + std::__throw_bad_alloc(); + return *std::move(__res); } template @@ -52,9 +55,10 @@ _LIBCPP_HIDE_FROM_ABI _ForwardIterator find_if_not(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Predicate __pred) { _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator); - return std::__pstl_frontend_dispatch( + auto __res = std::__pstl_frontend_dispatch( _LIBCPP_PSTL_CUSTOMIZATION_POINT(__pstl_find_if_not), - [&](_ForwardIterator __g_first, _ForwardIterator __g_last, _Predicate __g_pred) { + [&](_ForwardIterator&& __g_first, _ForwardIterator&& __g_last, _Predicate&& __g_pred) + -> optional<_ForwardIterator> { return std::find_if(__policy, __g_first, __g_last, [&](__iter_reference<_ForwardIterator> __value) { return !__g_pred(__value); }); @@ -62,6 +66,9 @@ std::move(__first), std::move(__last), std::move(__pred)); + if (!__res) + std::__throw_bad_alloc(); + return *std::move(__res); } template @@ -75,9 +82,9 @@ _LIBCPP_HIDE_FROM_ABI _ForwardIterator find(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last, const _Tp& __value) { _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator); - return std::__pstl_frontend_dispatch( + auto __res = std::__pstl_frontend_dispatch( _LIBCPP_PSTL_CUSTOMIZATION_POINT(__pstl_find), - [&](_ForwardIterator __g_first, _ForwardIterator __g_last, const _Tp& __g_value) { + [&](_ForwardIterator __g_first, _ForwardIterator __g_last, const _Tp& __g_value) -> optional<_ForwardIterator> { return std::find_if(__policy, __g_first, __g_last, [&](__iter_reference<_ForwardIterator> __element) { return __element == __g_value; }); @@ -85,6 +92,9 @@ std::move(__first), std::move(__last), __value); + if (!__res) + std::__throw_bad_alloc(); + return *std::move(__res); } _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__algorithm/pstl_for_each.h b/libcxx/include/__algorithm/pstl_for_each.h --- a/libcxx/include/__algorithm/pstl_for_each.h +++ b/libcxx/include/__algorithm/pstl_for_each.h @@ -40,7 +40,9 @@ for_each(_ExecutionPolicy&&, _ForwardIterator __first, _ForwardIterator __last, _Function __func) { _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator); using _Backend = typename __select_backend<_RawPolicy>::type; - std::__pstl_for_each<_RawPolicy>(_Backend{}, std::move(__first), std::move(__last), std::move(__func)); + auto __res = std::__pstl_for_each<_RawPolicy>(_Backend{}, std::move(__first), std::move(__last), std::move(__func)); + if (!__res) + std::__throw_bad_alloc(); } template @@ -55,18 +57,22 @@ _LIBCPP_HIDE_FROM_ABI void for_each_n(_ExecutionPolicy&& __policy, _ForwardIterator __first, _Size __size, _Function __func) { _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator); - return std::__pstl_frontend_dispatch( + auto __res = std::__pstl_frontend_dispatch( _LIBCPP_PSTL_CUSTOMIZATION_POINT(__pstl_for_each_n), - [&](_ForwardIterator __g_first, _Size __g_size, _Function __g_func) { + [&](_ForwardIterator __g_first, _Size __g_size, _Function __g_func) -> optional<__empty> { if constexpr (__has_random_access_iterator_category_or_concept<_ForwardIterator>::value) { std::for_each(__policy, std::move(__g_first), __g_first + __g_size, std::move(__g_func)); + return __empty{}; } else { std::for_each_n(std::move(__g_first), __g_size, std::move(__g_func)); + return __empty{}; } }, __first, __size, std::move(__func)); + if (!__res) + std::__throw_bad_alloc(); } _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__algorithm/pstl_merge.h b/libcxx/include/__algorithm/pstl_merge.h --- a/libcxx/include/__algorithm/pstl_merge.h +++ b/libcxx/include/__algorithm/pstl_merge.h @@ -41,7 +41,7 @@ _ForwardOutIterator __result, _Comp __comp = {}) { using _Backend = typename __select_backend<_RawPolicy>::type; - return std::__pstl_merge<_RawPolicy>( + auto __res = std::__pstl_merge<_RawPolicy>( _Backend{}, std::move(__first1), std::move(__last1), @@ -49,6 +49,9 @@ std::move(__last2), std::move(__result), std::move(__comp)); + if (!__res) + std::__throw_bad_alloc(); + return *std::move(__res); } _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__algorithm/pstl_replace.h b/libcxx/include/__algorithm/pstl_replace.h --- a/libcxx/include/__algorithm/pstl_replace.h +++ b/libcxx/include/__algorithm/pstl_replace.h @@ -42,18 +42,22 @@ _ForwardIterator __last, _Pred __pred, const _Tp& __new_value) { - std::__pstl_frontend_dispatch( + auto __res = std::__pstl_frontend_dispatch( _LIBCPP_PSTL_CUSTOMIZATION_POINT(__pstl_replace_if), - [&__policy](_ForwardIterator __g_first, _ForwardIterator __g_last, _Pred __g_pred, const _Tp& __g_new_value) { + [&__policy]( + _ForwardIterator&& __g_first, _ForwardIterator&& __g_last, _Pred&& __g_pred, const _Tp& __g_new_value) { std::for_each(__policy, __g_first, __g_last, [&](__iter_reference<_ForwardIterator> __element) { if (__g_pred(__element)) __element = __g_new_value; }); + return optional<__empty>{__empty{}}; }, std::move(__first), std::move(__last), std::move(__pred), __new_value); + if (!__res) + std::__throw_bad_alloc(); } template diff --git a/libcxx/include/__algorithm/pstl_sort.h b/libcxx/include/__algorithm/pstl_sort.h --- a/libcxx/include/__algorithm/pstl_sort.h +++ b/libcxx/include/__algorithm/pstl_sort.h @@ -37,14 +37,17 @@ enable_if_t, int> = 0> _LIBCPP_HIDE_FROM_ABI void sort(_ExecutionPolicy&& __policy, _RandomAccessIterator __first, _RandomAccessIterator __last, _Comp __comp) { - std::__pstl_frontend_dispatch( + auto __res = std::__pstl_frontend_dispatch( _LIBCPP_PSTL_CUSTOMIZATION_POINT(__pstl_sort), [&__policy](_RandomAccessIterator __g_first, _RandomAccessIterator __g_last, _Comp __g_comp) { std::stable_sort(__policy, std::move(__g_first), std::move(__g_last), std::move(__g_comp)); + return optional<__empty>{__empty{}}; }, std::move(__first), std::move(__last), std::move(__comp)); + if (!__res) + std::__throw_bad_alloc(); } template ::type; - std::__pstl_stable_sort<_RawPolicy>(_Backend{}, std::move(__first), std::move(__last), std::move(__comp)); + auto __res = + std::__pstl_stable_sort<_RawPolicy>(_Backend{}, std::move(__first), std::move(__last), std::move(__comp)); + if (!__res) + std::__throw_bad_alloc(); } _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__algorithm/pstl_transform.h b/libcxx/include/__algorithm/pstl_transform.h --- a/libcxx/include/__algorithm/pstl_transform.h +++ b/libcxx/include/__algorithm/pstl_transform.h @@ -42,8 +42,11 @@ _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardOutIterator); _LIBCPP_REQUIRE_CPP17_OUTPUT_ITERATOR(_ForwardOutIterator, decltype(__op(*__first))); using _Backend = typename __select_backend<_RawPolicy>::type; - return std::__pstl_transform<_RawPolicy>( + auto __res = std::__pstl_transform<_RawPolicy>( _Backend{}, std::move(__first), std::move(__last), std::move(__result), std::move(__op)); + if (!__res) + std::__throw_bad_alloc(); + return *std::move(__res); } template ::type; - return std::__pstl_transform<_RawPolicy>( + auto __res = std::__pstl_transform<_RawPolicy>( _Backend{}, std::move(__first1), std::move(__last1), std::move(__first2), std::move(__result), std::move(__op)); + if (!__res) + std::__throw_bad_alloc(); + return *std::move(__res); } _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__numeric/pstl_transform_reduce.h b/libcxx/include/__numeric/pstl_transform_reduce.h --- a/libcxx/include/__numeric/pstl_transform_reduce.h +++ b/libcxx/include/__numeric/pstl_transform_reduce.h @@ -43,7 +43,7 @@ _BinaryOperation1 __reduce, _BinaryOperation2 __transform) { using _Backend = typename __select_backend<_RawPolicy>::type; - return std::__pstl_transform_reduce<_RawPolicy>( + auto __res = std::__pstl_transform_reduce<_RawPolicy>( _Backend{}, std::move(__first1), std::move(__last1), @@ -51,6 +51,10 @@ std::move(__init), std::move(__reduce), std::move(__transform)); + + if (!__res) + std::__throw_bad_alloc(); + return *std::move(__res); } // This overload doesn't get a customization point because it's trivial to detect (through e.g. @@ -84,13 +88,16 @@ _BinaryOperation __reduce, _UnaryOperation __transform) { using _Backend = typename __select_backend<_RawPolicy>::type; - return std::__pstl_transform_reduce<_RawPolicy>( + auto __res = std::__pstl_transform_reduce<_RawPolicy>( _Backend{}, std::move(__first), std::move(__last), std::move(__init), std::move(__reduce), std::move(__transform)); + if (!__res) + std::__throw_bad_alloc(); + return *std::move(__res); } _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__utility/empty.h b/libcxx/include/__utility/empty.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__utility/empty.h @@ -0,0 +1,20 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___UTILITY_EMPTY_H +#define _LIBCPP___UTILITY_EMPTY_H + +#include <__config> + +_LIBCPP_BEGIN_NAMESPACE_STD + +struct __empty {}; + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___UTILITY_EMPTY_H diff --git a/libcxx/include/__utility/terminate_on_exception.h b/libcxx/include/__utility/terminate_on_exception.h --- a/libcxx/include/__utility/terminate_on_exception.h +++ b/libcxx/include/__utility/terminate_on_exception.h @@ -21,26 +21,11 @@ _LIBCPP_BEGIN_NAMESPACE_STD -# ifndef _LIBCPP_HAS_NO_EXCEPTIONS - template -_LIBCPP_HIDE_FROM_ABI auto __terminate_on_exception(_Func __func) { - try { +_LIBCPP_HIDE_FROM_ABI auto __terminate_on_exception(_Func __func) noexcept { return __func(); - } catch (...) { - std::terminate(); - } -} - -# else // _LIBCPP_HAS_NO_EXCEPTIONS - -template -_LIBCPP_HIDE_FROM_ABI auto __terminate_on_exception(_Func __func) { - return __func(); } -# endif // _LIBCPP_HAS_NO_EXCEPTIONS - _LIBCPP_END_NAMESPACE_STD #endif // _LIBCPP_STD_VER >= 17 diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/pstl.fill.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/pstl.fill.pass.cpp --- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/pstl.fill.pass.cpp +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/pstl.fill.pass.cpp @@ -24,6 +24,8 @@ #include "test_execution_policies.h" #include "test_iterators.h" +#include "check_assertion.h" + EXECUTION_POLICY_SFINAE_TEST(fill); static_assert(sfinae_test_fill); @@ -61,24 +63,8 @@ } }; -#ifndef TEST_HAS_NO_EXCEPTIONS -struct ThrowOnCopy { - ThrowOnCopy& operator=(const ThrowOnCopy&) { throw int{}; } -}; -#endif - int main(int, char**) { types::for_each(types::forward_iterator_list{}, TestIteratorWithPolicies{}); -#ifndef TEST_HAS_NO_EXCEPTIONS - std::set_terminate(terminate_successful); - ThrowOnCopy a[2]; - try { - (void)std::fill(std::execution::par, std::begin(a), std::end(a), ThrowOnCopy{}); - } catch (int) { - assert(false); - } -#endif - return 0; } diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/pstl.fill.terminate.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/pstl.fill.terminate.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/pstl.fill.terminate.pass.cpp @@ -0,0 +1,33 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14 + +// UNSUPPORTED: libcpp-has-no-incomplete-pstl + +// check that std::fill(ExecutionPolicy) terminates properly + +#include + +#include "check_assertion.h" +#include "test_execution_policies.h" + +#ifndef TEST_HAS_NO_EXCEPTIONS +struct ThrowOnCopy { + ThrowOnCopy& operator=(const ThrowOnCopy&) { throw int{}; } +}; +#endif + +int main(int, char**) { + ThrowOnCopy a[2]{}; + + test_execution_policies([&](auto&& policy) { + EXPECT_STD_TERMINATE([&] { (void)std::fill(policy, std::begin(a), std::end(a), ThrowOnCopy{}); }); + EXPECT_STD_TERMINATE([&] { (void)std::fill_n(policy, std::begin(a), std::size(a), ThrowOnCopy{}); }); + }); +} diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/pstl.fill_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/pstl.fill_n.pass.cpp --- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/pstl.fill_n.pass.cpp +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/pstl.fill_n.pass.cpp @@ -61,24 +61,8 @@ } }; -#ifndef TEST_HAS_NO_EXCEPTIONS -struct ThrowOnCopy { - ThrowOnCopy& operator=(const ThrowOnCopy&) { throw int{}; } -}; -#endif - int main(int, char**) { types::for_each(types::forward_iterator_list{}, TestIteratorWithPolicies{}); -#ifndef TEST_HAS_NO_EXCEPTIONS - std::set_terminate(terminate_successful); - ThrowOnCopy a[2]; - try { - (void)std::fill_n(std::execution::par, std::begin(a), std::size(a), ThrowOnCopy{}); - } catch (int) { - assert(false); - } -#endif - return 0; } diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/pstl.replace.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/pstl.replace.pass.cpp --- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/pstl.replace.pass.cpp +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/pstl.replace.pass.cpp @@ -73,24 +73,8 @@ } }; -struct ThrowOnCompare {}; - -#ifndef TEST_HAS_NO_EXCEPTIONS -bool operator==(ThrowOnCompare, ThrowOnCompare) { throw int{}; } -#endif - int main(int, char**) { types::for_each(types::forward_iterator_list{}, TestIteratorWithPolicies{}); -#ifndef TEST_HAS_NO_EXCEPTIONS - std::set_terminate(terminate_successful); - ThrowOnCompare a[2]; - try { - (void)std::replace(std::execution::par, std::begin(a), std::end(a), ThrowOnCompare{}, ThrowOnCompare{}); - } catch (int) { - assert(false); - } -#endif - return 0; } diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/pstl.replace.terminate.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/pstl.replace.terminate.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/pstl.replace.terminate.pass.cpp @@ -0,0 +1,47 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14 +// UNSUPPORTED: libcpp-has-no-incomplete-pstl + +#include + +#include "check_assertion.h" +#include "test_execution_policies.h" + +struct ThrowOnCompare {}; + +#ifndef TEST_HAS_NO_EXCEPTIONS +bool operator==(ThrowOnCompare, ThrowOnCompare) { throw int{}; } +#endif + +int main(int, char**) { + ThrowOnCompare a[2]{}; + + test_execution_policies([&](auto&& policy) { + EXPECT_STD_TERMINATE([&] { + (void)std::replace(policy, std::begin(a), std::end(a), ThrowOnCompare{}, ThrowOnCompare{}); + }); + EXPECT_STD_TERMINATE([&] { + (void)std::replace_if( + policy, std::begin(a), std::end(a), [](ThrowOnCompare&) -> bool { throw int{}; }, ThrowOnCompare{}); + }); + EXPECT_STD_TERMINATE([&] { + (void)std::replace_copy(policy, std::begin(a), std::end(a), std::begin(a), ThrowOnCompare{}, ThrowOnCompare{}); + }); + EXPECT_STD_TERMINATE([&] { + (void)std::replace_copy_if( + policy, + std::begin(a), + std::end(a), + std::begin(a), + [](ThrowOnCompare& i) { return i == i; }, + ThrowOnCompare{}); + }); + }); +} diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/pstl.replace_copy.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/pstl.replace_copy.pass.cpp --- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/pstl.replace_copy.pass.cpp +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/pstl.replace_copy.pass.cpp @@ -82,25 +82,8 @@ } }; -struct ThrowOnCompare {}; - -#ifndef TEST_HAS_NO_EXCEPTIONS -bool operator==(ThrowOnCompare, ThrowOnCompare) { throw int{}; } -#endif - int main(int, char**) { types::for_each(types::forward_iterator_list{}, TestIteratorWithPolicies{}); -#ifndef TEST_HAS_NO_EXCEPTIONS - std::set_terminate(terminate_successful); - ThrowOnCompare a[2]; - try { - (void)std::replace_copy( - std::execution::par, std::begin(a), std::end(a), std::begin(a), ThrowOnCompare{}, ThrowOnCompare{}); - } catch (int) { - assert(false); - } -#endif - return 0; } diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/pstl.replace_copy_if.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/pstl.replace_copy_if.pass.cpp --- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/pstl.replace_copy_if.pass.cpp +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/pstl.replace_copy_if.pass.cpp @@ -95,30 +95,8 @@ } }; -struct ThrowOnCompare {}; - -#ifndef TEST_HAS_NO_EXCEPTIONS -bool operator==(ThrowOnCompare, ThrowOnCompare) { throw int{}; } -#endif - int main(int, char**) { types::for_each(types::forward_iterator_list{}, TestIteratorWithPolicies{}); -#ifndef TEST_HAS_NO_EXCEPTIONS - std::set_terminate(terminate_successful); - ThrowOnCompare a[2]; - try { - (void)std::replace_copy_if( - std::execution::par, - std::begin(a), - std::end(a), - std::begin(a), - [](ThrowOnCompare& i) { return i == i; }, - ThrowOnCompare{}); - } catch (int) { - assert(false); - } -#endif - return 0; } diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/pstl.replace_if.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/pstl.replace_if.pass.cpp --- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/pstl.replace_if.pass.cpp +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/pstl.replace_if.pass.cpp @@ -78,25 +78,8 @@ } }; -struct ThrowOnCompare {}; - -#ifndef TEST_HAS_NO_EXCEPTIONS -bool operator==(ThrowOnCompare, ThrowOnCompare) { throw int{}; } -#endif - int main(int, char**) { types::for_each(types::forward_iterator_list{}, TestIteratorWithPolicies{}); -#ifndef TEST_HAS_NO_EXCEPTIONS - std::set_terminate(terminate_successful); - ThrowOnCompare a[2]; - try { - (void)std::replace_if( - std::execution::par, std::begin(a), std::end(a), [](ThrowOnCompare&) { return false; }, ThrowOnCompare{}); - } catch (int) { - assert(false); - } -#endif - return 0; } diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.transform/pstl.transform.terminate.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.transform/pstl.transform.terminate.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.transform/pstl.transform.terminate.pass.cpp @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14 +// UNSUPPORTED: libcpp-has-no-incomplete-pstl + +#include + +#include "check_assertion.h" +#include "test_execution_policies.h" + +int main(int, char**) { + int a[2]{}; + int b[2]{}; + int c[2]{}; + + test_execution_policies([&](auto&& policy) { + EXPECT_STD_TERMINATE([&] { + (void)std::transform( + policy, std::begin(a), std::end(a), std::begin(b), std::begin(c), [](auto v, auto) -> decltype(v) { + throw int{}; + }); + }); + EXPECT_STD_TERMINATE([&] { + (void)std::transform(policy, std::begin(a), std::end(a), std::begin(b), [](auto v) -> decltype(v) { + throw int{}; + }); + }); + }); +} diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.all_of/pstl.all_of.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.all_of/pstl.all_of.pass.cpp --- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.all_of/pstl.all_of.pass.cpp +++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.all_of/pstl.all_of.pass.cpp @@ -69,15 +69,5 @@ int main(int, char**) { types::for_each(types::forward_iterator_list{}, TestIteratorWithPolicies{}); -#ifndef TEST_HAS_NO_EXCEPTIONS - std::set_terminate(terminate_successful); - int a[] = {1, 2}; - try { - (void)std::all_of(std::execution::par, std::begin(a), std::end(a), [](int i) -> bool { throw i; }); - } catch (int) { - assert(false); - } -#endif - return 0; } diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.all_of/pstl.all_of.terminate.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.all_of/pstl.all_of.terminate.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.all_of/pstl.all_of.terminate.pass.cpp @@ -0,0 +1,23 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14 + +// UNSUPPORTED: libcpp-has-no-incomplete-pstl + +#include + +#include "check_assertion.h" + +int main(int, char**) { + int a[] = {1, 2}; + + EXPECT_STD_TERMINATE([&] { + (void)std::all_of(std::execution::par, std::begin(a), std::end(a), [](int i) -> bool { throw i; }); + }); +} diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.any_of/pstl.any_of.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.any_of/pstl.any_of.pass.cpp --- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.any_of/pstl.any_of.pass.cpp +++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.any_of/pstl.any_of.pass.cpp @@ -63,15 +63,5 @@ int main(int, char**) { types::for_each(types::forward_iterator_list{}, TestIteratorWithPolicies{}); -#ifndef TEST_HAS_NO_EXCEPTIONS - std::set_terminate(terminate_successful); - int a[] = {1, 2}; - try { - (void)std::any_of(std::execution::par, std::begin(a), std::end(a), [](int i) -> bool { throw i; }); - } catch (int) { - assert(false); - } -#endif - return 0; } diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.any_of/pstl.any_of.terminate.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.any_of/pstl.any_of.terminate.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.any_of/pstl.any_of.terminate.pass.cpp @@ -0,0 +1,25 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14 + +// UNSUPPORTED: libcpp-has-no-incomplete-pstl + +#include + +#include "check_assertion.h" +#include "test_execution_policies.h" + +int main(int, char**) { + test_execution_policies([](auto&& policy) { + EXPECT_STD_TERMINATE([&] { + int a[] = {1, 2}; + (void)std::any_of(policy, std::begin(a), std::end(a), [](int i) -> bool { throw i; }); + }); + }); +} diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/pstl.find.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/pstl.find.pass.cpp --- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/pstl.find.pass.cpp +++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/pstl.find.pass.cpp @@ -61,24 +61,8 @@ } }; -struct ThrowOnCompare {}; - -#ifndef TEST_HAS_NO_EXCEPTIONS -bool operator==(ThrowOnCompare, ThrowOnCompare) { throw int{}; } -#endif - int main(int, char**) { types::for_each(types::forward_iterator_list{}, TestIteratorWithPolicies{}); -#ifndef TEST_HAS_NO_EXCEPTIONS - std::set_terminate(terminate_successful); - ThrowOnCompare a[2]; - try { - (void)std::find(std::execution::par, std::begin(a), std::end(a), ThrowOnCompare{}); - } catch (int) { - assert(false); - } -#endif - return 0; } diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/pstl.find.terminate.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/pstl.find.terminate.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/pstl.find.terminate.pass.cpp @@ -0,0 +1,41 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14 + +// UNSUPPORTED: libcpp-has-no-incomplete-pstl + +#include + +#include "check_assertion.h" +#include "test_execution_policies.h" + +struct ThrowOnCompare {}; + +#ifndef TEST_HAS_NO_EXCEPTIONS +bool operator==(ThrowOnCompare, ThrowOnCompare) { throw int{}; } +#endif + +int main(int, char**) { + test_execution_policies([](auto&& policy) { + EXPECT_STD_TERMINATE([&] { + int a[] = {1, 2}; + (void)std::find_if(policy, std::begin(a), std::end(a), [](int) -> bool { throw int{}; }); + }); + + EXPECT_STD_TERMINATE([&] { + ThrowOnCompare a[2] = {}; + (void)std::find(policy, std::begin(a), std::end(a), ThrowOnCompare{}); + }); + + EXPECT_STD_TERMINATE([&] { + int a[] = {1, 2}; + (void)std::find_if_not(policy, std::begin(a), std::end(a), [](int) -> bool { throw int{}; }); + }); + }); +} diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/pstl.find_if.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/pstl.find_if.pass.cpp --- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/pstl.find_if.pass.cpp +++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/pstl.find_if.pass.cpp @@ -70,15 +70,5 @@ int main(int, char**) { types::for_each(types::forward_iterator_list{}, TestIteratorWithPolicies{}); -#ifndef TEST_HAS_NO_EXCEPTIONS - std::set_terminate(terminate_successful); - int a[] = {1, 2}; - try { - (void)std::find_if(std::execution::par, std::begin(a), std::end(a), [](int) -> bool { throw int{}; }); - } catch (int) { - assert(false); - } -#endif - return 0; } diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/pstl.find_if_not.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/pstl.find_if_not.pass.cpp --- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/pstl.find_if_not.pass.cpp +++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/pstl.find_if_not.pass.cpp @@ -71,15 +71,5 @@ int main(int, char**) { types::for_each(types::forward_iterator_list{}, TestIteratorWithPolicies{}); -#ifndef TEST_HAS_NO_EXCEPTIONS - std::set_terminate(terminate_successful); - int a[] = {1, 2}; - try { - (void)std::find_if_not(std::execution::par, std::begin(a), std::end(a), [](int) -> bool { throw int{}; }); - } catch (int) { - assert(false); - } -#endif - return 0; } diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/pstl.for_each.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/pstl.for_each.pass.cpp --- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/pstl.for_each.pass.cpp +++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/pstl.for_each.pass.cpp @@ -51,15 +51,5 @@ int main(int, char**) { types::for_each(types::forward_iterator_list{}, TestIteratorWithPolicies{}); -#ifndef TEST_HAS_NO_EXCEPTIONS - std::set_terminate(terminate_successful); - int a[] = {1, 2}; - try { - std::for_each(std::execution::par, std::begin(a), std::end(a), [](int) { throw int{}; }); - } catch (int) { - assert(false); - } -#endif - return 0; } diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/pstl.for_each.terminate.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/pstl.for_each.terminate.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/pstl.for_each.terminate.pass.cpp @@ -0,0 +1,24 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14 + +// UNSUPPORTED: libcpp-has-no-incomplete-pstl + +#include + +#include "check_assertion.h" +#include "test_execution_policies.h" + +int main(int, char**) { + test_execution_policies([](auto&& policy) { + int a[] = {1, 2}; + EXPECT_STD_TERMINATE([&] { std::for_each(policy, std::begin(a), std::end(a), [](int) { throw int{}; }); }); + EXPECT_STD_TERMINATE([&] { std::for_each_n(policy, std::data(a), std::size(a), [](int) { throw int{}; }); }); + }); +} diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/pstl.for_each_n.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/pstl.for_each_n.pass.cpp --- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/pstl.for_each_n.pass.cpp +++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/pstl.for_each_n.pass.cpp @@ -50,15 +50,5 @@ int main(int, char**) { types::for_each(types::forward_iterator_list{}, TestIteratorWithPolicies{}); -#ifndef TEST_HAS_NO_EXCEPTIONS - std::set_terminate(terminate_successful); - int a[] = {1, 2}; - try { - std::for_each_n(std::execution::par, std::data(a), std::size(a), [](int) { throw int{}; }); - } catch (int) { - assert(false); - } -#endif - return 0; } diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.none_of/pstl.none_of.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.none_of/pstl.none_of.pass.cpp --- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.none_of/pstl.none_of.pass.cpp +++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.none_of/pstl.none_of.pass.cpp @@ -63,15 +63,5 @@ int main(int, char**) { types::for_each(types::forward_iterator_list{}, TestIteratorWithPolicies{}); -#ifndef TEST_HAS_NO_EXCEPTIONS - std::set_terminate(terminate_successful); - int a[] = {1, 2}; - try { - (void)std::none_of(std::execution::par, std::begin(a), std::end(a), [](int i) -> bool { throw i; }); - } catch (int) { - assert(false); - } -#endif - return 0; } diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.none_of/pstl.none_of.terminate.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.none_of/pstl.none_of.terminate.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.none_of/pstl.none_of.terminate.pass.cpp @@ -0,0 +1,25 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14 + +// UNSUPPORTED: libcpp-has-no-incomplete-pstl + +#include + +#include "check_assertion.h" +#include "test_execution_policies.h" + +int main(int, char**) { + test_execution_policies([](auto&& policy) { + EXPECT_STD_TERMINATE([&] { + int a[] = {1, 2}; + (void)std::none_of(policy, std::begin(a), std::end(a), [](int i) -> bool { throw i; }); + }); + }); +} diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.merge/pstl.merge.terminate.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.merge/pstl.merge.terminate.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/algorithms/alg.sorting/alg.merge/pstl.merge.terminate.pass.cpp @@ -0,0 +1,29 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14 + +// UNSUPPORTED: libcpp-has-no-incomplete-pstl + +#include + +#include "check_assertion.h" +#include "test_execution_policies.h" + +int main(int, char**) { + test_execution_policies([](auto&& policy) { + EXPECT_STD_TERMINATE([&] { + int a[] = {1, 2}; + std::merge(policy, std::begin(a), std::end(a), std::begin(a), std::end(a), std::begin(a), [](int, int) -> bool { + throw int{}; + }); + }); + }); + + return 0; +} diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/pstl.stable_sort.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/pstl.stable_sort.pass.cpp --- a/libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/pstl.stable_sort.pass.cpp +++ b/libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/pstl.stable_sort.pass.cpp @@ -129,15 +129,5 @@ int main(int, char**) { types::for_each(types::random_access_iterator_list{}, TestIteratorWithPolicies{}); -#ifndef TEST_HAS_NO_EXCEPTIONS - std::set_terminate(terminate_successful); - int a[] = {1, 2}; - try { - std::stable_sort(std::execution::par, std::begin(a), std::end(a), [](int, int) -> bool { throw int{}; }); - } catch (int) { - assert(false); - } -#endif - return 0; } diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/pstl.stable_sort.terminate.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/pstl.stable_sort.terminate.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/pstl.stable_sort.terminate.pass.cpp @@ -0,0 +1,25 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14 + +// UNSUPPORTED: libcpp-has-no-incomplete-pstl + +#include + +#include "check_assertion.h" +#include "test_execution_policies.h" + +int main(int, char**) { + test_execution_policies([](auto&& policy) { + EXPECT_STD_TERMINATE([&] { + int a[] = {1, 2}; + std::stable_sort(policy, std::begin(a), std::end(a), [](int, int) -> bool { throw int{}; }); + }); + }); +} diff --git a/libcxx/test/support/check_assertion.h b/libcxx/test/support/check_assertion.h --- a/libcxx/test/support/check_assertion.h +++ b/libcxx/test/support/check_assertion.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -24,10 +25,6 @@ #include "test_macros.h" #include "test_allocator.h" -#ifndef _LIBCPP_VERSION -# error "This header may only be used for libc++ tests" -#endif - #if TEST_STD_VER < 11 # error "C++11 or greater is required to use this header" #endif @@ -111,7 +108,7 @@ struct DeathTest { enum ResultKind { - RK_DidNotDie, RK_MatchFound, RK_MatchFailure, RK_SetupFailure, RK_Unknown + RK_DidNotDie, RK_MatchFound, RK_MatchFailure, RK_Terminate, RK_SetupFailure, RK_Unknown }; static const char* ResultKindToString(ResultKind RK) { @@ -122,6 +119,7 @@ CASE(RK_SetupFailure); CASE(RK_MatchFound); CASE(RK_Unknown); + CASE(RK_Terminate); } return "not a result kind"; } @@ -236,6 +234,7 @@ std::string stderr_from_child_; }; +#ifdef _LIBCPP_VERSION void std::__libcpp_verbose_abort(char const* format, ...) { // Extract information from the error message. This has to stay synchronized with // how we format assertions in the library. @@ -252,9 +251,15 @@ } std::exit(DeathTest::RK_MatchFailure); } +#endif // _LIBCPP_VERSION + +[[noreturn]] inline void terminate_handler() { + std::exit(DeathTest::RK_Terminate); +} template inline bool ExpectDeath(const char* stmt, Func&& func, AssertionInfoMatcher Matcher) { + std::set_terminate(terminate_handler); DeathTest DT(Matcher); DeathTest::ResultKind RK = DT.Run(func); auto OnFailure = [&](const char* msg) { @@ -272,6 +277,7 @@ }; switch (RK) { case DeathTest::RK_MatchFound: + case DeathTest::RK_Terminate: return true; case DeathTest::RK_SetupFailure: return OnFailure("child failed to setup test environment"); @@ -293,6 +299,8 @@ /// Assert that the specified expression throws a libc++ debug exception. #define EXPECT_DEATH(...) assert((ExpectDeath(#__VA_ARGS__, [&]() { __VA_ARGS__; } ))) +#define EXPECT_STD_TERMINATE(...) assert(ExpectDeath(#__VA_ARGS__, __VA_ARGS__)) + #define EXPECT_DEATH_MATCHES(Matcher, ...) assert((ExpectDeath(#__VA_ARGS__, [&]() { __VA_ARGS__; }, Matcher))) #define TEST_LIBCPP_ASSERT_FAILURE(expr, message) assert((ExpectDeath(#expr, [&]() { (void)(expr); }, AssertionInfoMatcher(message)))) diff --git a/libcxx/test/support/test_execution_policies.h b/libcxx/test/support/test_execution_policies.h --- a/libcxx/test/support/test_execution_policies.h +++ b/libcxx/test/support/test_execution_policies.h @@ -58,8 +58,4 @@ } }; -#ifndef TEST_HAS_NO_EXCEPTIONS -[[noreturn]] inline void terminate_successful() { std::exit(0); } -#endif - #endif // TEST_SUPPORT_TEST_EXECUTION_POLICIES