[libcxx-commits] [PATCH] D154238: [libc++][PSTL] Move exception handling to the CPU backends

Louis Dionne via Phabricator via libcxx-commits libcxx-commits at lists.llvm.org
Wed Aug 9 09:27:35 PDT 2023


ldionne requested changes to this revision.
ldionne added inline comments.
This revision now requires changes to proceed.


================
Comment at: libcxx/include/__algorithm/pstl_backend.h:78
   template <class _ExecutionPolicy, class _Iterator, class _Size, class _Func>
   void __pstl_for_each_n(_Backend, _Iterator __first, _Size __n, _Func __f);
 
----------------
These should be updated for `optional<foo>`


================
Comment at: libcxx/include/__algorithm/pstl_backend.h:163
+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.
 */
----------------
`a disengaged optional`. Same below.


================
Comment at: libcxx/include/__algorithm/pstl_backends/cpu_backend.h:51
+
+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 throw
----------------
ldionne wrote:
> 
Actually, I think we want to say:

```
CPU backends are expected to report errors (i.e. failure to allocate) by returning a disengaged `optional` from their implementation. Exceptions shouldn't be used to report an internal failure-to-allocate, since all exceptions are turned into a program termination at the front-end level. When a backend returns a disengaged `optional` to the frontend, the frontend will turn that into a call to `std::__throw_bad_alloc();` to report the internal failure to the user.
```



================
Comment at: libcxx/include/__algorithm/pstl_backends/cpu_backends/find_if.h:105
+        [&__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);
----------------
Let's add an internal assertion here to document why `__pstl_find_if` will never return nullopt (the reason is that `unseq` isn't allowed to allocate, assuming that's true).

If that's not true, then this is a bug.


================
Comment at: libcxx/include/__algorithm/pstl_backends/cpu_backends/libdispatch.h:66
+_LIBCPP_HIDE_FROM_ABI optional<__empty>
+__parallel_for(_RandomAccessIterator __first, _RandomAccessIterator __last, _Functor __func) noexcept {
   auto __partitions = __libdispatch::__partition_chunks(__last - __first);
----------------
Per our discussion just now, we should actually remove `noexcept` here!


================
Comment at: libcxx/include/__algorithm/pstl_backends/cpu_backends/transform_reduce.h:166
+        [__transform, __reduce](auto __brick_first, auto __brick_last, _Tp __brick_init) {
+          return *std::__pstl_transform_reduce<__remove_parallel_policy_t<_ExecutionPolicy>>(
+              __cpu_backend_tag{},
----------------
Same, internal assertion.


================
Comment at: libcxx/include/__algorithm/pstl_for_each.h:58
 _LIBCPP_HIDE_FROM_ABI void
 for_each_n(_ExecutionPolicy&& __policy, _ForwardIterator __first, _Size __size, _Function __func) {
   _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator);
----------------
As discussed, it looks like we actually need to do this (otherwise we'd terminate in case `std::for_each(policy, ...)` throws `bad_alloc`):

```
template <class _ExecutionPolicy,
          class _ForwardIterator,
          class _Size,
          class _Function,
          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
_LIBCPP_HIDE_FROM_ABI void
__for_each_n(_ExecutionPolicy&& __policy, _ForwardIterator __first, _Size __size, _Function __func) noexcept {
  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator);
  return std::__pstl_frontend_dispatch(
      _LIBCPP_PSTL_CUSTOMIZATION_POINT(__pstl_for_each_n),
      [&](_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));
}

template <class _ExecutionPolicy,
          class _ForwardIterator,
          class _Size,
          class _Function,
          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
_LIBCPP_HIDE_FROM_ABI void
for_each_n(_ExecutionPolicy&& __policy, _ForwardIterator __first, _Size __size, _Function __func) {
  auto __res = std::__for_each_n(...);
  if (!__res)
    std::__throw_bad_alloc();
}
```

And then we should use the `noexcept` version from fallback implementations.


================
Comment at: libcxx/include/__utility/terminate_on_exception.h:25
 template <class _Func>
-_LIBCPP_HIDE_FROM_ABI auto __terminate_on_exception(_Func __func) {
-  try {
+_LIBCPP_HIDE_FROM_ABI auto __terminate_on_exception(_Func __func) noexcept {
     return __func();
----------------
I think this is just not needed at all anymore.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D154238/new/

https://reviews.llvm.org/D154238



More information about the libcxx-commits mailing list