summaryrefslogtreecommitdiff
path: root/boost/asio/experimental
diff options
context:
space:
mode:
authorDongHun Kwak <dh0128.kwak@samsung.com>2019-12-05 15:12:59 +0900
committerDongHun Kwak <dh0128.kwak@samsung.com>2019-12-05 15:12:59 +0900
commitb8cf34c691623e4ec329053cbbf68522a855882d (patch)
tree34da08632a99677f6b79ecb65e5b655a5b69a67f /boost/asio/experimental
parent3fdc3e5ee96dca5b11d1694975a65200787eab86 (diff)
downloadboost-upstream/1.67.0.tar.gz
boost-upstream/1.67.0.tar.bz2
boost-upstream/1.67.0.zip
Imported Upstream version 1.67.0upstream/1.67.0
Diffstat (limited to 'boost/asio/experimental')
-rw-r--r--boost/asio/experimental/co_spawn.hpp228
-rw-r--r--boost/asio/experimental/detached.hpp67
-rw-r--r--boost/asio/experimental/impl/co_spawn.hpp878
-rw-r--r--boost/asio/experimental/impl/detached.hpp93
-rw-r--r--boost/asio/experimental/impl/redirect_error.hpp296
-rw-r--r--boost/asio/experimental/redirect_error.hpp69
6 files changed, 1631 insertions, 0 deletions
diff --git a/boost/asio/experimental/co_spawn.hpp b/boost/asio/experimental/co_spawn.hpp
new file mode 100644
index 0000000000..c900da11f0
--- /dev/null
+++ b/boost/asio/experimental/co_spawn.hpp
@@ -0,0 +1,228 @@
+//
+// experimental/co_spawn.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2018 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 BOOST_ASIO_EXPERIMENTAL_CO_SPAWN_HPP
+#define BOOST_ASIO_EXPERIMENTAL_CO_SPAWN_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include <boost/asio/detail/config.hpp>
+
+#if defined(BOOST_ASIO_HAS_CO_AWAIT) || defined(GENERATING_DOCUMENTATION)
+
+#include <experimental/coroutine>
+#include <boost/asio/executor.hpp>
+#include <boost/asio/strand.hpp>
+
+#include <boost/asio/detail/push_options.hpp>
+
+namespace boost {
+namespace asio {
+namespace experimental {
+namespace detail {
+
+using std::experimental::coroutine_handle;
+
+template <typename> class awaiter;
+template <typename> class awaitee_base;
+template <typename, typename> class awaitee;
+template <typename, typename> class await_handler_base;
+template <typename Executor, typename F, typename CompletionToken>
+auto co_spawn(const Executor& ex, F&& f, CompletionToken&& token);
+
+} // namespace detail
+
+namespace this_coro {
+
+/// Awaitable type that returns a completion token for the current coroutine.
+struct token_t {};
+
+/// Awaitable object that returns a completion token for the current coroutine.
+constexpr inline token_t token() { return {}; }
+
+/// Awaitable type that returns the executor of the current coroutine.
+struct executor_t {};
+
+/// Awaitable object that returns the executor of the current coroutine.
+constexpr inline executor_t executor() { return {}; }
+
+} // namespace this_coro
+
+/// A completion token that represents the currently executing coroutine.
+/**
+ * The await_token class is used to represent the currently executing
+ * coroutine. An await_token may be passed as a handler to an asynchronous
+ * operation. For example:
+ *
+ * @code awaitable<void> my_coroutine()
+ * {
+ * await_token token = co_await this_coro::token();
+ * ...
+ * std::size_t n = co_await my_socket.async_read_some(buffer, token);
+ * ...
+ * } @endcode
+ *
+ * The initiating function (async_read_some in the above example) suspends the
+ * current coroutine. The coroutine is resumed when the asynchronous operation
+ * completes, and the result of the operation is returned.
+ */
+template <typename Executor>
+class await_token
+{
+public:
+ /// The associated executor type.
+ typedef Executor executor_type;
+
+ /// Copy constructor.
+ await_token(const await_token& other) noexcept
+ : awaiter_(other.awaiter_)
+ {
+ }
+
+ /// Move constructor.
+ await_token(await_token&& other) noexcept
+ : awaiter_(std::exchange(other.awaiter_, nullptr))
+ {
+ }
+
+ /// Get the associated executor.
+ executor_type get_executor() const noexcept
+ {
+ return awaiter_->get_executor();
+ }
+
+private:
+ // No assignment allowed.
+ await_token& operator=(const await_token&) = delete;
+
+ template <typename> friend class detail::awaitee_base;
+ template <typename, typename> friend class detail::await_handler_base;
+
+ // Private constructor used by awaitee_base.
+ explicit await_token(detail::awaiter<Executor>* a)
+ : awaiter_(a)
+ {
+ }
+
+ detail::awaiter<Executor>* awaiter_;
+};
+
+/// The return type of a coroutine or asynchronous operation.
+template <typename T, typename Executor = strand<executor>>
+class awaitable
+{
+public:
+ /// The type of the awaited value.
+ typedef T value_type;
+
+ /// The executor type that will be used for the coroutine.
+ typedef Executor executor_type;
+
+ /// Move constructor.
+ awaitable(awaitable&& other) noexcept
+ : awaitee_(std::exchange(other.awaitee_, nullptr))
+ {
+ }
+
+ /// Destructor
+ ~awaitable()
+ {
+ if (awaitee_)
+ {
+ detail::coroutine_handle<
+ detail::awaitee<T, Executor>>::from_promise(
+ *awaitee_).destroy();
+ }
+ }
+
+#if !defined(GENERATING_DOCUMENTATION)
+
+ // Support for co_await keyword.
+ bool await_ready() const noexcept
+ {
+ return awaitee_->ready();
+ }
+
+ // Support for co_await keyword.
+ void await_suspend(detail::coroutine_handle<detail::awaiter<Executor>> h)
+ {
+ awaitee_->attach_caller(h);
+ }
+
+ // Support for co_await keyword.
+ template <class U>
+ void await_suspend(detail::coroutine_handle<detail::awaitee<U, Executor>> h)
+ {
+ awaitee_->attach_caller(h);
+ }
+
+ // Support for co_await keyword.
+ T await_resume()
+ {
+ return awaitee_->get();
+ }
+
+#endif // !defined(GENERATING_DOCUMENTATION)
+
+private:
+ template <typename, typename> friend class detail::awaitee;
+ template <typename, typename> friend class detail::await_handler_base;
+
+ // Not copy constructible or copy assignable.
+ awaitable(const awaitable&) = delete;
+ awaitable& operator=(const awaitable&) = delete;
+
+ // Construct the awaitable from a coroutine's promise object.
+ explicit awaitable(detail::awaitee<T, Executor>* a) : awaitee_(a) {}
+
+ detail::awaitee<T, Executor>* awaitee_;
+};
+
+/// Spawn a new thread of execution.
+template <typename Executor, typename F, typename CompletionToken,
+ typename = typename enable_if<is_executor<Executor>::value>::type>
+inline auto co_spawn(const Executor& ex, F&& f, CompletionToken&& token)
+{
+ return detail::co_spawn(ex, std::forward<F>(f),
+ std::forward<CompletionToken>(token));
+}
+
+/// Spawn a new thread of execution.
+template <typename ExecutionContext, typename F, typename CompletionToken,
+ typename = typename enable_if<
+ is_convertible<ExecutionContext&, execution_context&>::value>::type>
+inline auto co_spawn(ExecutionContext& ctx, F&& f, CompletionToken&& token)
+{
+ return detail::co_spawn(ctx.get_executor(), std::forward<F>(f),
+ std::forward<CompletionToken>(token));
+}
+
+/// Spawn a new thread of execution.
+template <typename Executor, typename F, typename CompletionToken>
+inline auto co_spawn(const await_token<Executor>& parent,
+ F&& f, CompletionToken&& token)
+{
+ return detail::co_spawn(parent.get_executor(), std::forward<F>(f),
+ std::forward<CompletionToken>(token));
+}
+
+} // namespace experimental
+} // namespace asio
+} // namespace boost
+
+#include <boost/asio/detail/pop_options.hpp>
+
+#include <boost/asio/experimental/impl/co_spawn.hpp>
+
+#endif // defined(BOOST_ASIO_HAS_CO_AWAIT) || defined(GENERATING_DOCUMENTATION)
+
+#endif // BOOST_ASIO_EXPERIMENTAL_CO_SPAWN_HPP
diff --git a/boost/asio/experimental/detached.hpp b/boost/asio/experimental/detached.hpp
new file mode 100644
index 0000000000..c3e7adafac
--- /dev/null
+++ b/boost/asio/experimental/detached.hpp
@@ -0,0 +1,67 @@
+//
+// experimental/detached.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2018 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 BOOST_ASIO_EXPERIMENTAL_DETACHED_HPP
+#define BOOST_ASIO_EXPERIMENTAL_DETACHED_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include <boost/asio/detail/config.hpp>
+#include <memory>
+
+#include <boost/asio/detail/push_options.hpp>
+
+namespace boost {
+namespace asio {
+namespace experimental {
+
+/// Class used to specify that an asynchronous operation is detached.
+/**
+
+ * The detached_t class is used to indicate that an asynchronous operation is
+ * detached. That is, there is no completion handler waiting for the
+ * operation's result. A detached_t object may be passed as a handler to an
+ * asynchronous operation, typically using the special value
+ * @c boost::asio::experimental::detached. For example:
+
+ * @code my_socket.async_send(my_buffer, boost::asio::experimental::detached);
+ * @endcode
+ */
+class detached_t
+{
+public:
+ /// Constructor.
+ BOOST_ASIO_CONSTEXPR detached_t()
+ {
+ }
+};
+
+/// A special value, similar to std::nothrow.
+/**
+ * See the documentation for boost::asio::experimental::detached_t for a usage
+ * example.
+ */
+#if defined(BOOST_ASIO_HAS_CONSTEXPR) || defined(GENERATING_DOCUMENTATION)
+constexpr detached_t detached;
+#elif defined(BOOST_ASIO_MSVC)
+__declspec(selectany) detached_t detached;
+#endif
+
+} // namespace experimental
+} // namespace asio
+} // namespace boost
+
+#include <boost/asio/detail/pop_options.hpp>
+
+#include <boost/asio/experimental/impl/detached.hpp>
+
+#endif // BOOST_ASIO_EXPERIMENTAL_DETACHED_HPP
diff --git a/boost/asio/experimental/impl/co_spawn.hpp b/boost/asio/experimental/impl/co_spawn.hpp
new file mode 100644
index 0000000000..51ffb4eeb1
--- /dev/null
+++ b/boost/asio/experimental/impl/co_spawn.hpp
@@ -0,0 +1,878 @@
+//
+// experimental/impl/co_spawn.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2018 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 BOOST_ASIO_EXPERIMENTAL_IMPL_CO_SPAWN_HPP
+#define BOOST_ASIO_EXPERIMENTAL_IMPL_CO_SPAWN_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include <boost/asio/detail/config.hpp>
+#include <exception>
+#include <functional>
+#include <memory>
+#include <new>
+#include <tuple>
+#include <utility>
+#include <boost/asio/async_result.hpp>
+#include <boost/asio/detail/thread_context.hpp>
+#include <boost/asio/detail/thread_info_base.hpp>
+#include <boost/asio/detail/type_traits.hpp>
+#include <boost/asio/dispatch.hpp>
+#include <boost/asio/post.hpp>
+
+#include <boost/asio/detail/push_options.hpp>
+
+namespace boost {
+namespace asio {
+namespace experimental {
+namespace detail {
+
+// Promise object for coroutine at top of thread-of-execution "stack".
+template <typename Executor>
+class awaiter
+{
+public:
+ struct deleter
+ {
+ void operator()(awaiter* a)
+ {
+ if (a)
+ a->release();
+ }
+ };
+
+ typedef std::unique_ptr<awaiter, deleter> ptr;
+
+ typedef Executor executor_type;
+
+ ~awaiter()
+ {
+ if (has_executor_)
+ static_cast<Executor*>(static_cast<void*>(executor_))->~Executor();
+ }
+
+ void set_executor(const Executor& ex)
+ {
+ new (&executor_) Executor(ex);
+ has_executor_ = true;
+ }
+
+ executor_type get_executor() const noexcept
+ {
+ return *static_cast<const Executor*>(static_cast<const void*>(executor_));
+ }
+
+ awaiter* get_return_object()
+ {
+ return this;
+ }
+
+ auto initial_suspend()
+ {
+ return std::experimental::suspend_always();
+ }
+
+ auto final_suspend()
+ {
+ return std::experimental::suspend_always();
+ }
+
+ void return_void()
+ {
+ }
+
+ awaiter* add_ref()
+ {
+ ++ref_count_;
+ return this;
+ }
+
+ void release()
+ {
+ if (--ref_count_ == 0)
+ coroutine_handle<awaiter>::from_promise(*this).destroy();
+ }
+
+ void unhandled_exception()
+ {
+ pending_exception_ = std::current_exception();
+ }
+
+ void rethrow_unhandled_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_ = nullptr;
+ alignas(Executor) unsigned char executor_[sizeof(Executor)];
+ bool has_executor_ = false;
+};
+
+// Base promise for coroutines further down the thread-of-execution "stack".
+template <typename Executor>
+class awaitee_base
+{
+public:
+#if !defined(BOOST_ASIO_DISABLE_AWAITEE_RECYCLING)
+ void* operator new(std::size_t size)
+ {
+ return boost::asio::detail::thread_info_base::allocate(
+ boost::asio::detail::thread_info_base::awaitee_tag(),
+ boost::asio::detail::thread_context::thread_call_stack::top(),
+ size);
+ }
+
+ void operator delete(void* pointer, std::size_t size)
+ {
+ boost::asio::detail::thread_info_base::deallocate(
+ boost::asio::detail::thread_info_base::awaitee_tag(),
+ boost::asio::detail::thread_context::thread_call_stack::top(),
+ pointer, size);
+ }
+#endif // !defined(BOOST_ASIO_DISABLE_AWAITEE_RECYCLING)
+
+ auto initial_suspend()
+ {
+ return std::experimental::suspend_never();
+ }
+
+ struct final_suspender
+ {
+ awaitee_base* this_;
+
+ bool await_ready() const noexcept
+ {
+ return false;
+ }
+
+ void await_suspend(coroutine_handle<void>)
+ {
+ this_->wake_caller();
+ }
+
+ void await_resume() const noexcept
+ {
+ }
+ };
+
+ auto final_suspend()
+ {
+ return final_suspender{this};
+ }
+
+ void set_except(std::exception_ptr e)
+ {
+ pending_exception_ = e;
+ }
+
+ void unhandled_exception()
+ {
+ set_except(std::current_exception());
+ }
+
+ void rethrow_exception()
+ {
+ if (pending_exception_)
+ {
+ std::exception_ptr ex = std::exchange(pending_exception_, nullptr);
+ std::rethrow_exception(ex);
+ }
+ }
+
+ awaiter<Executor>* top()
+ {
+ return awaiter_;
+ }
+
+ coroutine_handle<void> caller()
+ {
+ return caller_;
+ }
+
+ bool ready() const
+ {
+ return ready_;
+ }
+
+ void wake_caller()
+ {
+ if (caller_)
+ caller_.resume();
+ else
+ ready_ = true;
+ }
+
+ class awaitable_executor
+ {
+ public:
+ explicit awaitable_executor(awaitee_base* a)
+ : this_(a)
+ {
+ }
+
+ bool await_ready() const noexcept
+ {
+ return this_->awaiter_ != nullptr;
+ }
+
+ template <typename U, typename Ex>
+ void await_suspend(coroutine_handle<detail::awaitee<U, Ex>> h) noexcept
+ {
+ this_->resume_on_attach_ = h;
+ }
+
+ Executor await_resume()
+ {
+ return this_->awaiter_->get_executor();
+ }
+
+ private:
+ awaitee_base* this_;
+ };
+
+ awaitable_executor await_transform(this_coro::executor_t) noexcept
+ {
+ return awaitable_executor(this);
+ }
+
+ class awaitable_token
+ {
+ public:
+ explicit awaitable_token(awaitee_base* a)
+ : this_(a)
+ {
+ }
+
+ bool await_ready() const noexcept
+ {
+ return this_->awaiter_ != nullptr;
+ }
+
+ template <typename U, typename Ex>
+ void await_suspend(coroutine_handle<detail::awaitee<U, Ex>> h) noexcept
+ {
+ this_->resume_on_attach_ = h;
+ }
+
+ await_token<Executor> await_resume()
+ {
+ return await_token<Executor>(this_->awaiter_);
+ }
+
+ private:
+ awaitee_base* this_;
+ };
+
+ awaitable_token await_transform(this_coro::token_t) noexcept
+ {
+ return awaitable_token(this);
+ }
+
+ template <typename T>
+ awaitable<T, Executor> await_transform(awaitable<T, Executor>& t) const
+ {
+ return std::move(t);
+ }
+
+ template <typename T>
+ awaitable<T, Executor> await_transform(awaitable<T, Executor>&& t) const
+ {
+ return std::move(t);
+ }
+
+ std::experimental::suspend_always await_transform(
+ std::experimental::suspend_always) const
+ {
+ return std::experimental::suspend_always();
+ }
+
+ void attach_caller(coroutine_handle<awaiter<Executor>> h)
+ {
+ this->caller_ = h;
+ this->attach_callees(&h.promise());
+ }
+
+ template <typename U>
+ void attach_caller(coroutine_handle<awaitee<U, Executor>> h)
+ {
+ this->caller_ = h;
+ if (h.promise().awaiter_)
+ this->attach_callees(h.promise().awaiter_);
+ else
+ h.promise().unattached_callee_ = this;
+ }
+
+ void attach_callees(awaiter<Executor>* a)
+ {
+ for (awaitee_base* curr = this; curr != nullptr;
+ curr = std::exchange(curr->unattached_callee_, nullptr))
+ {
+ curr->awaiter_ = a;
+ if (curr->resume_on_attach_)
+ return std::exchange(curr->resume_on_attach_, nullptr).resume();
+ }
+ }
+
+protected:
+ awaiter<Executor>* awaiter_ = nullptr;
+ coroutine_handle<void> caller_ = nullptr;
+ awaitee_base<Executor>* unattached_callee_ = nullptr;
+ std::exception_ptr pending_exception_ = nullptr;
+ coroutine_handle<void> resume_on_attach_ = nullptr;
+ bool ready_ = false;
+};
+
+// Promise object for coroutines further down the thread-of-execution "stack".
+template <typename T, typename Executor>
+class awaitee
+ : public awaitee_base<Executor>
+{
+public:
+ awaitee()
+ {
+ }
+
+ awaitee(awaitee&& other) noexcept
+ : awaitee_base<Executor>(std::move(other))
+ {
+ }
+
+ ~awaitee()
+ {
+ if (has_result_)
+ static_cast<T*>(static_cast<void*>(result_))->~T();
+ }
+
+ awaitable<T, Executor> get_return_object()
+ {
+ return awaitable<T, Executor>(this);
+ };
+
+ template <typename U>
+ void return_value(U&& u)
+ {
+ new (&result_) T(std::forward<U>(u));
+ has_result_ = true;
+ }
+
+ T get()
+ {
+ this->caller_ = nullptr;
+ this->rethrow_exception();
+ return std::move(*static_cast<T*>(static_cast<void*>(result_)));
+ }
+
+private:
+ alignas(T) unsigned char result_[sizeof(T)];
+ bool has_result_ = false;
+};
+
+// Promise object for coroutines further down the thread-of-execution "stack".
+template <typename Executor>
+class awaitee<void, Executor>
+ : public awaitee_base<Executor>
+{
+public:
+ awaitable<void, Executor> get_return_object()
+ {
+ return awaitable<void, Executor>(this);
+ };
+
+ void return_void()
+ {
+ }
+
+ void get()
+ {
+ this->caller_ = nullptr;
+ this->rethrow_exception();
+ }
+};
+
+template <typename Executor>
+class awaiter_task
+{
+public:
+ typedef Executor executor_type;
+
+ awaiter_task(awaiter<Executor>* a)
+ : awaiter_(a->add_ref())
+ {
+ }
+
+ awaiter_task(awaiter_task&& other) noexcept
+ : awaiter_(std::exchange(other.awaiter_, nullptr))
+ {
+ }
+
+ ~awaiter_task()
+ {
+ if (awaiter_)
+ {
+ // Coroutine "stack unwinding" must be performed through the executor.
+ executor_type ex(awaiter_->get_executor());
+ (post)(ex,
+ [a = std::move(awaiter_)]() mutable
+ {
+ typename awaiter<Executor>::ptr(std::move(a));
+ });
+ }
+ }
+
+ executor_type get_executor() const noexcept
+ {
+ return awaiter_->get_executor();
+ }
+
+protected:
+ typename awaiter<Executor>::ptr awaiter_;
+};
+
+template <typename Executor>
+class co_spawn_handler : public awaiter_task<Executor>
+{
+public:
+ using awaiter_task<Executor>::awaiter_task;
+
+ void operator()()
+ {
+ typename awaiter<Executor>::ptr ptr(std::move(this->awaiter_));
+ coroutine_handle<awaiter<Executor>>::from_promise(*ptr.get()).resume();
+ }
+};
+
+template <typename Executor, typename T>
+class await_handler_base : public awaiter_task<Executor>
+{
+public:
+ typedef awaitable<T, Executor> awaitable_type;
+
+ await_handler_base(await_token<Executor> token)
+ : awaiter_task<Executor>(token.awaiter_),
+ awaitee_(nullptr)
+ {
+ }
+
+ await_handler_base(await_handler_base&& other) noexcept
+ : awaiter_task<Executor>(std::move(other)),
+ awaitee_(std::exchange(other.awaitee_, nullptr))
+ {
+ }
+
+ void attach_awaitee(const awaitable<T, Executor>& a)
+ {
+ awaitee_ = a.awaitee_;
+ }
+
+protected:
+ awaitee<T, Executor>* awaitee_;
+};
+
+template <typename, typename...> class await_handler;
+
+template <typename Executor>
+class await_handler<Executor, void>
+ : public await_handler_base<Executor, void>
+{
+public:
+ using await_handler_base<Executor, void>::await_handler_base;
+
+ void operator()()
+ {
+ typename awaiter<Executor>::ptr ptr(std::move(this->awaiter_));
+ this->awaitee_->return_void();
+ this->awaitee_->wake_caller();
+ ptr->rethrow_unhandled_exception();
+ }
+};
+
+template <typename Executor>
+class await_handler<Executor, boost::system::error_code>
+ : public await_handler_base<Executor, void>
+{
+public:
+ typedef void return_type;
+
+ using await_handler_base<Executor, void>::await_handler_base;
+
+ void operator()(const boost::system::error_code& ec)
+ {
+ typename awaiter<Executor>::ptr ptr(std::move(this->awaiter_));
+ if (ec)
+ {
+ this->awaitee_->set_except(
+ std::make_exception_ptr(boost::system::system_error(ec)));
+ }
+ else
+ this->awaitee_->return_void();
+ this->awaitee_->wake_caller();
+ ptr->rethrow_unhandled_exception();
+ }
+};
+
+template <typename Executor>
+class await_handler<Executor, std::exception_ptr>
+ : public await_handler_base<Executor, void>
+{
+public:
+ using await_handler_base<Executor, void>::await_handler_base;
+
+ void operator()(std::exception_ptr ex)
+ {
+ typename awaiter<Executor>::ptr ptr(std::move(this->awaiter_));
+ if (ex)
+ this->awaitee_->set_except(ex);
+ else
+ this->awaitee_->return_void();
+ this->awaitee_->wake_caller();
+ ptr->rethrow_unhandled_exception();
+ }
+};
+
+template <typename Executor, typename T>
+class await_handler<Executor, T>
+ : public await_handler_base<Executor, T>
+{
+public:
+ using await_handler_base<Executor, T>::await_handler_base;
+
+ template <typename Arg>
+ void operator()(Arg&& arg)
+ {
+ typename awaiter<Executor>::ptr ptr(std::move(this->awaiter_));
+ this->awaitee_->return_value(std::forward<Arg>(arg));
+ this->awaitee_->wake_caller();
+ ptr->rethrow_unhandled_exception();
+ }
+};
+
+template <typename Executor, typename T>
+class await_handler<Executor, boost::system::error_code, T>
+ : public await_handler_base<Executor, T>
+{
+public:
+ using await_handler_base<Executor, T>::await_handler_base;
+
+ template <typename Arg>
+ void operator()(const boost::system::error_code& ec, Arg&& arg)
+ {
+ typename awaiter<Executor>::ptr ptr(std::move(this->awaiter_));
+ if (ec)
+ {
+ this->awaitee_->set_except(
+ std::make_exception_ptr(boost::system::system_error(ec)));
+ }
+ else
+ this->awaitee_->return_value(std::forward<Arg>(arg));
+ this->awaitee_->wake_caller();
+ ptr->rethrow_unhandled_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<Executor, T>::await_handler_base;
+
+ template <typename Arg>
+ void operator()(std::exception_ptr ex, Arg&& arg)
+ {
+ typename awaiter<Executor>::ptr ptr(std::move(this->awaiter_));
+ if (ex)
+ this->awaitee_->set_except(ex);
+ else
+ this->awaitee_->return_value(std::forward<Arg>(arg));
+ this->awaitee_->wake_caller();
+ ptr->rethrow_unhandled_exception();
+ }
+};
+
+template <typename Executor, typename... Ts>
+class await_handler
+ : public await_handler_base<Executor, std::tuple<Ts...>>
+{
+public:
+ using await_handler_base<Executor, std::tuple<Ts...>>::await_handler_base;
+
+ template <typename... Args>
+ void operator()(Args&&... args)
+ {
+ typename awaiter<Executor>::ptr ptr(std::move(this->awaiter_));
+ this->awaitee_->return_value(
+ std::forward_as_tuple(std::forward<Args>(args)...));
+ this->awaitee_->wake_caller();
+ ptr->rethrow_unhandled_exception();
+ }
+};
+
+template <typename Executor, typename... Ts>
+class await_handler<Executor, boost::system::error_code, Ts...>
+ : public await_handler_base<Executor, std::tuple<Ts...>>
+{
+public:
+ using await_handler_base<Executor, std::tuple<Ts...>>::await_handler_base;
+
+ template <typename... Args>
+ void operator()(const boost::system::error_code& ec, Args&&... args)
+ {
+ typename awaiter<Executor>::ptr ptr(std::move(this->awaiter_));
+ if (ec)
+ {
+ this->awaitee_->set_except(
+ std::make_exception_ptr(boost::system::system_error(ec)));
+ }
+ else
+ {
+ this->awaitee_->return_value(
+ std::forward_as_tuple(std::forward<Args>(args)...));
+ }
+ this->awaitee_->wake_caller();
+ ptr->rethrow_unhandled_exception();
+ }
+};
+
+template <typename Executor, typename... Ts>
+class await_handler<Executor, std::exception_ptr, Ts...>
+ : public await_handler_base<Executor, std::tuple<Ts...>>
+{
+public:
+ using await_handler_base<Executor, std::tuple<Ts...>>::await_handler_base;
+
+ template <typename... Args>
+ void operator()(std::exception_ptr ex, Args&&... args)
+ {
+ typename awaiter<Executor>::ptr ptr(std::move(this->awaiter_));
+ if (ex)
+ this->awaitee_->set_except(ex);
+ else
+ {
+ this->awaitee_->return_value(
+ std::forward_as_tuple(std::forward<Args>(args)...));
+ }
+ this->awaitee_->wake_caller();
+ ptr->rethrow_unhandled_exception();
+ }
+};
+
+template <typename T>
+struct awaitable_signature;
+
+template <typename T, typename Executor>
+struct awaitable_signature<awaitable<T, Executor>>
+{
+ typedef void type(std::exception_ptr, T);
+};
+
+template <typename Executor>
+struct awaitable_signature<awaitable<void, Executor>>
+{
+ typedef void type(std::exception_ptr);
+};
+
+template <typename T, typename Executor, typename F, typename Handler>
+awaiter<Executor>* co_spawn_entry_point(awaitable<T, Executor>*,
+ executor_work_guard<Executor> work_guard, F f, Handler handler)
+{
+ bool done = false;
+
+ try
+ {
+ T t = co_await f();
+
+ 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 Executor, typename F, typename Handler>
+awaiter<Executor>* co_spawn_entry_point(awaitable<void, Executor>*,
+ executor_work_guard<Executor> work_guard, F f, Handler handler)
+{
+ std::exception_ptr e = nullptr;
+
+ try
+ {
+ co_await f();
+ }
+ catch (...)
+ {
+ e = std::current_exception();
+ }
+
+ (dispatch)(work_guard.get_executor(),
+ [handler = std::move(handler), e]() mutable
+ {
+ handler(e);
+ });
+}
+
+template <typename Executor, typename F, typename CompletionToken>
+auto co_spawn(const Executor& ex, F&& f, CompletionToken&& token)
+{
+ typedef typename result_of<F()>::type awaitable_type;
+ typedef typename awaitable_type::executor_type executor_type;
+ typedef typename awaitable_signature<awaitable_type>::type signature_type;
+
+ async_completion<CompletionToken, signature_type> completion(token);
+
+ executor_type ex2(ex);
+ auto work_guard = make_work_guard(completion.completion_handler, ex2);
+
+ auto* a = (co_spawn_entry_point)(
+ static_cast<awaitable_type*>(nullptr), std::move(work_guard),
+ std::forward<F>(f), std::move(completion.completion_handler));
+
+ a->set_executor(ex2);
+ (post)(co_spawn_handler<executor_type>(a));
+
+ return completion.result.get();
+}
+
+#if defined(_MSC_VER)
+# pragma warning(push)
+# pragma warning(disable:4033)
+#endif // defined(_MSC_VER)
+
+#if defined(_MSC_VER)
+template <typename T> T dummy_return()
+{
+ return std::move(*static_cast<T*>(nullptr));
+}
+
+template <>
+inline void dummy_return()
+{
+}
+#endif // defined(_MSC_VER)
+
+template <typename Awaitable>
+inline Awaitable make_dummy_awaitable()
+{
+ for (;;) co_await std::experimental::suspend_always();
+#if defined(_MSC_VER)
+ co_return dummy_return<typename Awaitable::value_type>();
+#endif // defined(_MSC_VER)
+}
+
+#if defined(_MSC_VER)
+# pragma warning(pop)
+#endif // defined(_MSC_VER)
+
+} // namespace detail
+} // namespace experimental
+
+template <typename Executor, typename R, typename... Args>
+class async_result<experimental::await_token<Executor>, R(Args...)>
+{
+public:
+ typedef experimental::detail::await_handler<
+ Executor, typename decay<Args>::type...> completion_handler_type;
+
+ typedef typename experimental::detail::await_handler<
+ Executor, Args...>::awaitable_type return_type;
+
+ async_result(completion_handler_type& h)
+ : awaitable_(experimental::detail::make_dummy_awaitable<return_type>())
+ {
+ h.attach_awaitee(awaitable_);
+ }
+
+ return_type get()
+ {
+ return std::move(awaitable_);
+ }
+
+private:
+ return_type awaitable_;
+};
+
+#if !defined(BOOST_ASIO_NO_DEPRECATED)
+
+template <typename Executor, typename R, typename... Args>
+struct handler_type<experimental::await_token<Executor>, R(Args...)>
+{
+ typedef experimental::detail::await_handler<
+ Executor, typename decay<Args>::type...> type;
+};
+
+template <typename Executor, typename... Args>
+class async_result<experimental::detail::await_handler<Executor, Args...>>
+{
+public:
+ typedef typename experimental::detail::await_handler<
+ Executor, Args...>::awaitable_type type;
+
+ async_result(experimental::detail::await_handler<Executor, Args...>& h)
+ : awaitable_(experimental::detail::make_dummy_awaitable<type>())
+ {
+ h.attach_awaitee(awaitable_);
+ }
+
+ type get()
+ {
+ return std::move(awaitable_);
+ }
+
+private:
+ type awaitable_;
+};
+
+#endif // !defined(BOOST_ASIO_NO_DEPRECATED)
+
+} // namespace asio
+} // namespace boost
+
+namespace std { namespace experimental {
+
+template <typename Executor, typename... Args>
+struct coroutine_traits<
+ boost::asio::experimental::detail::awaiter<Executor>*, Args...>
+{
+ typedef boost::asio::experimental::detail::awaiter<Executor> promise_type;
+};
+
+template <typename T, typename Executor, typename... Args>
+struct coroutine_traits<
+ boost::asio::experimental::awaitable<T, Executor>, Args...>
+{
+ typedef boost::asio::experimental::detail::awaitee<T, Executor> promise_type;
+};
+
+}} // namespace std::experimental
+
+#include <boost/asio/detail/pop_options.hpp>
+
+#endif // BOOST_ASIO_EXPERIMENTAL_IMPL_CO_SPAWN_HPP
diff --git a/boost/asio/experimental/impl/detached.hpp b/boost/asio/experimental/impl/detached.hpp
new file mode 100644
index 0000000000..c3baf20299
--- /dev/null
+++ b/boost/asio/experimental/impl/detached.hpp
@@ -0,0 +1,93 @@
+//
+// experimental/impl/detached.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2018 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 BOOST_ASIO_EXPERIMENTAL_IMPL_DETACHED_HPP
+#define BOOST_ASIO_EXPERIMENTAL_IMPL_DETACHED_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include <boost/asio/detail/config.hpp>
+#include <boost/asio/async_result.hpp>
+#include <boost/asio/detail/variadic_templates.hpp>
+#include <boost/asio/handler_type.hpp>
+#include <boost/system/system_error.hpp>
+
+#include <boost/asio/detail/push_options.hpp>
+
+namespace boost {
+namespace asio {
+namespace experimental {
+namespace detail {
+
+ // Class to adapt a detached_t as a completion handler.
+ class detached_handler
+ {
+ public:
+ detached_handler(detached_t)
+ {
+ }
+
+#if defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
+
+ template <typename... Args>
+ void operator()(Args...)
+ {
+ }
+
+#else // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
+
+ void operator()()
+ {
+ }
+
+#define BOOST_ASIO_PRIVATE_DETACHED_DEF(n) \
+ template <BOOST_ASIO_VARIADIC_TPARAMS(n)> \
+ void operator()(BOOST_ASIO_VARIADIC_BYVAL_PARAMS(n)) \
+ { \
+ } \
+ /**/
+ BOOST_ASIO_VARIADIC_GENERATE(BOOST_ASIO_PRIVATE_DETACHED_DEF)
+#undef BOOST_ASIO_PRIVATE_DETACHED_DEF
+
+#endif // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
+ };
+
+} // namespace detail
+} // namespace experimental
+
+#if !defined(GENERATING_DOCUMENTATION)
+
+template <typename Signature>
+struct async_result<experimental::detached_t, Signature>
+{
+ typedef boost::asio::experimental::detail::detached_handler
+ completion_handler_type;
+
+ typedef void return_type;
+
+ explicit async_result(completion_handler_type&)
+ {
+ }
+
+ void get()
+ {
+ }
+};
+
+#endif // !defined(GENERATING_DOCUMENTATION)
+
+} // namespace asio
+} // namespace boost
+
+#include <boost/asio/detail/pop_options.hpp>
+
+#endif // BOOST_ASIO_EXPERIMENTAL_IMPL_DETACHED_HPP
diff --git a/boost/asio/experimental/impl/redirect_error.hpp b/boost/asio/experimental/impl/redirect_error.hpp
new file mode 100644
index 0000000000..00ffcc7848
--- /dev/null
+++ b/boost/asio/experimental/impl/redirect_error.hpp
@@ -0,0 +1,296 @@
+//
+// experimental/impl/redirect_error.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2018 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 BOOST_ASIO_EXPERIMENTAL_IMPL_REDIRECT_ERROR_HPP
+#define BOOST_ASIO_EXPERIMENTAL_IMPL_REDIRECT_ERROR_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include <boost/asio/detail/config.hpp>
+#include <boost/asio/associated_executor.hpp>
+#include <boost/asio/associated_allocator.hpp>
+#include <boost/asio/async_result.hpp>
+#include <boost/asio/detail/handler_alloc_helpers.hpp>
+#include <boost/asio/detail/handler_cont_helpers.hpp>
+#include <boost/asio/detail/handler_invoke_helpers.hpp>
+#include <boost/asio/detail/type_traits.hpp>
+#include <boost/asio/detail/variadic_templates.hpp>
+#include <boost/asio/handler_type.hpp>
+#include <boost/system/system_error.hpp>
+
+#include <boost/asio/detail/push_options.hpp>
+
+namespace boost {
+namespace asio {
+namespace experimental {
+namespace detail {
+
+// Class to adapt a redirect_error_t as a completion handler.
+template <typename Handler>
+class redirect_error_handler
+{
+public:
+ template <typename CompletionToken>
+ redirect_error_handler(redirect_error_t<CompletionToken> e)
+ : ec_(e.ec_),
+ handler_(BOOST_ASIO_MOVE_CAST(CompletionToken)(e.token_))
+ {
+ }
+
+ void operator()()
+ {
+ handler_();
+ }
+
+#if defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
+
+ template <typename Arg, typename... Args>
+ typename enable_if<
+ !is_same<typename decay<Arg>::type, boost::system::error_code>::value
+ >::type
+ operator()(BOOST_ASIO_MOVE_ARG(Arg) arg, BOOST_ASIO_MOVE_ARG(Args)... args)
+ {
+ handler_(BOOST_ASIO_MOVE_CAST(Arg)(arg),
+ BOOST_ASIO_MOVE_CAST(Args)(args)...);
+ }
+
+ template <typename... Args>
+ void operator()(const boost::system::error_code& ec,
+ BOOST_ASIO_MOVE_ARG(Args)... args)
+ {
+ ec_ = ec;
+ handler_(BOOST_ASIO_MOVE_CAST(Args)(args)...);
+ }
+
+#else // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
+
+ template <typename Arg>
+ typename enable_if<
+ !is_same<typename decay<Arg>::type, boost::system::error_code>::value
+ >::type
+ operator()(BOOST_ASIO_MOVE_ARG(Arg) arg)
+ {
+ handler_(BOOST_ASIO_MOVE_CAST(Arg)(arg));
+ }
+
+ void operator()(const boost::system::error_code& ec)
+ {
+ ec_ = ec;
+ handler_();
+ }
+
+#define BOOST_ASIO_PRIVATE_REDIRECT_ERROR_DEF(n) \
+ template <typename Arg, BOOST_ASIO_VARIADIC_TPARAMS(n)> \
+ typename enable_if< \
+ !is_same<typename decay<Arg>::type, boost::system::error_code>::value \
+ >::type \
+ operator()(BOOST_ASIO_MOVE_ARG(Arg) arg, BOOST_ASIO_VARIADIC_MOVE_PARAMS(n)) \
+ { \
+ handler_(BOOST_ASIO_MOVE_CAST(Arg)(arg), \
+ BOOST_ASIO_VARIADIC_MOVE_ARGS(n)); \
+ } \
+ \
+ template <BOOST_ASIO_VARIADIC_TPARAMS(n)> \
+ void operator()(const boost::system::error_code& ec, \
+ BOOST_ASIO_VARIADIC_MOVE_PARAMS(n)) \
+ { \
+ ec_ = ec; \
+ handler_(BOOST_ASIO_VARIADIC_MOVE_ARGS(n)); \
+ } \
+ /**/
+ BOOST_ASIO_VARIADIC_GENERATE(BOOST_ASIO_PRIVATE_REDIRECT_ERROR_DEF)
+#undef BOOST_ASIO_PRIVATE_REDIRECT_ERROR_DEF
+
+#endif // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
+
+//private:
+ boost::system::error_code& ec_;
+ Handler handler_;
+};
+
+template <typename Handler>
+inline void* asio_handler_allocate(std::size_t size,
+ redirect_error_handler<Handler>* this_handler)
+{
+ return boost_asio_handler_alloc_helpers::allocate(
+ size, this_handler->handler_);
+}
+
+template <typename Handler>
+inline void asio_handler_deallocate(void* pointer, std::size_t size,
+ redirect_error_handler<Handler>* this_handler)
+{
+ boost_asio_handler_alloc_helpers::deallocate(
+ pointer, size, this_handler->handler_);
+}
+
+template <typename Handler>
+inline bool asio_handler_is_continuation(
+ redirect_error_handler<Handler>* this_handler)
+{
+ return boost_asio_handler_cont_helpers::is_continuation(
+ this_handler->handler_);
+}
+
+template <typename Function, typename Handler>
+inline void asio_handler_invoke(Function& function,
+ redirect_error_handler<Handler>* this_handler)
+{
+ boost_asio_handler_invoke_helpers::invoke(
+ function, this_handler->handler_);
+}
+
+template <typename Function, typename Handler>
+inline void asio_handler_invoke(const Function& function,
+ redirect_error_handler<Handler>* this_handler)
+{
+ boost_asio_handler_invoke_helpers::invoke(
+ function, this_handler->handler_);
+}
+
+template <typename Signature>
+struct redirect_error_signature
+{
+ typedef Signature type;
+};
+
+#if defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
+
+template <typename R, typename... Args>
+struct redirect_error_signature<R(boost::system::error_code, Args...)>
+{
+ typedef R type(Args...);
+};
+
+template <typename R, typename... Args>
+struct redirect_error_signature<R(const boost::system::error_code&, Args...)>
+{
+ typedef R type(Args...);
+};
+
+#else // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
+
+template <typename R>
+struct redirect_error_signature<R(boost::system::error_code)>
+{
+ typedef R type();
+};
+
+template <typename R>
+struct redirect_error_signature<R(const boost::system::error_code&)>
+{
+ typedef R type();
+};
+
+#define BOOST_ASIO_PRIVATE_REDIRECT_ERROR_DEF(n) \
+ template <typename R, BOOST_ASIO_VARIADIC_TPARAMS(n)> \
+ struct redirect_error_signature< \
+ R(boost::system::error_code, BOOST_ASIO_VARIADIC_TARGS(n))> \
+ { \
+ typedef R type(BOOST_ASIO_VARIADIC_TARGS(n)); \
+ }; \
+ \
+ template <typename R, BOOST_ASIO_VARIADIC_TPARAMS(n)> \
+ struct redirect_error_signature< \
+ R(const boost::system::error_code&, BOOST_ASIO_VARIADIC_TARGS(n))> \
+ { \
+ typedef R type(BOOST_ASIO_VARIADIC_TARGS(n)); \
+ }; \
+ /**/
+ BOOST_ASIO_VARIADIC_GENERATE(BOOST_ASIO_PRIVATE_REDIRECT_ERROR_DEF)
+#undef BOOST_ASIO_PRIVATE_REDIRECT_ERROR_DEF
+
+#endif // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
+
+} // namespace detail
+} // namespace experimental
+
+#if !defined(GENERATING_DOCUMENTATION)
+
+template <typename CompletionToken, typename Signature>
+struct async_result<experimental::redirect_error_t<CompletionToken>, Signature>
+ : async_result<CompletionToken,
+ typename experimental::detail::redirect_error_signature<Signature>::type>
+{
+ typedef experimental::detail::redirect_error_handler<
+ typename async_result<CompletionToken,
+ typename experimental::detail::redirect_error_signature<Signature>::type>
+ ::completion_handler_type> completion_handler_type;
+
+ explicit async_result(completion_handler_type& h)
+ : async_result<CompletionToken,
+ typename experimental::detail::redirect_error_signature<
+ Signature>::type>(h.handler_)
+ {
+ }
+};
+
+#if !defined(BOOST_ASIO_NO_DEPRECATED)
+
+template <typename CompletionToken, typename Signature>
+struct handler_type<experimental::redirect_error_t<CompletionToken>, Signature>
+{
+ typedef experimental::detail::redirect_error_handler<
+ typename async_result<CompletionToken,
+ typename experimental::detail::redirect_error_signature<Signature>::type>
+ ::completion_handler_type> type;
+};
+
+template <typename Handler>
+struct async_result<experimental::detail::redirect_error_handler<Handler> >
+ : async_result<Handler>
+{
+ explicit async_result(
+ experimental::detail::redirect_error_handler<Handler>& h)
+ : async_result<Handler>(h.handler_)
+ {
+ }
+};
+
+#endif // !defined(BOOST_ASIO_NO_DEPRECATED)
+
+template <typename Handler, typename Executor>
+struct associated_executor<
+ experimental::detail::redirect_error_handler<Handler>, Executor>
+{
+ typedef typename associated_executor<Handler, Executor>::type type;
+
+ static type get(
+ const experimental::detail::redirect_error_handler<Handler>& h,
+ const Executor& ex = Executor()) BOOST_ASIO_NOEXCEPT
+ {
+ return associated_executor<Handler, Executor>::get(h.handler_, ex);
+ }
+};
+
+template <typename Handler, typename Allocator>
+struct associated_allocator<
+ experimental::detail::redirect_error_handler<Handler>, Allocator>
+{
+ typedef typename associated_allocator<Handler, Allocator>::type type;
+
+ static type get(
+ const experimental::detail::redirect_error_handler<Handler>& h,
+ const Allocator& a = Allocator()) BOOST_ASIO_NOEXCEPT
+ {
+ return associated_allocator<Handler, Allocator>::get(h.handler_, a);
+ }
+};
+
+#endif // !defined(GENERATING_DOCUMENTATION)
+
+} // namespace asio
+} // namespace boost
+
+#include <boost/asio/detail/pop_options.hpp>
+
+#endif // BOOST_ASIO_EXPERIMENTAL_IMPL_REDIRECT_ERROR_HPP
diff --git a/boost/asio/experimental/redirect_error.hpp b/boost/asio/experimental/redirect_error.hpp
new file mode 100644
index 0000000000..a138854521
--- /dev/null
+++ b/boost/asio/experimental/redirect_error.hpp
@@ -0,0 +1,69 @@
+//
+// experimental/redirect_error.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2018 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 BOOST_ASIO_EXPERIMENTAL_REDIRECT_ERROR_HPP
+#define BOOST_ASIO_EXPERIMENTAL_REDIRECT_ERROR_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include <boost/asio/detail/config.hpp>
+#include <boost/asio/detail/type_traits.hpp>
+#include <boost/system/error_code.hpp>
+
+#include <boost/asio/detail/push_options.hpp>
+
+namespace boost {
+namespace asio {
+namespace experimental {
+
+/// Completion token type used to specify that an error produced by an
+/// asynchronous operation is captured to an error_code variable.
+/**
+ * The redirect_error_t class is used to indicate that any error_code produced
+ * by an asynchronous operation is captured to a specified variable.
+ */
+template <typename CompletionToken>
+class redirect_error_t
+{
+public:
+ /// Constructor.
+ template <typename T>
+ redirect_error_t(BOOST_ASIO_MOVE_ARG(T) completion_token,
+ boost::system::error_code& ec)
+ : token_(BOOST_ASIO_MOVE_CAST(T)(completion_token)),
+ ec_(ec)
+ {
+ }
+
+//private:
+ CompletionToken token_;
+ boost::system::error_code& ec_;
+};
+
+/// Create a completion token to capture error_code values to a variable.
+template <typename CompletionToken>
+inline redirect_error_t<typename decay<CompletionToken>::type> redirect_error(
+ CompletionToken&& completion_token, boost::system::error_code& ec)
+{
+ return redirect_error_t<typename decay<CompletionToken>::type>(
+ BOOST_ASIO_MOVE_CAST(CompletionToken)(completion_token), ec);
+}
+
+} // namespace experimental
+} // namespace asio
+} // namespace boost
+
+#include <boost/asio/detail/pop_options.hpp>
+
+#include <boost/asio/experimental/impl/redirect_error.hpp>
+
+#endif // BOOST_ASIO_EXPERIMENTAL_REDIRECT_ERROR_HPP