/* * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License Version 2.0 with LLVM Exceptions * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * https://llvm.org/LICENSE.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include namespace unifex { namespace _retry_when { template struct _op { class type; }; template using operation = typename _op>::type; template struct _source_receiver { class type; }; template using source_receiver = typename _source_receiver::type; template struct _trigger_receiver { class type; }; template using trigger_receiver = typename _trigger_receiver::type; template class _trigger_receiver::type { using trigger_receiver = type; public: explicit type(operation* op) noexcept : op_(op) {} type(trigger_receiver&& other) noexcept : op_(std::exchange(other.op_, nullptr)) {} void set_value() && noexcept { UNIFEX_ASSERT(op_ != nullptr); // This signals to retry the operation. auto* const op = op_; destroy_trigger_op(); using source_receiver_t = source_receiver; if constexpr (is_nothrow_connectable_v) { auto& sourceOp = unifex::activate_union_member_with(op->sourceOp_, [&]() noexcept { return unifex::connect(op->source_, source_receiver_t{op}); }); op->isSourceOpConstructed_ = true; unifex::start(sourceOp); } else { UNIFEX_TRY { auto& sourceOp = unifex::activate_union_member_with(op->sourceOp_, [&] { return unifex::connect(op->source_, source_receiver_t{op}); }); op->isSourceOpConstructed_ = true; unifex::start(sourceOp); } UNIFEX_CATCH (...) { unifex::set_error((Receiver&&)op->receiver_, std::current_exception()); } } } template(typename R = Receiver) (requires receiver) void set_done() && noexcept { UNIFEX_ASSERT(op_ != nullptr); auto* const op = op_; destroy_trigger_op(); unifex::set_done((Receiver&&)op->receiver_); } template(typename Error) (requires receiver) void set_error(Error error) && noexcept { UNIFEX_ASSERT(op_ != nullptr); auto* const op = op_; // Note, parameter taken by value so that its lifetime will continue // to be valid after we destroy the operation-state that sent it. destroy_trigger_op(); unifex::set_error((Receiver&&)op->receiver_, (Error&&)error); } private: template(typename CPO) (requires is_receiver_query_cpo_v AND is_callable_v) friend auto tag_invoke(CPO cpo, const trigger_receiver& r) noexcept(is_nothrow_callable_v) -> callable_result_t { return std::move(cpo)(r.get_receiver()); } template friend void tag_invoke( tag_t, const trigger_receiver& r, VisitFunc&& func) noexcept(std::is_nothrow_invocable_v< VisitFunc&, const Receiver&>) { std::invoke(func, r.get_receiver()); } const Receiver& get_receiver() const noexcept { UNIFEX_ASSERT(op_ != nullptr); return op_->receiver_; } void destroy_trigger_op() noexcept { using trigger_op = connect_result_t; unifex::deactivate_union_member(op_->triggerOps_); } operation* op_; }; template class _source_receiver::type { using source_receiver = type; public: explicit type(operation* op) noexcept : op_(op) {} type(type&& other) noexcept : op_(std::exchange(other.op_, {})) {} template(typename... Values) (requires receiver_of) void set_value(Values&&... values) noexcept(is_nothrow_receiver_of_v) { UNIFEX_ASSERT(op_ != nullptr); unifex::set_value(std::move(op_->receiver_), (Values&&)values...); } void set_done() noexcept { UNIFEX_ASSERT(op_ != nullptr); unifex::set_done(std::move(op_->receiver_)); } template(typename Error) (requires std::is_invocable_v) void set_error(Error error) noexcept { UNIFEX_ASSERT(op_ != nullptr); auto* const op = op_; op->isSourceOpConstructed_ = false; unifex::deactivate_union_member(op->sourceOp_); using trigger_sender_t = std::invoke_result_t; using trigger_receiver_t = trigger_receiver; using trigger_op_t = unifex::connect_result_t; if constexpr (std::is_nothrow_invocable_v && is_nothrow_connectable_v) { auto& triggerOp = unifex::activate_union_member_with( op->triggerOps_, [&]() noexcept { return unifex::connect( std::invoke(op->func_, (Error&&)error), trigger_receiver_t{op}); }); unifex::start(triggerOp); } else { UNIFEX_TRY { auto& triggerOp = unifex::activate_union_member_with( op->triggerOps_, [&]() { return unifex::connect( std::invoke(op->func_, (Error&&)error), trigger_receiver_t{op}); }); unifex::start(triggerOp); } UNIFEX_CATCH (...) { unifex::set_error((Receiver&&)op->receiver_, std::current_exception()); } } } private: template(typename CPO) (requires is_receiver_query_cpo_v AND is_callable_v) friend auto tag_invoke(CPO cpo, const source_receiver& r) noexcept(is_nothrow_callable_v) -> callable_result_t { return std::move(cpo)(r.get_receiver()); } template friend void tag_invoke( tag_t, const source_receiver& r, VisitFunc&& func) noexcept(std::is_nothrow_invocable_v< VisitFunc&, const Receiver&>) { std::invoke(func, r.get_receiver()); } const Receiver& get_receiver() const noexcept { UNIFEX_ASSERT(op_ != nullptr); return op_->receiver_; } operation* op_; }; template class _op::type { using operation = type; using source_receiver_t = source_receiver; public: template explicit type(Source2&& source, Func2&& func, Receiver2&& receiver) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v && std::is_nothrow_constructible_v && is_nothrow_connectable_v) : source_((Source2&&)source) , func_((Func2&&)func) , receiver_((Receiver&&)receiver) { unifex::activate_union_member_with(sourceOp_, [&] { return unifex::connect(source_, source_receiver_t{this}); }); } ~type() { if (isSourceOpConstructed_) { unifex::deactivate_union_member(sourceOp_); } } void start() & noexcept { unifex::start(sourceOp_.get()); } private: friend source_receiver_t; template friend struct _trigger_receiver; using source_op_t = connect_result_t; template using trigger_sender_t = std::invoke_result_t>; template using trigger_receiver_t = trigger_receiver>; template using trigger_op_t = connect_result_t< trigger_sender_t, trigger_receiver_t>; template using trigger_op_union = manual_lifetime_union...>; UNIFEX_NO_UNIQUE_ADDRESS Source source_; UNIFEX_NO_UNIQUE_ADDRESS Func func_; UNIFEX_NO_UNIQUE_ADDRESS Receiver receiver_; bool isSourceOpConstructed_ = true; union { manual_lifetime sourceOp_; typename Source::template error_types triggerOps_; }; }; template struct _sender { class type; }; template using sender = typename _sender, std::decay_t>::type; template struct sends_done_impl : std::bool_constant::sends_done> {}; template using any_sends_done = std::disjunction...>; template class _sender::type { using sender = type; template using trigger_sender = std::invoke_result_t>; template using make_error_type_list = typename concat_type_lists_unique< sender_error_types_t, type_list>..., type_list>::type; template using sends_done_impl = any_sends_done...>; public: template