5 // Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
7 // Distributed under the Boost Software License, Version 1.0. (See accompanying
8 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
11 #ifndef BOOST_ASIO_IMPL_AWAITABLE_HPP
12 #define BOOST_ASIO_IMPL_AWAITABLE_HPP
14 #if defined(_MSC_VER) && (_MSC_VER >= 1200)
16 #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
18 #include <boost/asio/detail/config.hpp>
23 #include <boost/asio/detail/thread_context.hpp>
24 #include <boost/asio/detail/thread_info_base.hpp>
25 #include <boost/asio/detail/type_traits.hpp>
26 #include <boost/asio/post.hpp>
27 #include <boost/system/system_error.hpp>
28 #include <boost/asio/this_coro.hpp>
30 #include <boost/asio/detail/push_options.hpp>
36 // An awaitable_thread represents a thread-of-execution that is composed of one
37 // or more "stack frames", with each frame represented by an awaitable_frame.
38 // All execution occurs in the context of the awaitable_thread's executor. An
39 // awaitable_thread continues to "pump" the stack frames by repeatedly resuming
40 // the top stack frame until the stack is empty, or until ownership of the
41 // stack is transferred to another awaitable_thread object.
43 // +------------------------------------+
46 // +--------------+---+ +-----------------+
48 // | awaitable_thread |<---------------------------+ awaitable_frame |
49 // | | attached_thread_ | |
50 // +--------------+---+ (Set only when +---+-------------+
51 // | frames are being |
52 // | actively pumped | caller_
53 // | by a thread, and |
55 // | the top frame.) +-----------------+
57 // | | awaitable_frame |
59 // | +---+-------------+
66 // | +-----------------+
67 // | bottom_of_stack_ | |
68 // +------------------------------->| awaitable_frame |
70 // +-----------------+
72 template <typename Executor>
73 class awaitable_frame_base
76 #if !defined(BOOST_ASIO_DISABLE_AWAITABLE_FRAME_RECYCLING)
77 void* operator new(std::size_t size)
79 return boost::asio::detail::thread_info_base::allocate(
80 boost::asio::detail::thread_info_base::awaitable_frame_tag(),
81 boost::asio::detail::thread_context::thread_call_stack::top(),
85 void operator delete(void* pointer, std::size_t size)
87 boost::asio::detail::thread_info_base::deallocate(
88 boost::asio::detail::thread_info_base::awaitable_frame_tag(),
89 boost::asio::detail::thread_context::thread_call_stack::top(),
92 #endif // !defined(BOOST_ASIO_DISABLE_AWAITABLE_FRAME_RECYCLING)
94 // The frame starts in a suspended state until the awaitable_thread object
96 auto initial_suspend() noexcept
98 return suspend_always();
101 // On final suspension the frame is popped from the top of the stack.
102 auto final_suspend() noexcept
106 awaitable_frame_base* this_;
108 bool await_ready() const noexcept
113 void await_suspend(coroutine_handle<void>) noexcept
118 void await_resume() const noexcept
126 void set_except(std::exception_ptr e) noexcept
128 pending_exception_ = e;
131 void set_error(const boost::system::error_code& ec)
133 this->set_except(std::make_exception_ptr(boost::system::system_error(ec)));
136 void unhandled_exception()
138 set_except(std::current_exception());
141 void rethrow_exception()
143 if (pending_exception_)
145 std::exception_ptr ex = std::exchange(pending_exception_, nullptr);
146 std::rethrow_exception(ex);
150 template <typename T>
151 auto await_transform(awaitable<T, Executor> a) const
156 // This await transformation obtains the associated executor of the thread of
158 auto await_transform(this_coro::executor_t) noexcept
162 awaitable_frame_base* this_;
164 bool await_ready() const noexcept
169 void await_suspend(coroutine_handle<void>) noexcept
173 auto await_resume() const noexcept
175 return this_->attached_thread_->get_executor();
182 // This await transformation is used to run an async operation's initiation
183 // function object after the coroutine has been suspended. This ensures that
184 // immediate resumption of the coroutine in another thread does not cause a
186 template <typename Function>
187 auto await_transform(Function f,
190 typename result_of<Function(awaitable_frame_base*)>::type,
191 awaitable_thread<Executor>*
198 awaitable_frame_base* this_;
200 bool await_ready() const noexcept
205 void await_suspend(coroutine_handle<void>) noexcept
210 void await_resume() const noexcept
215 return result{std::move(f), this};
218 void attach_thread(awaitable_thread<Executor>* handler) noexcept
220 attached_thread_ = handler;
223 awaitable_thread<Executor>* detach_thread() noexcept
225 return std::exchange(attached_thread_, nullptr);
228 void push_frame(awaitable_frame_base<Executor>* caller) noexcept
231 attached_thread_ = caller_->attached_thread_;
232 attached_thread_->top_of_stack_ = this;
233 caller_->attached_thread_ = nullptr;
236 void pop_frame() noexcept
239 caller_->attached_thread_ = attached_thread_;
240 attached_thread_->top_of_stack_ = caller_;
241 attached_thread_ = nullptr;
256 coroutine_handle<void> coro_ = nullptr;
257 awaitable_thread<Executor>* attached_thread_ = nullptr;
258 awaitable_frame_base<Executor>* caller_ = nullptr;
259 std::exception_ptr pending_exception_ = nullptr;
262 template <typename T, typename Executor>
263 class awaitable_frame
264 : public awaitable_frame_base<Executor>
267 awaitable_frame() noexcept
271 awaitable_frame(awaitable_frame&& other) noexcept
272 : awaitable_frame_base<Executor>(std::move(other))
279 static_cast<T*>(static_cast<void*>(result_))->~T();
282 awaitable<T, Executor> get_return_object() noexcept
284 this->coro_ = coroutine_handle<awaitable_frame>::from_promise(*this);
285 return awaitable<T, Executor>(this);
288 template <typename U>
289 void return_value(U&& u)
291 new (&result_) T(std::forward<U>(u));
295 template <typename... Us>
296 void return_values(Us&&... us)
298 this->return_value(std::forward_as_tuple(std::forward<Us>(us)...));
303 this->caller_ = nullptr;
304 this->rethrow_exception();
305 return std::move(*static_cast<T*>(static_cast<void*>(result_)));
309 alignas(T) unsigned char result_[sizeof(T)];
310 bool has_result_ = false;
313 template <typename Executor>
314 class awaitable_frame<void, Executor>
315 : public awaitable_frame_base<Executor>
318 awaitable<void, Executor> get_return_object()
320 this->coro_ = coroutine_handle<awaitable_frame>::from_promise(*this);
321 return awaitable<void, Executor>(this);
330 this->caller_ = nullptr;
331 this->rethrow_exception();
335 template <typename Executor>
336 class awaitable_thread
339 typedef Executor executor_type;
341 // Construct from the entry point of a new thread of execution.
342 awaitable_thread(awaitable<void, Executor> p, const Executor& ex)
343 : bottom_of_stack_(std::move(p)),
344 top_of_stack_(bottom_of_stack_.frame_),
349 // Transfer ownership from another awaitable_thread.
350 awaitable_thread(awaitable_thread&& other) noexcept
351 : bottom_of_stack_(std::move(other.bottom_of_stack_)),
352 top_of_stack_(std::exchange(other.top_of_stack_, nullptr)),
353 executor_(std::move(other.executor_))
357 // Clean up with a last ditch effort to ensure the thread is unwound within
358 // the context of the executor.
361 if (bottom_of_stack_.valid())
363 // Coroutine "stack unwinding" must be performed through the executor.
365 [a = std::move(bottom_of_stack_)]() mutable
367 awaitable<void, Executor>(std::move(a));
372 executor_type get_executor() const noexcept
377 // Launch a new thread of execution.
380 top_of_stack_->attach_thread(this);
385 template <typename> friend class awaitable_frame_base;
387 // Repeatedly resume the top stack frame until the stack is empty or until it
388 // has been transferred to another resumable_thread object.
391 do top_of_stack_->resume(); while (top_of_stack_);
392 if (bottom_of_stack_.valid())
394 awaitable<void, Executor> a(std::move(bottom_of_stack_));
395 a.frame_->rethrow_exception();
399 awaitable<void, Executor> bottom_of_stack_;
400 awaitable_frame_base<Executor>* top_of_stack_;
401 executor_type executor_;
404 } // namespace detail
408 #if !defined(GENERATING_DOCUMENTATION)
410 namespace std { namespace experimental {
412 template <typename T, typename Executor, typename... Args>
413 struct coroutine_traits<boost::asio::awaitable<T, Executor>, Args...>
415 typedef boost::asio::detail::awaitable_frame<T, Executor> promise_type;
418 }} // namespace std::experimental
420 #endif // !defined(GENERATING_DOCUMENTATION)
422 #include <boost/asio/detail/pop_options.hpp>
424 #endif // BOOST_ASIO_IMPL_AWAITABLE_HPP