Add threadId to coroutines
This commit is contained in:
parent
84c562f45d
commit
773b393d96
1 changed files with 427 additions and 1 deletions
|
@ -3,7 +3,433 @@
|
|||
|
||||
namespace cserver {
|
||||
|
||||
template <typename T>
|
||||
namespace this_coro {
|
||||
|
||||
struct ThreadIdGetter : std::suspend_never {
|
||||
std::size_t threadId;
|
||||
inline constexpr auto await_resume() -> std::size_t {
|
||||
return this->threadId;
|
||||
};
|
||||
};
|
||||
|
||||
inline constexpr auto kGetThreadId = ThreadIdGetter{};
|
||||
|
||||
struct SetThreadId : public std::suspend_never {
|
||||
std::size_t threadId;
|
||||
};
|
||||
|
||||
} // namespace this_coro
|
||||
|
||||
template <typename T = void>
|
||||
using Task = boost::asio::awaitable<T>;
|
||||
|
||||
template <typename...>
|
||||
struct TaskAwaitable {};
|
||||
|
||||
} // namespace cserver
|
||||
|
||||
namespace boost::asio::detail {
|
||||
/*
|
||||
Part of this code is taken from boost asio
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
template <>
|
||||
struct awaitable_frame_base<any_io_executor> {
|
||||
public:
|
||||
using Executor = any_io_executor;
|
||||
#if !defined(BOOST_ASIO_DISABLE_AWAITABLE_FRAME_RECYCLING)
|
||||
auto constexpr operator new(std::size_t size) -> void* {
|
||||
return boost::asio::detail::thread_info_base::allocate(
|
||||
boost::asio::detail::thread_info_base::awaitable_frame_tag(),
|
||||
boost::asio::detail::thread_context::top_of_thread_call_stack(),
|
||||
size);
|
||||
};
|
||||
|
||||
inline constexpr auto operator delete(void* pointer, std::size_t size) -> void {
|
||||
boost::asio::detail::thread_info_base::deallocate(
|
||||
boost::asio::detail::thread_info_base::awaitable_frame_tag(),
|
||||
boost::asio::detail::thread_context::top_of_thread_call_stack(),
|
||||
pointer, size);
|
||||
};
|
||||
#endif // !defined(BOOST_ASIO_DISABLE_AWAITABLE_FRAME_RECYCLING)
|
||||
|
||||
// The frame starts in a suspended state until the awaitable_thread object
|
||||
// pumps the stack.
|
||||
inline constexpr auto initial_suspend() noexcept {
|
||||
return suspend_always();
|
||||
};
|
||||
|
||||
// On final suspension the frame is popped from the top of the stack.
|
||||
inline constexpr auto final_suspend() noexcept {
|
||||
struct Result {
|
||||
awaitable_frame_base* this_;
|
||||
inline constexpr auto await_ready() const noexcept -> bool {
|
||||
return false;
|
||||
};
|
||||
|
||||
inline constexpr auto await_suspend(coroutine_handle<void>) noexcept -> void {
|
||||
this->this_->pop_frame();
|
||||
};
|
||||
|
||||
inline constexpr auto await_resume() const noexcept -> void {};
|
||||
};
|
||||
return Result{this};
|
||||
};
|
||||
|
||||
inline constexpr auto set_except(std::exception_ptr e) noexcept -> void {
|
||||
pending_exception_ = e;
|
||||
};
|
||||
|
||||
inline constexpr auto set_error(const boost::system::error_code& ec) -> void {
|
||||
this->set_except(std::make_exception_ptr(boost::system::system_error(ec)));
|
||||
};
|
||||
|
||||
inline constexpr auto unhandled_exception() -> void {
|
||||
set_except(std::current_exception());
|
||||
};
|
||||
|
||||
inline constexpr auto rethrow_exception() -> void {
|
||||
if(pending_exception_) {
|
||||
std::exception_ptr ex = std::exchange(pending_exception_, nullptr);
|
||||
std::rethrow_exception(ex);
|
||||
};
|
||||
};
|
||||
|
||||
inline constexpr auto clear_cancellation_slot() -> void {
|
||||
this->attached_thread_->entry_point()->cancellation_state_.slot().clear();
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline constexpr auto await_transform(awaitable<T, Executor> a) const -> awaitable<T, Executor> {
|
||||
if (attached_thread_->entry_point()->throw_if_cancelled_) {
|
||||
if (!!attached_thread_->get_cancellation_state().cancelled()) {
|
||||
throw_error(boost::asio::error::operation_aborted, "co_await");
|
||||
};
|
||||
};
|
||||
return a;
|
||||
};
|
||||
|
||||
template <typename Op>
|
||||
inline constexpr auto await_transform(Op&& op,
|
||||
constraint_t<is_async_operation<Op>::value> = 0
|
||||
#if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
|
||||
# if defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
|
||||
, detail::source_location location = detail::source_location::current()
|
||||
# endif // defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
|
||||
#endif // defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
|
||||
) {
|
||||
if (attached_thread_->entry_point()->throw_if_cancelled_) {
|
||||
if (!!attached_thread_->get_cancellation_state().cancelled()) {
|
||||
throw_error(boost::asio::error::operation_aborted, "co_await");
|
||||
};
|
||||
};
|
||||
|
||||
return awaitable_async_op<
|
||||
completion_signature_of_t<Op>, decay_t<Op>, Executor>{
|
||||
std::forward<Op>(op), this
|
||||
#if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
|
||||
# if defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
|
||||
, location
|
||||
# endif // defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
|
||||
#endif // defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
|
||||
};
|
||||
};
|
||||
|
||||
// This await transformation obtains the associated executor of the thread of
|
||||
// execution.
|
||||
inline constexpr auto await_transform(this_coro::executor_t) noexcept {
|
||||
struct Result {
|
||||
awaitable_frame_base* this_;
|
||||
|
||||
inline constexpr auto await_ready() const noexcept -> bool {
|
||||
return true;
|
||||
};
|
||||
|
||||
inline constexpr auto await_suspend(coroutine_handle<void>) noexcept -> void {};
|
||||
|
||||
inline constexpr auto await_resume() const noexcept {
|
||||
return this_->attached_thread_->get_executor();
|
||||
};
|
||||
};
|
||||
|
||||
return Result{this};
|
||||
};
|
||||
|
||||
// This await transformation obtains the associated cancellation state of the
|
||||
// thread of execution.
|
||||
inline constexpr auto await_transform(this_coro::cancellation_state_t) noexcept {
|
||||
struct Result {
|
||||
awaitable_frame_base* this_;
|
||||
|
||||
inline constexpr auto await_ready() const noexcept -> bool {
|
||||
return true;
|
||||
};
|
||||
|
||||
inline constexpr auto await_suspend(coroutine_handle<void>) noexcept -> void {};
|
||||
|
||||
auto await_resume() const noexcept {
|
||||
return this_->attached_thread_->get_cancellation_state();
|
||||
};
|
||||
};
|
||||
|
||||
return Result{this};
|
||||
};
|
||||
|
||||
// This await transformation resets the associated cancellation state.
|
||||
inline constexpr auto await_transform(this_coro::reset_cancellation_state_0_t) noexcept {
|
||||
struct Result {
|
||||
awaitable_frame_base* this_;
|
||||
|
||||
inline constexpr auto await_ready() const noexcept -> bool {
|
||||
return true;
|
||||
};
|
||||
|
||||
inline constexpr auto await_suspend(coroutine_handle<void>) noexcept -> void {};
|
||||
|
||||
inline constexpr auto await_resume() const {
|
||||
return this_->attached_thread_->reset_cancellation_state();
|
||||
};
|
||||
};
|
||||
|
||||
return Result{this};
|
||||
};
|
||||
|
||||
// This await transformation resets the associated cancellation state.
|
||||
template <typename Filter>
|
||||
inline constexpr auto await_transform(
|
||||
this_coro::reset_cancellation_state_1_t<Filter> reset) noexcept {
|
||||
struct Result {
|
||||
awaitable_frame_base* this_;
|
||||
Filter filter_;
|
||||
|
||||
inline constexpr auto await_ready() const noexcept -> bool {
|
||||
return true;
|
||||
};
|
||||
|
||||
inline constexpr auto await_suspend(coroutine_handle<void>) noexcept -> void {};
|
||||
|
||||
inline constexpr auto await_resume() {
|
||||
return this_->attached_thread_->reset_cancellation_state(
|
||||
static_cast<Filter&&>(filter_));
|
||||
};
|
||||
};
|
||||
|
||||
return Result{this, static_cast<Filter&&>(reset.filter)};
|
||||
}
|
||||
template <typename T>
|
||||
inline constexpr auto await_transform(cserver::TaskAwaitable<T> awaitable) -> cserver::TaskAwaitable<T> {
|
||||
return awaitable;
|
||||
};
|
||||
|
||||
// This await transformation resets the associated cancellation state.
|
||||
template <typename InFilter, typename OutFilter>
|
||||
inline constexpr auto await_transform(
|
||||
this_coro::reset_cancellation_state_2_t<InFilter, OutFilter> reset) noexcept {
|
||||
struct Result {
|
||||
awaitable_frame_base* this_;
|
||||
InFilter in_filter_;
|
||||
OutFilter out_filter_;
|
||||
|
||||
inline constexpr auto await_ready() const noexcept -> bool {
|
||||
return true;
|
||||
};
|
||||
|
||||
inline constexpr auto await_suspend(coroutine_handle<void>) noexcept -> void {};
|
||||
|
||||
inline constexpr auto await_resume() {
|
||||
return this_->attached_thread_->reset_cancellation_state(
|
||||
static_cast<InFilter&&>(in_filter_),
|
||||
static_cast<OutFilter&&>(out_filter_));
|
||||
};
|
||||
};
|
||||
|
||||
return Result{this,
|
||||
static_cast<InFilter&&>(reset.in_filter),
|
||||
static_cast<OutFilter&&>(reset.out_filter)};
|
||||
};
|
||||
|
||||
// This await transformation determines whether cancellation is propagated as
|
||||
// an exception.
|
||||
inline constexpr auto await_transform(this_coro::throw_if_cancelled_0_t) noexcept {
|
||||
struct Result {
|
||||
awaitable_frame_base* this_;
|
||||
|
||||
inline constexpr auto await_ready() const noexcept -> bool {
|
||||
return true;
|
||||
};
|
||||
|
||||
inline constexpr auto await_suspend(coroutine_handle<void>) noexcept -> void {};
|
||||
|
||||
inline constexpr auto await_resume() {
|
||||
return this_->attached_thread_->throw_if_cancelled();
|
||||
};
|
||||
};
|
||||
|
||||
return Result{this};
|
||||
}
|
||||
|
||||
// This await transformation sets whether cancellation is propagated as an
|
||||
// exception.
|
||||
inline constexpr auto await_transform(this_coro::throw_if_cancelled_1_t throw_if_cancelled) noexcept {
|
||||
struct Result {
|
||||
awaitable_frame_base* this_;
|
||||
bool value_;
|
||||
|
||||
inline constexpr auto await_ready() const noexcept -> bool {
|
||||
return true;
|
||||
};
|
||||
|
||||
inline constexpr auto await_suspend(coroutine_handle<void>) noexcept -> void {};
|
||||
|
||||
inline constexpr auto await_resume() {
|
||||
this_->attached_thread_->throw_if_cancelled(value_);
|
||||
};
|
||||
};
|
||||
|
||||
return Result{this, throw_if_cancelled.value};
|
||||
}
|
||||
|
||||
// This await transformation is used to run an async operation's initiation
|
||||
// function object after the coroutine has been suspended. This ensures that
|
||||
// immediate resumption of the coroutine in another thread does not cause a
|
||||
// race condition.
|
||||
template <typename Function>
|
||||
inline constexpr auto await_transform(Function f,
|
||||
enable_if_t<
|
||||
is_convertible<
|
||||
result_of_t<Function(awaitable_frame_base*)>,
|
||||
awaitable_thread<Executor>*
|
||||
>::value
|
||||
>* = nullptr) {
|
||||
struct Result {
|
||||
Function function_;
|
||||
awaitable_frame_base* this_;
|
||||
|
||||
inline constexpr auto await_ready() const noexcept -> bool {
|
||||
return false;
|
||||
};
|
||||
|
||||
inline constexpr auto await_suspend(coroutine_handle<void>) noexcept -> void {
|
||||
this_->after_suspend(
|
||||
[](void* arg)
|
||||
{
|
||||
Result* r = static_cast<Result*>(arg);
|
||||
r->function_(r->this_);
|
||||
}, this);
|
||||
};
|
||||
|
||||
inline constexpr auto await_resume() const noexcept -> void {};
|
||||
};
|
||||
|
||||
return Result{std::move(f), this};
|
||||
};
|
||||
|
||||
// Access the awaitable thread's has_context_switched_ flag.
|
||||
inline constexpr auto await_transform(detail::awaitable_thread_has_context_switched) noexcept {
|
||||
struct Result {
|
||||
awaitable_frame_base* this_;
|
||||
|
||||
inline constexpr auto await_ready() const noexcept -> bool {
|
||||
return true;
|
||||
};
|
||||
|
||||
inline constexpr auto await_suspend(coroutine_handle<void>) noexcept -> void {};
|
||||
|
||||
inline constexpr auto await_resume() const noexcept -> bool& {
|
||||
return this_->attached_thread_->entry_point()->has_context_switched_;
|
||||
};
|
||||
};
|
||||
|
||||
return Result{this};
|
||||
};
|
||||
|
||||
inline constexpr auto attach_thread(awaitable_thread<Executor>* handler) noexcept -> void {
|
||||
attached_thread_ = handler;
|
||||
};
|
||||
|
||||
inline constexpr auto detach_thread() noexcept ->awaitable_thread<Executor>* {
|
||||
attached_thread_->entry_point()->has_context_switched_ = true;
|
||||
return std::exchange(attached_thread_, nullptr);
|
||||
};
|
||||
|
||||
inline constexpr auto push_frame(awaitable_frame_base<Executor>* caller) noexcept -> void {
|
||||
this->threadId = caller->threadId;
|
||||
this->caller_ = caller;
|
||||
this->attached_thread_ = caller_->attached_thread_;
|
||||
this->attached_thread_->entry_point()->top_of_stack_ = this;
|
||||
this->caller_->attached_thread_ = nullptr;
|
||||
};
|
||||
|
||||
inline constexpr auto pop_frame() noexcept -> void {
|
||||
if (caller_) {
|
||||
caller_->attached_thread_ = attached_thread_;
|
||||
};
|
||||
attached_thread_->entry_point()->top_of_stack_ = caller_;
|
||||
attached_thread_ = nullptr;
|
||||
caller_ = nullptr;
|
||||
};
|
||||
|
||||
struct resume_context {
|
||||
void (*after_suspend_fn_)(void*) = nullptr;
|
||||
void *after_suspend_arg_ = nullptr;
|
||||
};
|
||||
|
||||
inline constexpr auto resume() -> void {
|
||||
resume_context context;
|
||||
resume_context_ = &context;
|
||||
coro_.resume();
|
||||
if (context.after_suspend_fn_) {
|
||||
context.after_suspend_fn_(context.after_suspend_arg_);
|
||||
};
|
||||
};
|
||||
|
||||
inline constexpr auto after_suspend(void (*fn)(void*), void* arg) -> void {
|
||||
resume_context_->after_suspend_fn_ = fn;
|
||||
resume_context_->after_suspend_arg_ = arg;
|
||||
};
|
||||
|
||||
inline constexpr auto destroy() -> void {
|
||||
coro_.destroy();
|
||||
};
|
||||
|
||||
inline constexpr auto await_transform(cserver::this_coro::ThreadIdGetter awaitable) const noexcept -> cserver::this_coro::ThreadIdGetter {
|
||||
awaitable.threadId = this->threadId;
|
||||
return awaitable;
|
||||
};
|
||||
inline constexpr auto await_transform(cserver::this_coro::SetThreadId awaitable) noexcept -> cserver::this_coro::SetThreadId {
|
||||
this->threadId = awaitable.threadId;
|
||||
return awaitable;
|
||||
};
|
||||
std::size_t threadId{};
|
||||
protected:
|
||||
coroutine_handle<void> coro_ = nullptr;
|
||||
awaitable_thread<Executor>* attached_thread_ = nullptr;
|
||||
awaitable_frame_base<Executor>* caller_ = nullptr;
|
||||
std::exception_ptr pending_exception_ = nullptr;
|
||||
resume_context* resume_context_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace boost::asio::detail
|
||||
|
|
Loading…
Reference in a new issue