Chris@16: // Chris@16: // coroutine.hpp Chris@16: // ~~~~~~~~~~~~~ Chris@16: // Chris@101: // Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com) Chris@16: // Chris@16: // Distributed under the Boost Software License, Version 1.0. (See accompanying Chris@16: // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) Chris@16: // Chris@16: Chris@16: #ifndef BOOST_ASIO_COROUTINE_HPP Chris@16: #define BOOST_ASIO_COROUTINE_HPP Chris@16: Chris@16: namespace boost { Chris@16: namespace asio { Chris@16: namespace detail { Chris@16: Chris@16: class coroutine_ref; Chris@16: Chris@16: } // namespace detail Chris@16: Chris@16: /// Provides support for implementing stackless coroutines. Chris@16: /** Chris@16: * The @c coroutine class may be used to implement stackless coroutines. The Chris@16: * class itself is used to store the current state of the coroutine. Chris@16: * Chris@16: * Coroutines are copy-constructible and assignable, and the space overhead is Chris@16: * a single int. They can be used as a base class: Chris@16: * Chris@16: * @code class session : coroutine Chris@16: * { Chris@16: * ... Chris@16: * }; @endcode Chris@16: * Chris@16: * or as a data member: Chris@16: * Chris@16: * @code class session Chris@16: * { Chris@16: * ... Chris@16: * coroutine coro_; Chris@16: * }; @endcode Chris@16: * Chris@16: * or even bound in as a function argument using lambdas or @c bind(). The Chris@16: * important thing is that as the application maintains a copy of the object Chris@16: * for as long as the coroutine must be kept alive. Chris@16: * Chris@16: * @par Pseudo-keywords Chris@16: * Chris@16: * A coroutine is used in conjunction with certain "pseudo-keywords", which Chris@16: * are implemented as macros. These macros are defined by a header file: Chris@16: * Chris@16: * @code #include @endcode Chris@16: * Chris@16: * and may conversely be undefined as follows: Chris@16: * Chris@16: * @code #include @endcode Chris@16: * Chris@16: * reenter Chris@16: * Chris@16: * The @c reenter macro is used to define the body of a coroutine. It takes a Chris@16: * single argument: a pointer or reference to a coroutine object. For example, Chris@16: * if the base class is a coroutine object you may write: Chris@16: * Chris@16: * @code reenter (this) Chris@16: * { Chris@16: * ... coroutine body ... Chris@16: * } @endcode Chris@16: * Chris@16: * and if a data member or other variable you can write: Chris@16: * Chris@16: * @code reenter (coro_) Chris@16: * { Chris@16: * ... coroutine body ... Chris@16: * } @endcode Chris@16: * Chris@16: * When @c reenter is executed at runtime, control jumps to the location of the Chris@16: * last @c yield or @c fork. Chris@16: * Chris@16: * The coroutine body may also be a single statement, such as: Chris@16: * Chris@16: * @code reenter (this) for (;;) Chris@16: * { Chris@16: * ... Chris@16: * } @endcode Chris@16: * Chris@16: * @b Limitation: The @c reenter macro is implemented using a switch. This Chris@16: * means that you must take care when using local variables within the Chris@16: * coroutine body. The local variable is not allowed in a position where Chris@16: * reentering the coroutine could bypass the variable definition. Chris@16: * Chris@16: * yield statement Chris@16: * Chris@16: * This form of the @c yield keyword is often used with asynchronous operations: Chris@16: * Chris@16: * @code yield socket_->async_read_some(buffer(*buffer_), *this); @endcode Chris@16: * Chris@16: * This divides into four logical steps: Chris@16: * Chris@16: * @li @c yield saves the current state of the coroutine. Chris@16: * @li The statement initiates the asynchronous operation. Chris@16: * @li The resume point is defined immediately following the statement. Chris@16: * @li Control is transferred to the end of the coroutine body. Chris@16: * Chris@16: * When the asynchronous operation completes, the function object is invoked Chris@16: * and @c reenter causes control to transfer to the resume point. It is Chris@16: * important to remember to carry the coroutine state forward with the Chris@16: * asynchronous operation. In the above snippet, the current class is a Chris@16: * function object object with a coroutine object as base class or data member. Chris@16: * Chris@16: * The statement may also be a compound statement, and this permits us to Chris@16: * define local variables with limited scope: Chris@16: * Chris@16: * @code yield Chris@16: * { Chris@16: * mutable_buffers_1 b = buffer(*buffer_); Chris@16: * socket_->async_read_some(b, *this); Chris@16: * } @endcode Chris@16: * Chris@16: * yield return expression ; Chris@16: * Chris@16: * This form of @c yield is often used in generators or coroutine-based parsers. Chris@16: * For example, the function object: Chris@16: * Chris@16: * @code struct interleave : coroutine Chris@16: * { Chris@16: * istream& is1; Chris@16: * istream& is2; Chris@16: * char operator()(char c) Chris@16: * { Chris@16: * reenter (this) for (;;) Chris@16: * { Chris@16: * yield return is1.get(); Chris@16: * yield return is2.get(); Chris@16: * } Chris@16: * } Chris@16: * }; @endcode Chris@16: * Chris@16: * defines a trivial coroutine that interleaves the characters from two input Chris@16: * streams. Chris@16: * Chris@16: * This type of @c yield divides into three logical steps: Chris@16: * Chris@16: * @li @c yield saves the current state of the coroutine. Chris@16: * @li The resume point is defined immediately following the semicolon. Chris@16: * @li The value of the expression is returned from the function. Chris@16: * Chris@16: * yield ; Chris@16: * Chris@16: * This form of @c yield is equivalent to the following steps: Chris@16: * Chris@16: * @li @c yield saves the current state of the coroutine. Chris@16: * @li The resume point is defined immediately following the semicolon. Chris@16: * @li Control is transferred to the end of the coroutine body. Chris@16: * Chris@16: * This form might be applied when coroutines are used for cooperative Chris@16: * threading and scheduling is explicitly managed. For example: Chris@16: * Chris@16: * @code struct task : coroutine Chris@16: * { Chris@16: * ... Chris@16: * void operator()() Chris@16: * { Chris@16: * reenter (this) Chris@16: * { Chris@16: * while (... not finished ...) Chris@16: * { Chris@16: * ... do something ... Chris@16: * yield; Chris@16: * ... do some more ... Chris@16: * yield; Chris@16: * } Chris@16: * } Chris@16: * } Chris@16: * ... Chris@16: * }; Chris@16: * ... Chris@16: * task t1, t2; Chris@16: * for (;;) Chris@16: * { Chris@16: * t1(); Chris@16: * t2(); Chris@16: * } @endcode Chris@16: * Chris@16: * yield break ; Chris@16: * Chris@16: * The final form of @c yield is used to explicitly terminate the coroutine. Chris@16: * This form is comprised of two steps: Chris@16: * Chris@16: * @li @c yield sets the coroutine state to indicate termination. Chris@16: * @li Control is transferred to the end of the coroutine body. Chris@16: * Chris@16: * Once terminated, calls to is_complete() return true and the coroutine cannot Chris@16: * be reentered. Chris@16: * Chris@16: * Note that a coroutine may also be implicitly terminated if the coroutine Chris@16: * body is exited without a yield, e.g. by return, throw or by running to the Chris@16: * end of the body. Chris@16: * Chris@16: * fork statement Chris@16: * Chris@16: * The @c fork pseudo-keyword is used when "forking" a coroutine, i.e. splitting Chris@16: * it into two (or more) copies. One use of @c fork is in a server, where a new Chris@16: * coroutine is created to handle each client connection: Chris@16: * Chris@16: * @code reenter (this) Chris@16: * { Chris@16: * do Chris@16: * { Chris@16: * socket_.reset(new tcp::socket(io_service_)); Chris@16: * yield acceptor->async_accept(*socket_, *this); Chris@16: * fork server(*this)(); Chris@16: * } while (is_parent()); Chris@16: * ... client-specific handling follows ... Chris@16: * } @endcode Chris@16: * Chris@16: * The logical steps involved in a @c fork are: Chris@16: * Chris@16: * @li @c fork saves the current state of the coroutine. Chris@16: * @li The statement creates a copy of the coroutine and either executes it Chris@16: * immediately or schedules it for later execution. Chris@16: * @li The resume point is defined immediately following the semicolon. Chris@16: * @li For the "parent", control immediately continues from the next line. Chris@16: * Chris@16: * The functions is_parent() and is_child() can be used to differentiate Chris@16: * between parent and child. You would use these functions to alter subsequent Chris@16: * control flow. Chris@16: * Chris@16: * Note that @c fork doesn't do the actual forking by itself. It is the Chris@16: * application's responsibility to create a clone of the coroutine and call it. Chris@16: * The clone can be called immediately, as above, or scheduled for delayed Chris@16: * execution using something like io_service::post(). Chris@16: * Chris@16: * @par Alternate macro names Chris@16: * Chris@16: * If preferred, an application can use macro names that follow a more typical Chris@16: * naming convention, rather than the pseudo-keywords. These are: Chris@16: * Chris@16: * @li @c BOOST_ASIO_CORO_REENTER instead of @c reenter Chris@16: * @li @c BOOST_ASIO_CORO_YIELD instead of @c yield Chris@16: * @li @c BOOST_ASIO_CORO_FORK instead of @c fork Chris@16: */ Chris@16: class coroutine Chris@16: { Chris@16: public: Chris@16: /// Constructs a coroutine in its initial state. Chris@16: coroutine() : value_(0) {} Chris@16: Chris@16: /// Returns true if the coroutine is the child of a fork. Chris@16: bool is_child() const { return value_ < 0; } Chris@16: Chris@16: /// Returns true if the coroutine is the parent of a fork. Chris@16: bool is_parent() const { return !is_child(); } Chris@16: Chris@16: /// Returns true if the coroutine has reached its terminal state. Chris@16: bool is_complete() const { return value_ == -1; } Chris@16: Chris@16: private: Chris@16: friend class detail::coroutine_ref; Chris@16: int value_; Chris@16: }; Chris@16: Chris@16: Chris@16: namespace detail { Chris@16: Chris@16: class coroutine_ref Chris@16: { Chris@16: public: Chris@16: coroutine_ref(coroutine& c) : value_(c.value_), modified_(false) {} Chris@16: coroutine_ref(coroutine* c) : value_(c->value_), modified_(false) {} Chris@16: ~coroutine_ref() { if (!modified_) value_ = -1; } Chris@16: operator int() const { return value_; } Chris@16: int& operator=(int v) { modified_ = true; return value_ = v; } Chris@16: private: Chris@16: void operator=(const coroutine_ref&); Chris@16: int& value_; Chris@16: bool modified_; Chris@16: }; Chris@16: Chris@16: } // namespace detail Chris@16: } // namespace asio Chris@16: } // namespace boost Chris@16: Chris@16: #define BOOST_ASIO_CORO_REENTER(c) \ Chris@16: switch (::boost::asio::detail::coroutine_ref _coro_value = c) \ Chris@16: case -1: if (_coro_value) \ Chris@16: { \ Chris@16: goto terminate_coroutine; \ Chris@16: terminate_coroutine: \ Chris@16: _coro_value = -1; \ Chris@16: goto bail_out_of_coroutine; \ Chris@16: bail_out_of_coroutine: \ Chris@16: break; \ Chris@16: } \ Chris@16: else case 0: Chris@16: Chris@16: #define BOOST_ASIO_CORO_YIELD_IMPL(n) \ Chris@16: for (_coro_value = (n);;) \ Chris@16: if (_coro_value == 0) \ Chris@16: { \ Chris@16: case (n): ; \ Chris@16: break; \ Chris@16: } \ Chris@16: else \ Chris@16: switch (_coro_value ? 0 : 1) \ Chris@16: for (;;) \ Chris@16: case -1: if (_coro_value) \ Chris@16: goto terminate_coroutine; \ Chris@16: else for (;;) \ Chris@16: case 1: if (_coro_value) \ Chris@16: goto bail_out_of_coroutine; \ Chris@16: else case 0: Chris@16: Chris@16: #define BOOST_ASIO_CORO_FORK_IMPL(n) \ Chris@16: for (_coro_value = -(n);; _coro_value = (n)) \ Chris@16: if (_coro_value == (n)) \ Chris@16: { \ Chris@16: case -(n): ; \ Chris@16: break; \ Chris@16: } \ Chris@16: else Chris@16: Chris@16: #if defined(_MSC_VER) Chris@16: # define BOOST_ASIO_CORO_YIELD BOOST_ASIO_CORO_YIELD_IMPL(__COUNTER__ + 1) Chris@16: # define BOOST_ASIO_CORO_FORK BOOST_ASIO_CORO_FORK_IMPL(__COUNTER__ + 1) Chris@16: #else // defined(_MSC_VER) Chris@16: # define BOOST_ASIO_CORO_YIELD BOOST_ASIO_CORO_YIELD_IMPL(__LINE__) Chris@16: # define BOOST_ASIO_CORO_FORK BOOST_ASIO_CORO_FORK_IMPL(__LINE__) Chris@16: #endif // defined(_MSC_VER) Chris@16: Chris@16: #endif // BOOST_ASIO_COROUTINE_HPP