blob: 3630875b80dc8d443f61e11af11d19177befb5b6 [file] [log] [blame]
//
// impl/await.hpp
// ~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// 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_IMPL_AWAIT_HPP
#define ASIO_IMPL_AWAIT_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include <exception>
#include <functional>
#include <memory>
#include <new>
#include <tuple>
#include <utility>
#include "asio/async_result.hpp"
#include "asio/detail/type_traits.hpp"
#include "asio/dispatch.hpp"
#include "asio/post.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail {
// Promise object for coroutine at top of thread-of-execution "stack".
class awaiter
{
public:
awaiter* get_return_object()
{
return this;
}
auto initial_suspend()
{
return std::experimental::suspend_never();
}
auto final_suspend()
{
return std::experimental::suspend_always();
}
void set_exception(std::exception_ptr ex)
{
pending_exception_ = ex;
}
void return_void()
{
}
awaiter* add_ref()
{
++ref_count_;
return this;
}
void release()
{
if (--ref_count_ == 0)
coroutine_handle<awaiter>::from_promise(this).destroy();
}
void rethrow_exception()
{
if (pending_exception_)
{
std::exception_ptr ex = std::exchange(pending_exception_, nullptr);
std::rethrow_exception(ex);
}
}
private:
std::size_t ref_count_ = 0;
std::exception_ptr pending_exception_;
};
struct awaiter_delete
{
public:
void operator()(awaiter* a)
{
if (a)
a->release();
}
};
typedef std::unique_ptr<awaiter, awaiter_delete> awaiter_ptr;
class awaitee_base
{
public:
auto initial_suspend()
{
return std::experimental::suspend_never();
}
struct final_suspender
{
awaitee_base* this_;
bool await_ready()
{
return false;
}
void await_suspend(coroutine_handle<void>)
{
this_->wake_caller();
}
void await_resume()
{
}
};
auto final_suspend()
{
return final_suspender{this};
}
void set_exception(std::exception_ptr e)
{
pending_exception_ = e;
ready_ = true;
}
void wake_caller()
{
if (caller_)
caller_.resume();
}
protected:
void rethrow_exception()
{
if (pending_exception_)
{
std::exception_ptr ex = std::exchange(pending_exception_, nullptr);
std::rethrow_exception(ex);
}
}
template <typename> friend class awaitable;
awaiter* awaiter_ = nullptr;
coroutine_handle<void> caller_ = nullptr;
std::exception_ptr pending_exception_ = nullptr;
bool ready_ = false;
};
template <typename T>
class awaitee
: public awaitee_base
{
public:
~awaitee()
{
if (initialised_)
{
T* p = static_cast<T*>(static_cast<void*>(buf_));
p->~T();
}
}
awaitable<T> get_return_object()
{
return awaitable<T>(this);
};
template <typename U>
void return_value(U&& u)
{
T* p = static_cast<T*>(static_cast<void*>(buf_));
new (p) T(std::forward<U>(u));
initialised_ = true;
}
T value()
{
rethrow_exception();
return std::move(*static_cast<T*>(static_cast<void*>(buf_)));
}
private:
template <typename> friend class awaitable;
unsigned char buf_[sizeof(T)] alignas(T);
bool initialised_ = false;
};
template <>
class awaitee<void>
: public awaitee_base
{
public:
awaitable<void> get_return_object()
{
return awaitable<void>(this);
};
void return_void()
{
}
void value()
{
rethrow_exception();
}
};
#if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable:4033)
#endif // defined(_MSC_VER)
template <typename T> T dummy_return()
{
return static_cast<T&&>(*static_cast<T*>(nullptr));
}
template <> void dummy_return<void>()
{
}
template <typename T>
awaitable<T> make_dummy_awaitable()
{
for (;;) co_await std::experimental::suspend_always();
return dummy_return<T>();
}
#if defined(_MSC_VER)
# pragma warning(pop)
#endif // defined(_MSC_VER)
template <typename Executor>
class destroy_awaiter
{
public:
typedef Executor executor_type;
destroy_awaiter(Executor ex, awaiter_ptr a)
: ex_(ex),
awaiter_(std::move(a))
{
}
destroy_awaiter(destroy_awaiter&& other)
: ex_(std::move(other.ex_)),
awaiter_(std::move(other.awaiter_))
{
}
executor_type get_executor() const noexcept
{
return ex_;
}
void operator()()
{
awaiter_ptr(std::move(awaiter_));
}
protected:
Executor ex_;
awaiter_ptr awaiter_;
};
template <typename Executor>
class awaiter_task
{
public:
typedef Executor executor_type;
awaiter_task(Executor ex, awaiter* a)
: ex_(ex),
awaiter_(a->add_ref())
{
}
awaiter_task(awaiter_task&& other)
: ex_(std::move(other.ex_)),
awaiter_(std::move(other.awaiter_))
{
}
~awaiter_task()
{
if (awaiter_)
(post)(destroy_awaiter<Executor>(ex_, std::move(awaiter_)));
}
executor_type get_executor() const noexcept
{
return ex_;
}
protected:
Executor ex_;
awaiter_ptr awaiter_;
};
template <typename Executor>
class spawn_handler : public awaiter_task<Executor>
{
public:
using awaiter_task::awaiter_task;
void operator()()
{
awaiter_ptr ptr(std::move(awaiter_));
coroutine_handle<awaiter>::from_promise(ptr.get()).resume();
}
};
template <typename Executor, typename T>
class await_handler_base : public awaiter_task<Executor>
{
public:
typedef awaitable<T> awaitable_type;
await_handler_base(basic_unsynchronized_await_context<Executor> ctx)
: awaiter_task<Executor>(ctx.get_executor(), ctx.awaiter_),
awaitee_(nullptr)
{
}
awaitable<T> make_awaitable()
{
awaitable<T> a(make_dummy_awaitable<T>());
awaitee_ = a.awaitee_;
return a;
}
protected:
awaitee<T>* awaitee_;
};
template <typename Executor, typename... Args>
class await_handler;
template <typename Executor>
class await_handler<Executor>
: public await_handler_base<Executor, void>
{
public:
using await_handler_base::await_handler_base;
void operator()()
{
awaiter_ptr ptr(std::move(awaiter_));
awaitee_->return_void();
awaitee_->wake_caller();
ptr->rethrow_exception();
}
};
template <typename Executor>
class await_handler<Executor, error_code>
: public await_handler_base<Executor, void>
{
public:
typedef void return_type;
using await_handler_base::await_handler_base;
void operator()(error_code ec)
{
awaiter_ptr ptr(std::move(awaiter_));
if (ec)
awaitee_->set_exception(std::make_exception_ptr(system_error(ec)));
else
awaitee_->return_void();
awaitee_->wake_caller();
ptr->rethrow_exception();
}
};
template <typename Executor>
class await_handler<Executor, std::exception_ptr>
: public await_handler_base<Executor, void>
{
public:
using await_handler_base::await_handler_base;
void operator()(std::exception_ptr ex)
{
awaiter_ptr ptr(std::move(awaiter_));
if (ec)
awaitee_->set_exception(ex);
else
awaitee_->return_void();
awaitee_->wake_caller();
ptr->rethrow_exception();
}
};
template <typename Executor, typename T>
class await_handler<Executor, T>
: public await_handler_base<Executor, T>
{
public:
using await_handler_base::await_handler_base;
void operator()(T t)
{
awaiter_ptr ptr(std::move(awaiter_));
if (ec)
awaitee_->set_exception(ex);
else
awaitee_->return_value(std::forward<T>(t));
awaitee_->wake_caller();
ptr->rethrow_exception();
}
};
template <typename Executor, typename T>
class await_handler<Executor, error_code, T>
: public await_handler_base<Executor, T>
{
public:
using await_handler_base::await_handler_base;
void operator()(error_code ec, T t)
{
awaiter_ptr ptr(std::move(awaiter_));
if (ec)
awaitee_->set_exception(std::make_exception_ptr(system_error(ec)));
else
awaitee_->return_value(std::forward<T>(t));
awaitee_->wake_caller();
ptr->rethrow_exception();
}
};
template <typename Executor, typename T>
class await_handler<Executor, std::exception_ptr, T>
: public await_handler_base<Executor, T>
{
public:
using await_handler_base::await_handler_base;
void operator()(std::exception_ptr ex, T t)
{
awaiter_ptr ptr(std::move(awaiter_));
if (ec)
awaitee_->set_exception(ex);
else
awaitee_->return_value(std::forward<T>(t));
awaitee_->wake_caller();
ptr->rethrow_exception();
}
};
template <typename Executor>
class make_await_context
{
public:
explicit make_await_context(Executor ex)
: ex_(ex)
{
}
bool await_ready()
{
return false;
}
void await_suspend(coroutine_handle<detail::awaiter> h)
{
awaiter_ = &h.promise();
}
basic_unsynchronized_await_context<Executor> await_resume()
{
return basic_unsynchronized_await_context<Executor>(ex_, awaiter_);
}
private:
Executor ex_;
awaiter* awaiter_ = nullptr;
};
template <typename T>
struct awaitable_signature;
template <typename T>
struct awaitable_signature<awaitable<T>>
{
typedef void type(std::exception_ptr, T);
};
template <>
struct awaitable_signature<awaitable<void>>
{
typedef void type(std::exception_ptr);
};
template <typename T, typename WorkGuard, typename Handler,
typename Executor, typename F, typename... Args>
awaiter* spawn_entry_point(awaitable<T>*, WorkGuard work_guard,
Handler handler, Executor ex, F f, Args... args)
{
bool done = false;
try
{
T t = co_await std::invoke(std::move(f), std::move(args)...,
co_await make_await_context<Executor>(ex));
done = true;
(dispatch)(work_guard.get_executor(),
[handler = std::move(handler), t = std::move(t)]() mutable
{
handler(std::exception_ptr(), std::move(t));
});
}
catch (...)
{
if (done)
throw;
(dispatch)(work_guard.get_executor(),
[handler = std::move(handler), e = std::current_exception()]() mutable
{
handler(e, T());
});
}
}
template <typename WorkGuard, typename Handler,
typename Executor, typename F, typename... Args>
awaiter* spawn_entry_point(awaitable<void>*, WorkGuard work_guard,
Handler handler, Executor ex, F f, Args... args)
{
std::exception_ptr e = nullptr;
try
{
co_await std::invoke(std::move(f), std::move(args)...,
co_await make_await_context<Executor>(ex));
}
catch (...)
{
e = std::current_exception();
}
(dispatch)(work_guard.get_executor(),
[handler = std::move(handler), e]() mutable
{
handler(e);
});
}
template <typename CompletionToken,
typename Executor, typename F, typename... Args>
auto spawn(CompletionToken&& token, const Executor& ex, F&& f, Args&&... args)
{
typedef decltype(
std::invoke(std::declval<std::decay_t<F>>(),
std::declval<std::decay_t<Args>>()...,
std::declval<basic_unsynchronized_await_context<Executor>>()))
awaitable_type;
typedef typename awaitable_signature<awaitable_type>::type signature_type;
async_completion<CompletionToken, signature_type> completion(token);
auto work_guard = make_work_guard(completion.handler, ex);
awaiter* a = (spawn_entry_point)(static_cast<awaitable_type*>(nullptr),
std::move(work_guard), std::move(completion.handler), ex,
std::forward<F>(f), std::forward<Args>(args)...);
(post)(spawn_handler<Executor>(ex, a));
return completion.result.get();
}
template <typename ArgTypes, typename Executor,
typename F, typename CompletionToken>
inline auto spawn_reorder(std::index_sequence<> index_seq,
const Executor& ex, F&& f, CompletionToken&& token)
{
return (spawn)(std::forward<CompletionToken>(token), ex, std::forward<F>(f));
}
template <typename ArgTypes, std::size_t... Index,
typename Executor, typename F, typename CompletionToken>
inline auto spawn_reorder(std::index_sequence<Index...> index_seq,
const Executor& ex, F&& f, std::tuple_element_t<Index, ArgTypes>&&... args,
CompletionToken&& token)
{
return (spawn)(std::forward<CompletionToken>(token), ex, std::move(f),
std::forward<std::tuple_element_t<Index, ArgTypes>>(args)...);
}
} // namespace detail
template <typename T>
awaitable<T>::~awaitable()
{
if (awaitee_)
{
detail::coroutine_handle<
detail::awaitee<T>>::from_promise(
awaitee_).destroy();
}
}
template <typename T>
inline bool awaitable<T>::await_ready()
{
return awaitee_->ready_;
}
template <typename T>
inline void awaitable<T>::await_suspend(
detail::coroutine_handle<detail::awaiter> h)
{
awaitee_->caller_ = h;
}
template <typename T> template <typename U>
inline void awaitable<T>::await_suspend(
detail::coroutine_handle<detail::awaitee<U>> h)
{
awaitee_->caller_ = h;
}
template <typename T>
inline T awaitable<T>::await_resume()
{
awaitee_->caller_ = nullptr;
return awaitee_->value();
}
template <typename Executor, typename R, typename... Args>
struct handler_type<basic_unsynchronized_await_context<Executor>, R(Args...)>
{
typedef detail::await_handler<
Executor, typename decay<Args>::type...> type;
};
template <typename Executor, typename... Args>
class async_result<detail::await_handler<Executor, Args...>>
{
public:
typedef typename detail::await_handler<
Executor, Args...>::awaitable_type type;
async_result(detail::await_handler<Executor, Args...>& h)
: awaitable_(h.make_awaitable())
{
}
type get()
{
return std::move(awaitable_);
}
private:
type awaitable_;
};
template <typename Executor, typename F, typename... Args, typename>
inline auto spawn(const Executor& ex, F&& f, Args&&... args)
{
static_assert(sizeof...(Args) > 0, "CompletionToken required");
return detail::spawn_reorder<std::tuple<Args...>>(
std::make_index_sequence<sizeof...(Args) - 1>(),
ex, std::forward<F>(f), std::forward<Args>(args)...);
}
template <typename ExecutionContext, typename F, typename... Args, typename>
inline auto spawn(ExecutionContext& ctx, F&& f, Args&&... args)
{
return (spawn)(ctx.get_executor(), std::forward<F>(f),
std::forward<Args>(args)...);
}
template <typename Executor, typename F, typename... Args>
inline auto spawn(const basic_unsynchronized_await_context<Executor>& ctx,
F&& f, Args&&... args)
{
return (spawn)(ctx.get_executor(), std::forward<F>(f),
std::forward<Args>(args)...);
}
} // namespace asio
namespace std {
namespace experimental {
template <typename... Args>
struct coroutine_traits<asio::detail::awaiter*, Args...>
{
typedef asio::detail::awaiter promise_type;
};
template <typename T, typename... Args>
struct coroutine_traits<asio::awaitable<T>, Args...>
{
typedef asio::detail::awaitee<T> promise_type;
};
} // namespace experimental
} // namespace std
#include "asio/detail/pop_options.hpp"
#endif // ASIO_IMPL_AWAIT_HPP