mirror of
https://github.com/30hours/blah2.git
synced 2024-11-08 12:25:42 +00:00
293 lines
8 KiB
C++
293 lines
8 KiB
C++
//
|
|
// experimental/coro.hpp
|
|
// ~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
// Copyright (c) 2021-2022 Klemens D. Morgenstern
|
|
// (klemens dot morgenstern at gmx dot net)
|
|
//
|
|
// 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 ASIO_EXPERIMENTAL_CORO_HPP
|
|
#define ASIO_EXPERIMENTAL_CORO_HPP
|
|
|
|
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
|
# pragma once
|
|
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
|
|
|
#include "asio/detail/config.hpp"
|
|
#include "asio/dispatch.hpp"
|
|
#include "asio/error.hpp"
|
|
#include "asio/error_code.hpp"
|
|
#include "asio/experimental/coro_traits.hpp"
|
|
#include "asio/experimental/detail/coro_promise_allocator.hpp"
|
|
#include "asio/experimental/detail/partial_promise.hpp"
|
|
#include "asio/post.hpp"
|
|
|
|
#include "asio/detail/push_options.hpp"
|
|
|
|
namespace asio {
|
|
namespace experimental {
|
|
namespace detail {
|
|
|
|
template <typename Signature, typename Return,
|
|
typename Executor, typename Allocator>
|
|
struct coro_promise;
|
|
|
|
template <typename T, typename Coroutine>
|
|
struct coro_with_arg;
|
|
|
|
} // namespace detail
|
|
|
|
/// The main type of a resumable coroutine.
|
|
/**
|
|
* Template parameter @c Yield specifies type or signature used by co_yield,
|
|
* @c Return specifies the type used for co_return, and @c Executor specifies
|
|
* the underlying executor type.
|
|
*/
|
|
template <typename Yield = void, typename Return = void,
|
|
typename Executor = any_io_executor,
|
|
typename Allocator = std::allocator<void>>
|
|
struct coro
|
|
{
|
|
/// The traits of the coroutine. See asio::experimental::coro_traits
|
|
/// for details.
|
|
using traits = coro_traits<Yield, Return, Executor>;
|
|
|
|
/// The value that can be passed into a symmetrical cororoutine. @c void if
|
|
/// asymmetrical.
|
|
using input_type = typename traits::input_type;
|
|
|
|
/// The type that can be passed out through a co_yield.
|
|
using yield_type = typename traits::yield_type;
|
|
|
|
/// The type that can be passed out through a co_return.
|
|
using return_type = typename traits::return_type;
|
|
|
|
/// The type received by a co_await or async_resume. Its a combination of
|
|
/// yield and return.
|
|
using result_type = typename traits::result_type;
|
|
|
|
/// The signature used by the async_resume.
|
|
using signature_type = typename traits::signature_type;
|
|
|
|
/// Whether or not the coroutine is noexcept.
|
|
constexpr static bool is_noexcept = traits::is_noexcept;
|
|
|
|
/// The error type of the coroutine. Void for noexcept
|
|
using error_type = typename traits::error_type;
|
|
|
|
/// Completion handler type used by async_resume.
|
|
using completion_handler = typename traits::completion_handler;
|
|
|
|
/// The internal promise-type of the coroutine.
|
|
using promise_type = detail::coro_promise<Yield, Return, Executor, Allocator>;
|
|
|
|
#if !defined(GENERATING_DOCUMENTATION)
|
|
template <typename T, typename Coroutine>
|
|
friend struct detail::coro_with_arg;
|
|
#endif // !defined(GENERATING_DOCUMENTATION)
|
|
|
|
/// The executor type.
|
|
using executor_type = Executor;
|
|
|
|
/// The allocator type.
|
|
using allocator_type = Allocator;
|
|
|
|
#if !defined(GENERATING_DOCUMENTATION)
|
|
friend struct detail::coro_promise<Yield, Return, Executor, Allocator>;
|
|
#endif // !defined(GENERATING_DOCUMENTATION)
|
|
|
|
/// The default constructor, gives an invalid coroutine.
|
|
coro() = default;
|
|
|
|
/// Move constructor.
|
|
coro(coro&& lhs) noexcept
|
|
: coro_(std::exchange(lhs.coro_, nullptr))
|
|
{
|
|
}
|
|
|
|
coro(const coro&) = delete;
|
|
|
|
/// Move assignment.
|
|
coro& operator=(coro&& lhs) noexcept
|
|
{
|
|
std::swap(coro_, lhs.coro_);
|
|
return *this;
|
|
}
|
|
|
|
coro& operator=(const coro&) = delete;
|
|
|
|
/// Destructor. Destroys the coroutine, if it holds a valid one.
|
|
/**
|
|
* @note This does not cancel an active coroutine. Destructing a resumable
|
|
* coroutine, i.e. one with a call to async_resume that has not completed, is
|
|
* undefined behaviour.
|
|
*/
|
|
~coro()
|
|
{
|
|
if (coro_ != nullptr)
|
|
{
|
|
struct destroyer
|
|
{
|
|
detail::coroutine_handle<promise_type> handle;
|
|
|
|
destroyer(const detail::coroutine_handle<promise_type>& handle)
|
|
: handle(handle)
|
|
{ }
|
|
|
|
destroyer(destroyer&& lhs)
|
|
: handle(std::exchange(lhs.handle, nullptr))
|
|
{
|
|
}
|
|
|
|
destroyer(const destroyer&) = delete;
|
|
|
|
void operator()() {}
|
|
|
|
~destroyer()
|
|
{
|
|
if (handle)
|
|
handle.destroy();
|
|
}
|
|
};
|
|
|
|
auto handle =
|
|
detail::coroutine_handle<promise_type>::from_promise(*coro_);
|
|
if (handle)
|
|
asio::dispatch(coro_->get_executor(), destroyer{handle});
|
|
}
|
|
}
|
|
|
|
/// Get the used executor.
|
|
executor_type get_executor() const
|
|
{
|
|
if (coro_)
|
|
return coro_->get_executor();
|
|
|
|
if constexpr (std::is_default_constructible_v<Executor>)
|
|
return Executor{};
|
|
else
|
|
throw std::logic_error("Coroutine has no executor");
|
|
}
|
|
|
|
/// Get the used allocator.
|
|
allocator_type get_allocator() const
|
|
{
|
|
if (coro_)
|
|
return coro_->get_allocator();
|
|
|
|
if constexpr (std::is_default_constructible_v<Allocator>)
|
|
return Allocator{};
|
|
else
|
|
throw std::logic_error(
|
|
"Coroutine has no available allocator without a constructed promise");
|
|
}
|
|
|
|
/// Resume the coroutine.
|
|
/**
|
|
* @param token The completion token of the async resume.
|
|
*
|
|
* @attention Calling an invalid coroutine with a noexcept signature is
|
|
* undefined behaviour.
|
|
*
|
|
* @note This overload is only available for coroutines without an input
|
|
* value.
|
|
*/
|
|
template <typename CompletionToken>
|
|
requires std::is_void_v<input_type>
|
|
auto async_resume(CompletionToken&& token) &
|
|
{
|
|
return async_initiate<CompletionToken,
|
|
typename traits::completion_handler>(
|
|
initiate_async_resume(this), token);
|
|
}
|
|
|
|
/// Resume the coroutine.
|
|
/**
|
|
* @param token The completion token of the async resume.
|
|
*
|
|
* @attention Calling an invalid coroutine with a noexcept signature is
|
|
* undefined behaviour.
|
|
*
|
|
* @note This overload is only available for coroutines with an input value.
|
|
*/
|
|
template <typename CompletionToken, detail::convertible_to<input_type> T>
|
|
auto async_resume(T&& ip, CompletionToken&& token) &
|
|
{
|
|
return async_initiate<CompletionToken,
|
|
typename traits::completion_handler>(
|
|
initiate_async_resume(this), token, std::forward<T>(ip));
|
|
}
|
|
|
|
/// Operator used for coroutines without input value.
|
|
auto operator co_await() requires (std::is_void_v<input_type>)
|
|
{
|
|
return awaitable_t{*this};
|
|
}
|
|
|
|
/// Operator used for coroutines with input value.
|
|
/**
|
|
* @param ip The input value
|
|
*
|
|
* @returns An awaitable handle.
|
|
*
|
|
* @code
|
|
* coro<void> push_values(coro<double(int)> c)
|
|
* {
|
|
* std::optional<double> res = co_await c(42);
|
|
* }
|
|
* @endcode
|
|
*/
|
|
template <detail::convertible_to<input_type> T>
|
|
auto operator()(T&& ip)
|
|
{
|
|
return detail::coro_with_arg<std::decay_t<T>, coro>{
|
|
std::forward<T>(ip), *this};
|
|
}
|
|
|
|
/// Check whether the coroutine is open, i.e. can be resumed.
|
|
bool is_open() const
|
|
{
|
|
if (coro_)
|
|
{
|
|
auto handle =
|
|
detail::coroutine_handle<promise_type>::from_promise(*coro_);
|
|
return handle && !handle.done();
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
/// Check whether the coroutine is open, i.e. can be resumed.
|
|
explicit operator bool() const { return is_open(); }
|
|
|
|
private:
|
|
struct awaitable_t;
|
|
|
|
struct initiate_async_resume;
|
|
|
|
explicit coro(promise_type* const cr) : coro_(cr) {}
|
|
|
|
promise_type* coro_{nullptr};
|
|
};
|
|
|
|
/// A generator is a coro that returns void and yields value.
|
|
template<typename T, typename Executor = asio::any_io_executor,
|
|
typename Allocator = std::allocator<void>>
|
|
using generator = coro<T, void, Executor, Allocator>;
|
|
|
|
/// A task is a coro that does not yield values
|
|
template<typename T, typename Executor = asio::any_io_executor,
|
|
typename Allocator = std::allocator<void>>
|
|
using task = coro<void(), T, Executor, Allocator>;
|
|
|
|
} // namespace experimental
|
|
} // namespace asio
|
|
|
|
#include "asio/detail/pop_options.hpp"
|
|
|
|
#include "asio/experimental/impl/coro.hpp"
|
|
|
|
#endif // ASIO_EXPERIMENTAL_CORO_HPP
|