/* * 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 namespace unifex { // Forward-declaration for any_scheduler, defined in // namespace _any_sched { template struct _with { struct any_scheduler; struct any_scheduler_ref; }; template using any_scheduler = typename _with::any_scheduler; template using any_scheduler_ref = typename _with::any_scheduler_ref; } // _any_sched namespace _any { using _operation_state = any_unique_t(start)>; template struct _rec_ref_base; template struct _rec_ref_base> { #if defined(_MSC_VER) template using type = any_ref< tag_t)>, tag_t)>, tag_t)>, CPOs...>; #else template using type = any_ref< tag_t(set_value)>, tag_t(set_error)>, tag_t(set_done)>, CPOs...>; #endif }; template struct _rec_ref { struct type; }; template struct _rec_ref::type : _rec_ref_base::template type { template type(inplace_stop_token st, Op* op) : _rec_ref_base::template type(*op) , stoken_(st) {} private: friend inplace_stop_token tag_invoke(tag_t, const type& self) noexcept { return self.stoken_; } inplace_stop_token stoken_; }; template using _receiver_ref = typename _rec_ref::type; // For in-place constructing non-movable operation states. // Relies on C++17's guaranteed copy elision. template struct _rvo { Sender&& s; Receiver r; operator connect_result_t() { return connect((Sender &&) s, std::move(r)); } }; template _rvo(Sender&&, Receiver) -> _rvo; template struct _connect_fn { struct type; }; template struct _connect_fn::type { using _rec_ref_t = _receiver_ref; using type_erased_signature_t = _operation_state(this_&&, _rec_ref_t); template(typename Sender) (requires sender_to) friend _operation_state tag_invoke(const type&, Sender&& s, _rec_ref_t r) { using Op = connect_result_t; return _operation_state{std::in_place_type, _rvo{(Sender &&) s, std::move(r)}}; } #ifdef _MSC_VER // MSVC (_MSC_VER == 1927) doesn't seem to like the requires // clause here. Use SFINAE instead. template tag_invoke_result_t operator()(Self&& s, _rec_ref_t r) const { return tag_invoke(*this, (Self&&) s, std::move(r)); } #else template(typename Self) (requires tag_invocable) _operation_state operator()(Self&& s, _rec_ref_t r) const { return tag_invoke(*this, (Self&&) s, std::move(r)); } #endif }; template inline constexpr typename _connect_fn::type _connect{}; template struct _op_for { struct type; }; template using _operation_state_for = typename _op_for::type; template struct _op_for::type { template explicit type(Receiver r, Fn fn) : rec_((Receiver&&) r) , state_{fn({subscription_.subscribe(unifex::get_stop_token(rec_)), this})} {} void start() & noexcept { unifex::start(state_); } // This operation state also implements the receiver CPOs and forwards them // to the receiver after unsubscribing the stop token. template (typename CPO, typename... Args) (requires is_receiver_cpo_v AND is_callable_v) friend void tag_invoke(CPO cpo, type&& self, Args&&... args) noexcept(is_nothrow_callable_v) { self.subscription_.unsubscribe(); cpo(std::move(self).rec_, (Args&&) args...); } // Forward other receiver queries template (typename CPO) (requires is_receiver_query_cpo_v AND is_callable_v) friend auto tag_invoke(CPO cpo, const type& self) noexcept(is_nothrow_callable_v) -> callable_result_t { return std::move(cpo)(self.rec_); } UNIFEX_NO_UNIQUE_ADDRESS Receiver rec_; detail::inplace_stop_token_adapter_subscription> subscription_{}; _operation_state state_; }; template using _sender_base = any_unique_t<_connect>; template struct _sender { struct type; }; template struct _with { template struct _sender { struct type; }; template using any_sender_of = typename _sender::type; using any_scheduler = _any_sched::any_scheduler; using any_scheduler_ref = _any_sched::any_scheduler_ref; template using any_receiver_ref = _receiver_ref, Values...>; }; template template struct _with::_sender::type : private _sender_base, Values...> { template