/* * 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 #include #include namespace unifex { namespace _let_v { template using decayed_tuple = std::tuple...>; template struct _successor_receiver { struct type; }; template using successor_receiver = typename _successor_receiver::type; template struct _successor_receiver::type { using successor_receiver = type; Operation& op_; typename Operation::receiver_type& get_receiver() const noexcept { return op_.receiver_; } template void set_value(SuccessorValues&&... values) && noexcept { UNIFEX_TRY { // Taking by value here to force a copy on the offchance the value // objects lives in the operation state (e.g., just), in which // case the call to cleanup() would invalidate them. [this](auto... copies) { cleanup(); unifex::set_value( std::move(op_.receiver_), (decltype(copies) &&) copies...); } ((SuccessorValues&&) values...); } UNIFEX_CATCH (...) { unifex::set_error(std::move(op_.receiver_), std::current_exception()); } } void set_done() && noexcept { cleanup(); unifex::set_done(std::move(op_.receiver_)); } // Taking by value here to force a copy on the offchance the error // object lives in the operation state (e.g., just_error), in which // case the call to cleanup() would invalidate it. template void set_error(Error error) && noexcept { cleanup(); unifex::set_error(std::move(op_.receiver_), (Error &&) error); } private: template using successor_operation = typename Operation::template successor_operation; void cleanup() noexcept { unifex::deactivate_union_member>(op_.succOp_); op_.values_.template destruct>(); } template(typename CPO) (requires is_receiver_query_cpo_v) friend auto tag_invoke(CPO cpo, const successor_receiver& r) noexcept( is_nothrow_callable_v) -> callable_result_t { return std::move(cpo)(std::as_const(r.get_receiver())); } template friend void tag_invoke( tag_t, const successor_receiver& r, Func&& f) { std::invoke(f, r.get_receiver()); } }; template struct _predecessor_receiver { struct type; }; template using predecessor_receiver = typename _predecessor_receiver::type; template struct _predecessor_receiver::type { using predecessor_receiver = type; using receiver_type = typename Operation::receiver_type; template using successor_operation = typename Operation::template successor_operation; Operation& op_; receiver_type& get_receiver() const noexcept { return op_.receiver_; } template void set_value(Values&&... values) && noexcept { auto& op = op_; UNIFEX_TRY { scope_guard destroyPredOp = [&]() noexcept { unifex::deactivate_union_member(op.predOp_); }; auto& valueTuple = op.values_.template construct>((Values &&) values...); destroyPredOp.reset(); scope_guard destroyValues = [&]() noexcept { op.values_.template destruct>(); }; auto& succOp = unifex::activate_union_member_with>( op.succOp_, [&] { return unifex::connect( std::apply(std::move(op.func_), valueTuple), successor_receiver{op}); }); unifex::start(succOp); destroyValues.release(); } UNIFEX_CATCH (...) { unifex::set_error(std::move(op.receiver_), std::current_exception()); } } void set_done() && noexcept { auto& op = op_; unifex::deactivate_union_member(op.predOp_); unifex::set_done(std::move(op.receiver_)); } // Taking by value here to force a copy on the offchange the error // object lives in the operation state, in which case destroying the // predecessor operation state would invalidate it. template void set_error(Error error) && noexcept { auto& op = op_; unifex::deactivate_union_member(op.predOp_); unifex::set_error(std::move(op.receiver_), (Error &&) error); } template(typename CPO) (requires is_receiver_query_cpo_v) friend auto tag_invoke(CPO cpo, const predecessor_receiver& r) noexcept( is_nothrow_callable_v) -> callable_result_t { return std::move(cpo)(std::as_const(r.get_receiver())); } template friend void tag_invoke( tag_t, const predecessor_receiver& r, Func&& f) { std::invoke(f, r.get_receiver()); } }; template struct _op { struct type; }; template using operation = typename _op< Predecessor, SuccessorFactory, remove_cvref_t>::type; template struct _op::type { using operation = type; using receiver_type = Receiver; template using successor_type = std::invoke_result_t&...>; template using successor_operation = connect_result_t, successor_receiver>; friend predecessor_receiver; template friend struct _successor_receiver; template explicit type( Predecessor&& pred, SuccessorFactory2&& func, Receiver2&& receiver) : func_((SuccessorFactory2 &&) func), receiver_((Receiver2 &&) receiver) { unifex::activate_union_member_with(predOp_, [&] { return unifex::connect( (Predecessor &&) pred, predecessor_receiver{*this}); }); } ~type() { if (!started_) { unifex::deactivate_union_member(predOp_); } } void start() noexcept { started_ = true; unifex::start(predOp_.get()); } private: using predecessor_type = remove_cvref_t; UNIFEX_NO_UNIQUE_ADDRESS SuccessorFactory func_; UNIFEX_NO_UNIQUE_ADDRESS Receiver receiver_; UNIFEX_NO_UNIQUE_ADDRESS typename predecessor_type:: template value_types values_; union { manual_lifetime>> predOp_; typename predecessor_type::template value_types succOp_; }; bool started_ = false; }; template struct _sender { class type; }; template using sender = typename _sender< remove_cvref_t, remove_cvref_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; Predecessor pred_; SuccessorFactory func_; template using successor_type = std::invoke_result_t&...>; template