155 lines
3.6 KiB
C++
155 lines
3.6 KiB
C++
// Copyright (c) 2022 Klemens D. Morgenstern
|
|
//
|
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
#ifndef BOOST_COBALT_DETAIL_WITH_HPP
|
|
#define BOOST_COBALT_DETAIL_WITH_HPP
|
|
|
|
#include <boost/cobalt/concepts.hpp>
|
|
#include <boost/cobalt/this_coro.hpp>
|
|
|
|
#include <boost/asio/cancellation_signal.hpp>
|
|
|
|
namespace boost::cobalt::detail
|
|
{
|
|
|
|
template<typename T>
|
|
struct [[nodiscard]] with_impl
|
|
{
|
|
struct promise_type;
|
|
|
|
bool await_ready() { return false;}
|
|
|
|
template<typename Promise>
|
|
BOOST_NOINLINE auto await_suspend(std::coroutine_handle<Promise> h) -> std::coroutine_handle<promise_type>;
|
|
inline T await_resume();
|
|
|
|
private:
|
|
with_impl(promise_type & promise) : promise(promise) {}
|
|
promise_type & promise;
|
|
};
|
|
|
|
template<typename T>
|
|
struct with_promise_value
|
|
{
|
|
std::optional<T> result;
|
|
|
|
void return_value(std::optional<T> && value)
|
|
{
|
|
if (value) // so non-move-assign types work
|
|
result.emplace(std::move(*value));
|
|
}
|
|
|
|
T get_result()
|
|
{
|
|
return std::move(result).value();
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct with_promise_value<void>
|
|
{
|
|
void return_void() {}
|
|
void get_result() {}
|
|
};
|
|
|
|
|
|
template<typename T>
|
|
struct with_impl<T>::promise_type
|
|
: with_promise_value<T>,
|
|
enable_awaitables<promise_type>,
|
|
enable_await_allocator<promise_type>
|
|
{
|
|
using enable_awaitables<promise_type>::await_transform;
|
|
using enable_await_allocator<promise_type>::await_transform;
|
|
|
|
|
|
using executor_type = executor;
|
|
const executor_type & get_executor() const {return *exec;}
|
|
std::optional<executor_type> exec;
|
|
|
|
with_impl get_return_object()
|
|
{
|
|
return with_impl{*this};
|
|
}
|
|
|
|
std::exception_ptr e;
|
|
void unhandled_exception()
|
|
{
|
|
e = std::current_exception();
|
|
}
|
|
|
|
std::suspend_always initial_suspend() noexcept {return {};}
|
|
|
|
struct final_awaitable
|
|
{
|
|
promise_type *promise;
|
|
|
|
bool await_ready() const noexcept
|
|
{
|
|
return false;
|
|
}
|
|
BOOST_NOINLINE
|
|
auto await_suspend(std::coroutine_handle<promise_type> h) noexcept -> std::coroutine_handle<void>
|
|
{
|
|
return std::coroutine_handle<void>::from_address(h.promise().awaited_from.address());
|
|
}
|
|
|
|
void await_resume() noexcept
|
|
{
|
|
}
|
|
};
|
|
|
|
auto final_suspend() noexcept
|
|
{
|
|
return final_awaitable{this};
|
|
}
|
|
using cancellation_slot_type = asio::cancellation_slot;
|
|
cancellation_slot_type get_cancellation_slot() const {return slot_;}
|
|
asio::cancellation_slot slot_;
|
|
|
|
std::coroutine_handle<void> awaited_from{nullptr};
|
|
|
|
};
|
|
|
|
template<typename T>
|
|
T with_impl<T>::await_resume()
|
|
{
|
|
auto e = promise.e;
|
|
auto res = std::move(promise.get_result());
|
|
std::coroutine_handle<promise_type>::from_promise(promise).destroy();
|
|
if (e)
|
|
std::rethrow_exception(e);
|
|
|
|
return std::move(res);
|
|
}
|
|
|
|
template<>
|
|
inline void with_impl<void>::await_resume()
|
|
{
|
|
auto e = promise.e;
|
|
std::coroutine_handle<promise_type>::from_promise(promise).destroy();
|
|
if (e)
|
|
std::rethrow_exception(e);
|
|
}
|
|
|
|
template<typename T>
|
|
template<typename Promise>
|
|
auto with_impl<T>::await_suspend(std::coroutine_handle<Promise> h) -> std::coroutine_handle<promise_type>
|
|
{
|
|
if constexpr (requires (Promise p) {p.get_executor();})
|
|
promise.exec.emplace(h.promise().get_executor());
|
|
else
|
|
promise.exec.emplace(this_thread::get_executor());
|
|
|
|
if constexpr (requires (Promise p) {p.get_cancellation_slot();})
|
|
promise.slot_ = h.promise().get_cancellation_slot();
|
|
|
|
promise.awaited_from = h;
|
|
return std::coroutine_handle<promise_type>::from_promise(promise);
|
|
}
|
|
|
|
}
|
|
|
|
#endif //BOOST_COBALT_DETAIL_WITH_HPP
|