/* * 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 _seq { template struct _op { class type; }; template using operation = typename _op< Predecessor, Successor, remove_cvref_t>::type; template struct _successor_receiver { class type; }; template using successor_receiver = typename _successor_receiver< Predecessor, Successor, remove_cvref_t>::type; template class _successor_receiver::type final { using successor_receiver = type; using operation_type = operation; public: explicit type(operation_type* op) noexcept : op_(op) {} type(type&& other) noexcept : op_(std::exchange(other.op_, nullptr)) {} private: template(typename CPO, typename R, typename... Args) (requires is_receiver_cpo_v AND same_as AND is_callable_v) friend auto tag_invoke( CPO cpo, R&& r, Args&&... args) noexcept(is_nothrow_callable_v< CPO, Receiver, Args...>) -> callable_result_t { return static_cast(cpo)( r.get_receiver_rvalue(), static_cast(args)...); } template(typename CPO, typename R) (requires is_receiver_query_cpo_v AND same_as AND is_callable_v) friend auto tag_invoke( CPO cpo, const R& r) noexcept(is_nothrow_callable_v< CPO, const Receiver&>) -> callable_result_t { return static_cast(cpo)(r.get_const_receiver()); } template friend void tag_invoke( tag_t, const successor_receiver& r, Func&& func) { std::invoke(func, r.get_const_receiver()); } Receiver&& get_receiver_rvalue() noexcept { return static_cast(op_->receiver_); } const Receiver& get_const_receiver() const noexcept { return op_->receiver_; } operation_type* op_; }; template struct _predecessor_receiver { class type; }; template using predecessor_receiver = typename _predecessor_receiver< Predecessor, Successor, remove_cvref_t>::type; template class _predecessor_receiver::type final { using predecessor_receiver = type; using operation_type = operation; public: explicit type(operation_type* op) noexcept : op_(op) {} type(type&& other) noexcept : op_(std::exchange(other.op_, nullptr)) {} void set_value() && noexcept { // Take a copy of op_ before destroying predOp_ as this may end up // destroying *this. using successor_receiver_t = successor_receiver; auto* op = op_; op->status_ = operation_type::status::empty; unifex::deactivate_union_member(op->predOp_); if constexpr (is_nothrow_connectable_v) { unifex::activate_union_member_with(op->succOp_, [&]() noexcept { return unifex::connect( static_cast(op->successor_), successor_receiver_t{op}); }); op->status_ = operation_type::status::successor_operation_constructed; unifex::start(op->succOp_.get()); } else { UNIFEX_TRY { unifex::activate_union_member_with(op->succOp_, [&] { return unifex::connect( static_cast(op->successor_), successor_receiver_t{op}); }); op->status_ = operation_type::status::successor_operation_constructed; unifex::start(op->succOp_.get()); } UNIFEX_CATCH (...) { unifex::set_error( static_cast(op->receiver_), std::current_exception()); } } } template(typename Error) (requires receiver) void set_error(Error&& error) && noexcept { unifex::set_error( static_cast(op_->receiver_), static_cast(error)); } void set_done() && noexcept { unifex::set_done(static_cast(op_->receiver_)); } private: template(typename CPO, typename R) (requires is_receiver_query_cpo_v AND same_as AND is_callable_v) friend auto tag_invoke( CPO cpo, const R& r) noexcept(is_nothrow_callable_v< CPO, const Receiver&>) -> callable_result_t { return static_cast(cpo)(r.get_const_receiver()); } template friend void tag_invoke( tag_t, const predecessor_receiver& r, Func&& func) { std::invoke(func, r.get_const_receiver()); } const Receiver& get_const_receiver() const noexcept { return op_->receiver_; } operation_type* op_; }; template class _op::type { using operation = type; public: template explicit type( Predecessor&& predecessor, Successor2&& successor, Receiver2&& receiver) : successor_(static_cast(successor)) , receiver_(static_cast(receiver)) , status_(status::predecessor_operation_constructed) { unifex::activate_union_member_with(predOp_, [&] { return unifex::connect( static_cast(predecessor), predecessor_receiver{this}); }); } ~type() { switch (status_) { case status::predecessor_operation_constructed: unifex::deactivate_union_member(predOp_); break; case status::successor_operation_constructed: unifex::deactivate_union_member(succOp_); break; case status::empty: break; } } void start() & noexcept { UNIFEX_ASSERT(status_ == status::predecessor_operation_constructed); unifex::start(predOp_.get()); } private: friend predecessor_receiver< Predecessor, Successor, Receiver>; friend successor_receiver< Predecessor, Successor, Receiver>; Successor successor_; Receiver receiver_; enum class status { empty, predecessor_operation_constructed, successor_operation_constructed }; status status_; union { manual_lifetime>> predOp_; manual_lifetime>> succOp_; }; }; template struct _sender { class type; }; template using sender = typename _sender< remove_cvref_t, remove_cvref_t>::type; template class _sender::type { using sender = type; public: template < template class Variant, template class Tuple> using value_types = sender_value_types_t; template