/* * 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 _sync_wait { template struct promise { promise() {} ~promise() { if (state_ == state::value) { unifex::deactivate_union_member(value_); } else if (state_ == state::error) { unifex::deactivate_union_member(exception_); } } union { manual_lifetime value_; manual_lifetime exception_; }; enum class state { incomplete, done, value, error }; state state_ = state::incomplete; }; template struct _receiver { struct type { promise& promise_; manual_event_loop& ctx_; template void set_value(Values&&... values) && noexcept { UNIFEX_TRY { unifex::activate_union_member(promise_.value_, (Values&&)values...); promise_.state_ = promise::state::value; } UNIFEX_CATCH (...) { unifex::activate_union_member(promise_.exception_, std::current_exception()); promise_.state_ = promise::state::error; } signal_complete(); } void set_error(std::exception_ptr err) && noexcept { unifex::activate_union_member(promise_.exception_, std::move(err)); promise_.state_ = promise::state::error; signal_complete(); } void set_error(std::error_code ec) && noexcept { std::move(*this).set_error(make_exception_ptr(std::system_error{ec, "sync_wait"})); } template void set_error(Error&& e) && noexcept { std::move(*this).set_error(make_exception_ptr((Error&&)e)); } void set_done() && noexcept { promise_.state_ = promise::state::done; signal_complete(); } friend auto tag_invoke(tag_t, const type& r) noexcept { return r.ctx_.get_scheduler(); } private: void signal_complete() noexcept { ctx_.stop(); } }; }; template using receiver_t = typename _receiver::type; template UNIFEX_CLANG_DISABLE_OPTIMIZATION std::optional _impl(Sender&& sender) { using promise_t = _sync_wait::promise; promise_t promise; manual_event_loop ctx; // Store state for the operation on the stack. auto operation = connect( (Sender&&)sender, _sync_wait::receiver_t{promise, ctx}); start(operation); ctx.run(); switch (promise.state_) { case promise_t::state::done: return std::nullopt; case promise_t::state::value: return std::move(promise.value_).get(); case promise_t::state::error: std::rethrow_exception(promise.exception_.get()); default: std::terminate(); } } } // namespace _sync_wait namespace _sync_wait_cpo { struct _fn { template(typename Sender) (requires typed_sender) auto operator()(Sender&& sender) const -> std::optional>> { using Result = sender_single_value_result_t>; return _sync_wait::_impl((Sender&&) sender); } constexpr auto operator()() const noexcept(is_nothrow_callable_v< tag_t, _fn>) -> bind_back_result_t<_fn> { return bind_back(*this); } }; } // namespace _sync_wait_cpo inline constexpr _sync_wait_cpo::_fn sync_wait {}; namespace _sync_wait_r_cpo { template struct _fn { template(typename Sender) (requires sender) decltype(auto) operator()(Sender&& sender) const { using Result2 = non_void_t>>; return _sync_wait::_impl((Sender&&) sender); } }; } // namespace _sync_wait_r_cpo template inline constexpr _sync_wait_r_cpo::_fn sync_wait_r {}; } // namespace unifex #include