//===----------------------------------------------------------------------===// // // Part of CUDA Experimental in CUDA C++ Core Libraries, // 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 // SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. // //===----------------------------------------------------------------------===// #pragma once #include #include #include #include #include namespace _retry_detail { namespace ex = ::cuda::experimental::execution; template using _copy_cvref_t = ::cuda::std::__copy_cvref_t; // _conv needed so we can emplace construct non-movable types into // a cuda::std::optional. template struct _conv { using result_type = decltype(::cuda::std::declval()()); operator result_type() && { return static_cast(f_)(); } F f_; }; template _conv(F) -> _conv; /////////////////////////////////////////////////////////////////////////////// // retry algorithm: template struct _opstate; // pass through all customizations except set_error, which retries the operation. template struct _retry_receiver { using receiver_concept = ex::receiver_t; template void set_value(Ts&&... ts) && noexcept { ex::set_value(::cuda::std::move(o_->r_), static_cast(ts)...); } template void set_error(Error&&) && noexcept { o_->_retry(); // This causes the op to be retried } void set_stopped() && noexcept { ex::set_stopped(static_cast(o_->r_)); } [[nodiscard]] auto get_env() const noexcept -> ex::env_of_t { return ex::get_env(o_->r_); } _opstate* o_; }; // Hold the nested operation state in an optional so we can // re-construct and re-start it if the operation fails. template struct _opstate { using operation_state_concept = ex::operation_state_t; using _nested_op_t = ex::connect_result_t>; explicit _opstate(S s, R r) : s_(static_cast(s)) , r_(static_cast(r)) , o_{_connect()} {} _opstate(_opstate&&) = delete; [[nodiscard]] auto _connect() noexcept { return _conv{[this] { return ex::connect(s_, _retry_receiver{this}); }}; } void _retry() noexcept { _CCCL_TRY { o_.emplace(_connect()); // potentially throwing ex::start(*o_); } _CCCL_CATCH_ALL { ex::set_error(static_cast(r_), ex::current_exception()); } } void start() & noexcept { ex::start(*o_); } private: friend struct _retry_receiver; S s_; R r_; ::cuda::std::optional<_nested_op_t> o_; }; struct _swallow_signature { template _CCCL_CONSTEVAL auto operator()() const noexcept { return ex::completion_signatures{}; } }; template struct _retry_sender { using sender_concept = ex::sender_t; explicit _retry_sender(S s) : s_(static_cast(s)) {} template static _CCCL_CONSTEVAL auto get_completion_signatures() { return ex::transform_completion_signatures( ex::get_child_completion_signatures(), {}, _swallow_signature{}, {}, ex::completion_signatures{}); } template [[nodiscard]] auto connect(R r) && -> _opstate { return _opstate{::cuda::std::move(*this).s_, ::cuda::std::move(r)}; } template [[nodiscard]] auto connect(R r) const& -> _opstate { return _opstate{s_, ::cuda::std::move(r)}; } auto get_env() const noexcept -> ex::env_of_t { return ex::get_env(s_); } private: S s_; }; } // namespace _retry_detail struct retry_t { template [[nodiscard]] auto operator()(S s) const -> _retry_detail::_retry_sender { return _retry_detail::_retry_sender{static_cast(s)}; } }; inline constexpr retry_t retry{};