/* * 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 namespace unifex { namespace _let_d { template struct _op { class type; }; template using operation_type = typename _op>::type; template struct _rcvr { class type; }; template using receiver_type = typename _rcvr>::type; template struct _frcvr { class type; }; template using final_receiver_type = typename _frcvr>::type; template struct _sndr { class type; }; template using _sender = typename _sndr::type; template class _rcvr::type { using operation = operation_type; using final_receiver = final_receiver_type; using final_sender_t = callable_result_t; 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); auto op = op_; // preserve pointer value. if constexpr ( is_nothrow_callable_v && is_nothrow_connectable_v) { op->startedOp_ = 0; unifex::deactivate_union_member(op->sourceOp_); unifex::activate_union_member_with(op->finalOp_, [&] { return unifex::connect(std::move(op->done_)(), final_receiver{op}); }); op->startedOp_ = 0 - 1; unifex::start(op->finalOp_.get()); } else { UNIFEX_TRY { op->startedOp_ = 0; unifex::deactivate_union_member(op->sourceOp_); unifex::activate_union_member_with(op->finalOp_, [&] { return unifex::connect(std::move(op->done_)(), final_receiver{op}); }); op->startedOp_ = 0 - 1; unifex::start(op->finalOp_.get()); } UNIFEX_CATCH (...) { unifex::set_error(std::move(op->receiver_), std::current_exception()); } } } template(typename Error) (requires receiver) void set_error(Error&& error) noexcept { UNIFEX_ASSERT(op_ != nullptr); unifex::set_error(std::move(op_->receiver_), (Error&&)error); } private: template(typename CPO, typename Self) (requires is_receiver_query_cpo_v AND same_as, type> AND is_callable_v) friend auto tag_invoke(CPO cpo, Self&& r) noexcept(is_nothrow_callable_v) -> callable_result_t { return std::move(cpo)(r.get_receiver()); } template friend void tag_invoke( tag_t, const type& r, VisitFunc&& func) noexcept(is_nothrow_callable_v< VisitFunc&, const Receiver&>) { func(r.get_receiver()); } const Receiver& get_receiver() const noexcept { UNIFEX_ASSERT(op_ != nullptr); return op_->receiver_; } operation* op_; }; template class _frcvr::type { using operation = operation_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 receiver) void set_error(Error&& error) noexcept { UNIFEX_ASSERT(op_ != nullptr); unifex::set_error(std::move(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 type& r) noexcept(is_nothrow_callable_v) -> callable_result_t { return std::move(cpo)(r.get_receiver()); } template friend void tag_invoke( tag_t, const type& r, VisitFunc&& func) noexcept(is_nothrow_callable_v< VisitFunc&, const Receiver&>) { func(r.get_receiver()); } const Receiver& get_receiver() const noexcept { UNIFEX_ASSERT(op_ != nullptr); return op_->receiver_; } operation* op_; }; template class _op::type { using source_receiver = receiver_type; using final_receiver = final_receiver_type; public: template explicit type(Source&& source, Done2&& done, Receiver2&& dest) noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_move_constructible_v && is_nothrow_connectable_v) : done_((Done2&&)done) , receiver_((Receiver2&&)dest) { unifex::activate_union_member_with(sourceOp_, [&] { return unifex::connect((Source&&)source, source_receiver{this}); }); startedOp_ = 0 + 1; } ~type() { if (startedOp_ < 0) { unifex::deactivate_union_member(finalOp_); } else if (startedOp_ > 0) { unifex::deactivate_union_member(sourceOp_); } startedOp_ = 0; } void start() & noexcept { unifex::start(sourceOp_.get()); } private: friend source_receiver; friend final_receiver; using source_op_t = connect_result_t; using final_sender_t = callable_result_t; using final_op_t = connect_result_t; UNIFEX_NO_UNIQUE_ADDRESS Done done_; UNIFEX_NO_UNIQUE_ADDRESS Receiver receiver_; int startedOp_ = 0; union { manual_lifetime sourceOp_; manual_lifetime finalOp_; }; }; template class _sndr::type { using final_sender_t = callable_result_t; public: template