AimRT/install_x64/include/unifex/await_transform.hpp
2025-01-12 19:51:34 +08:00

237 lines
7.2 KiB
C++

/*
* 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 <unifex/coroutine.hpp>
#if UNIFEX_NO_COROUTINES
# error "Coroutine support is required to use <unifex/await_transform.hpp>"
#endif
#include <unifex/async_trace.hpp>
#include <unifex/coroutine_concepts.hpp>
#include <unifex/receiver_concepts.hpp>
#include <unifex/sender_concepts.hpp>
#include <unifex/type_traits.hpp>
#include <unifex/manual_lifetime.hpp>
#include <exception>
#include <optional>
#include <type_traits>
#include <unifex/detail/prologue.hpp>
namespace unifex {
namespace _util {
enum class _state { empty, value, exception, done };
template <typename Value>
struct _expected {
_expected() noexcept {}
void reset_value() noexcept {
_reset_value(std::exchange(state_, _state::empty));
}
~_expected() {
_reset_value(state_);
}
_state state_ = _state::empty;
union {
manual_lifetime<Value> value_;
manual_lifetime<std::exception_ptr> exception_;
};
private:
void _reset_value(_state s) noexcept {
switch(s) {
case _state::value:
unifex::deactivate_union_member(value_);
break;
case _state::exception:
unifex::deactivate_union_member(exception_);
break;
default:;
}
}
};
} // namespace _util
namespace _await_tfx {
using namespace _util;
template <typename Promise, typename Value>
struct _awaitable_base {
struct type;
};
template <typename Promise, typename Sender>
struct _awaitable {
struct type;
};
template <typename Promise, typename Value>
struct _awaitable_base<Promise, Value>::type {
struct _rec {
public:
explicit _rec(_expected<Value>* result, coro::coroutine_handle<Promise> continuation) noexcept
: result_(result)
, continuation_(continuation)
{}
_rec(_rec&& r) noexcept
: result_(std::exchange(r.result_, nullptr))
, continuation_(std::exchange(r.continuation_, nullptr))
{}
template(class... Us)
(requires (constructible_from<Value, Us...> ||
(std::is_void_v<Value> && sizeof...(Us) == 0)))
void set_value(Us&&... us) &&
noexcept(std::is_nothrow_constructible_v<Value, Us...> ||
std::is_void_v<Value>) {
unifex::activate_union_member(result_->value_, (Us&&) us...);
result_->state_ = _state::value;
continuation_.resume();
}
void set_error(std::exception_ptr eptr) && noexcept {
unifex::activate_union_member(result_->exception_, std::move(eptr));
result_->state_ = _state::exception;
continuation_.resume();
}
void set_done() && noexcept {
result_->state_ = _state::done;
continuation_.promise().unhandled_done().resume();
}
template(typename CPO)
(requires is_receiver_query_cpo_v<CPO> AND is_callable_v<CPO, const Promise&>)
friend auto tag_invoke(CPO cpo, const _rec& r)
noexcept(is_nothrow_callable_v<CPO, const Promise&>)
-> callable_result_t<CPO, const Promise&> {
const Promise& p = r.continuation_.promise();
return std::move(cpo)(p);
}
template <typename Func>
friend void
tag_invoke(tag_t<visit_continuations>, const _rec& r, Func&& func) {
visit_continuations(r.continuation_.promise(), (Func&&)func);
}
private:
_expected<Value>* result_;
coro::coroutine_handle<Promise> continuation_;
};
bool await_ready() const noexcept {
return false;
}
Value await_resume() {
switch (result_.state_) {
case _state::value:
return std::move(result_.value_).get();
default:
UNIFEX_ASSERT(result_.state_ == _state::exception);
std::rethrow_exception(result_.exception_.get());
}
}
protected:
_expected<Value> result_;
};
template <typename Promise, typename Sender>
using _awaitable_base_t =
typename _awaitable_base<
Promise,
sender_single_value_return_type_t<remove_cvref_t<Sender>>>::type;
template <typename Promise, typename Sender>
using _receiver_t = typename _awaitable_base_t<Promise, Sender>::_rec;
template <typename Promise, typename Sender>
struct _awaitable<Promise, Sender>::type
: _awaitable_base_t<Promise, Sender> {
private:
using _rec = _receiver_t<Promise, Sender>;
connect_result_t<Sender, _rec> op_;
public:
explicit type(Sender&& sender, coro::coroutine_handle<Promise> h)
noexcept(is_nothrow_connectable_v<Sender, _rec>)
: op_(unifex::connect((Sender&&)sender, _rec{&this->result_, h}))
{}
void await_suspend(coro::coroutine_handle<Promise>) noexcept {
unifex::start(op_);
}
};
template <typename Promise, typename Sender>
using _as_awaitable = typename _awaitable<Promise, Sender>::type;
inline const struct _fn {
// Call custom implementation if present.
template(typename Promise, typename Value)
(requires tag_invocable<_fn, Promise&, Value>)
auto operator()(Promise& promise, Value&& value) const
noexcept(is_nothrow_tag_invocable_v<_fn, Promise&, Value>)
-> tag_invoke_result_t<_fn, Promise&, Value> {
return unifex::tag_invoke(_fn{}, promise, (Value&&)value);
}
// Default implementation.
template(typename Promise, typename Value)
(requires (!tag_invocable<_fn, Promise&, Value>))
decltype(auto) operator()(Promise& promise, Value&& value) const {
// Note we don't fold the two '(Value&&) value'-returning cases here
// to avoid instantiating 'unifex::sender<Value>' concept check in
// the case that _awaitable<Value> evaluates to true.
if constexpr (detail::_awaitable<Value>) {
return (Value&&) value;
} else if constexpr (unifex::sender<Value>) {
if constexpr (unifex::sender_to<Value, _receiver_t<Promise, Value>>) {
auto h = coro::coroutine_handle<Promise>::from_promise(promise);
return _as_awaitable<Promise, Value>{(Value&&) value, h};
} else {
static_assert(
unifex::sender_to<Value, _receiver_t<Promise, Value>>,
"This sender is not awaitable in this coroutine type.");
return (Value&&) value;
}
} else {
return (Value&&) value;
}
}
} await_transform {};
} // namespace _await_tfx
// The await_transform() customisation point allows value-types to customise
// what kind of awaitable object should be used for this type when it is used
// within a co_await expression. It is similar to 'operator co_await()' but
// works around limitations of 'operator co_await()' by providing access to
// the promise object and promise type so that different awaitable types can
// be returned depending on the awaiting context.
//
// Coroutine promise_types can implement their .await_transform() methods to
// forward to this customisation point to enable use of type customisations.
using _await_tfx::await_transform;
} // namespace unifex
#include <unifex/detail/epilogue.hpp>