Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:29:09

0001 //
0002 // coroutine.hpp
0003 // ~~~~~~~~~~~~~
0004 //
0005 // Copyright (c) 2003-2023 Christopher M. Kohlhoff (chris at kohlhoff dot com)
0006 //
0007 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0008 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0009 //
0010 
0011 #ifndef BOOST_ASIO_COROUTINE_HPP
0012 #define BOOST_ASIO_COROUTINE_HPP
0013 
0014 namespace boost {
0015 namespace asio {
0016 namespace detail {
0017 
0018 class coroutine_ref;
0019 
0020 } // namespace detail
0021 
0022 /// Provides support for implementing stackless coroutines.
0023 /**
0024  * The @c coroutine class may be used to implement stackless coroutines. The
0025  * class itself is used to store the current state of the coroutine.
0026  *
0027  * Coroutines are copy-constructible and assignable, and the space overhead is
0028  * a single int. They can be used as a base class:
0029  *
0030  * @code class session : coroutine
0031  * {
0032  *   ...
0033  * }; @endcode
0034  *
0035  * or as a data member:
0036  *
0037  * @code class session
0038  * {
0039  *   ...
0040  *   coroutine coro_;
0041  * }; @endcode
0042  *
0043  * or even bound in as a function argument using lambdas or @c bind(). The
0044  * important thing is that as the application maintains a copy of the object
0045  * for as long as the coroutine must be kept alive.
0046  *
0047  * @par Pseudo-keywords
0048  *
0049  * A coroutine is used in conjunction with certain "pseudo-keywords", which
0050  * are implemented as macros. These macros are defined by a header file:
0051  *
0052  * @code #include <boost/asio/yield.hpp>@endcode
0053  *
0054  * and may conversely be undefined as follows:
0055  *
0056  * @code #include <boost/asio/unyield.hpp>@endcode
0057  *
0058  * <b>reenter</b>
0059  *
0060  * The @c reenter macro is used to define the body of a coroutine. It takes a
0061  * single argument: a pointer or reference to a coroutine object. For example,
0062  * if the base class is a coroutine object you may write:
0063  *
0064  * @code reenter (this)
0065  * {
0066  *   ... coroutine body ...
0067  * } @endcode
0068  *
0069  * and if a data member or other variable you can write:
0070  *
0071  * @code reenter (coro_)
0072  * {
0073  *   ... coroutine body ...
0074  * } @endcode
0075  *
0076  * When @c reenter is executed at runtime, control jumps to the location of the
0077  * last @c yield or @c fork.
0078  *
0079  * The coroutine body may also be a single statement, such as:
0080  *
0081  * @code reenter (this) for (;;)
0082  * {
0083  *   ...
0084  * } @endcode
0085  *
0086  * @b Limitation: The @c reenter macro is implemented using a switch. This
0087  * means that you must take care when using local variables within the
0088  * coroutine body. The local variable is not allowed in a position where
0089  * reentering the coroutine could bypass the variable definition.
0090  *
0091  * <b>yield <em>statement</em></b>
0092  *
0093  * This form of the @c yield keyword is often used with asynchronous operations:
0094  *
0095  * @code yield socket_->async_read_some(buffer(*buffer_), *this); @endcode
0096  *
0097  * This divides into four logical steps:
0098  *
0099  * @li @c yield saves the current state of the coroutine.
0100  * @li The statement initiates the asynchronous operation.
0101  * @li The resume point is defined immediately following the statement.
0102  * @li Control is transferred to the end of the coroutine body.
0103  *
0104  * When the asynchronous operation completes, the function object is invoked
0105  * and @c reenter causes control to transfer to the resume point. It is
0106  * important to remember to carry the coroutine state forward with the
0107  * asynchronous operation. In the above snippet, the current class is a
0108  * function object object with a coroutine object as base class or data member.
0109  *
0110  * The statement may also be a compound statement, and this permits us to
0111  * define local variables with limited scope:
0112  *
0113  * @code yield
0114  * {
0115  *   mutable_buffers_1 b = buffer(*buffer_);
0116  *   socket_->async_read_some(b, *this);
0117  * } @endcode
0118  *
0119  * <b>yield return <em>expression</em> ;</b>
0120  *
0121  * This form of @c yield is often used in generators or coroutine-based parsers.
0122  * For example, the function object:
0123  *
0124  * @code struct interleave : coroutine
0125  * {
0126  *   istream& is1;
0127  *   istream& is2;
0128  *   char operator()(char c)
0129  *   {
0130  *     reenter (this) for (;;)
0131  *     {
0132  *       yield return is1.get();
0133  *       yield return is2.get();
0134  *     }
0135  *   }
0136  * }; @endcode
0137  *
0138  * defines a trivial coroutine that interleaves the characters from two input
0139  * streams.
0140  *
0141  * This type of @c yield divides into three logical steps:
0142  *
0143  * @li @c yield saves the current state of the coroutine.
0144  * @li The resume point is defined immediately following the semicolon.
0145  * @li The value of the expression is returned from the function.
0146  *
0147  * <b>yield ;</b>
0148  *
0149  * This form of @c yield is equivalent to the following steps:
0150  *
0151  * @li @c yield saves the current state of the coroutine.
0152  * @li The resume point is defined immediately following the semicolon.
0153  * @li Control is transferred to the end of the coroutine body.
0154  *
0155  * This form might be applied when coroutines are used for cooperative
0156  * threading and scheduling is explicitly managed. For example:
0157  *
0158  * @code struct task : coroutine
0159  * {
0160  *   ...
0161  *   void operator()()
0162  *   {
0163  *     reenter (this)
0164  *     {
0165  *       while (... not finished ...)
0166  *       {
0167  *         ... do something ...
0168  *         yield;
0169  *         ... do some more ...
0170  *         yield;
0171  *       }
0172  *     }
0173  *   }
0174  *   ...
0175  * };
0176  * ...
0177  * task t1, t2;
0178  * for (;;)
0179  * {
0180  *   t1();
0181  *   t2();
0182  * } @endcode
0183  *
0184  * <b>yield break ;</b>
0185  *
0186  * The final form of @c yield is used to explicitly terminate the coroutine.
0187  * This form is comprised of two steps:
0188  *
0189  * @li @c yield sets the coroutine state to indicate termination.
0190  * @li Control is transferred to the end of the coroutine body.
0191  *
0192  * Once terminated, calls to is_complete() return true and the coroutine cannot
0193  * be reentered.
0194  *
0195  * Note that a coroutine may also be implicitly terminated if the coroutine
0196  * body is exited without a yield, e.g. by return, throw or by running to the
0197  * end of the body.
0198  *
0199  * <b>fork <em>statement</em></b>
0200  *
0201  * The @c fork pseudo-keyword is used when "forking" a coroutine, i.e. splitting
0202  * it into two (or more) copies. One use of @c fork is in a server, where a new
0203  * coroutine is created to handle each client connection:
0204  * 
0205  * @code reenter (this)
0206  * {
0207  *   do
0208  *   {
0209  *     socket_.reset(new tcp::socket(my_context_));
0210  *     yield acceptor->async_accept(*socket_, *this);
0211  *     fork server(*this)();
0212  *   } while (is_parent());
0213  *   ... client-specific handling follows ...
0214  * } @endcode
0215  * 
0216  * The logical steps involved in a @c fork are:
0217  * 
0218  * @li @c fork saves the current state of the coroutine.
0219  * @li The statement creates a copy of the coroutine and either executes it
0220  *     immediately or schedules it for later execution.
0221  * @li The resume point is defined immediately following the semicolon.
0222  * @li For the "parent", control immediately continues from the next line.
0223  *
0224  * The functions is_parent() and is_child() can be used to differentiate
0225  * between parent and child. You would use these functions to alter subsequent
0226  * control flow.
0227  *
0228  * Note that @c fork doesn't do the actual forking by itself. It is the
0229  * application's responsibility to create a clone of the coroutine and call it.
0230  * The clone can be called immediately, as above, or scheduled for delayed
0231  * execution using something like boost::asio::post().
0232  *
0233  * @par Alternate macro names
0234  *
0235  * If preferred, an application can use macro names that follow a more typical
0236  * naming convention, rather than the pseudo-keywords. These are:
0237  *
0238  * @li @c BOOST_ASIO_CORO_REENTER instead of @c reenter
0239  * @li @c BOOST_ASIO_CORO_YIELD instead of @c yield
0240  * @li @c BOOST_ASIO_CORO_FORK instead of @c fork
0241  */
0242 class coroutine
0243 {
0244 public:
0245   /// Constructs a coroutine in its initial state.
0246   coroutine() : value_(0) {}
0247 
0248   /// Returns true if the coroutine is the child of a fork.
0249   bool is_child() const { return value_ < 0; }
0250 
0251   /// Returns true if the coroutine is the parent of a fork.
0252   bool is_parent() const { return !is_child(); }
0253 
0254   /// Returns true if the coroutine has reached its terminal state.
0255   bool is_complete() const { return value_ == -1; }
0256 
0257 private:
0258   friend class detail::coroutine_ref;
0259   int value_;
0260 };
0261 
0262 
0263 namespace detail {
0264 
0265 class coroutine_ref
0266 {
0267 public:
0268   coroutine_ref(coroutine& c) : value_(c.value_), modified_(false) {}
0269   coroutine_ref(coroutine* c) : value_(c->value_), modified_(false) {}
0270   ~coroutine_ref() { if (!modified_) value_ = -1; }
0271   operator int() const { return value_; }
0272   int& operator=(int v) { modified_ = true; return value_ = v; }
0273 private:
0274   void operator=(const coroutine_ref&);
0275   int& value_;
0276   bool modified_;
0277 };
0278 
0279 } // namespace detail
0280 } // namespace asio
0281 } // namespace boost
0282 
0283 #define BOOST_ASIO_CORO_REENTER(c) \
0284   switch (::boost::asio::detail::coroutine_ref _coro_value = c) \
0285     case -1: if (_coro_value) \
0286     { \
0287       goto terminate_coroutine; \
0288       terminate_coroutine: \
0289       _coro_value = -1; \
0290       goto bail_out_of_coroutine; \
0291       bail_out_of_coroutine: \
0292       break; \
0293     } \
0294     else /* fall-through */ case 0:
0295 
0296 #define BOOST_ASIO_CORO_YIELD_IMPL(n) \
0297   for (_coro_value = (n);;) \
0298     if (_coro_value == 0) \
0299     { \
0300       case (n): ; \
0301       break; \
0302     } \
0303     else \
0304       switch (_coro_value ? 0 : 1) \
0305         for (;;) \
0306           /* fall-through */ case -1: if (_coro_value) \
0307             goto terminate_coroutine; \
0308           else for (;;) \
0309             /* fall-through */ case 1: if (_coro_value) \
0310               goto bail_out_of_coroutine; \
0311             else /* fall-through */ case 0:
0312 
0313 #define BOOST_ASIO_CORO_FORK_IMPL(n) \
0314   for (_coro_value = -(n);; _coro_value = (n)) \
0315     if (_coro_value == (n)) \
0316     { \
0317       case -(n): ; \
0318       break; \
0319     } \
0320     else
0321 
0322 #if defined(_MSC_VER)
0323 # define BOOST_ASIO_CORO_YIELD BOOST_ASIO_CORO_YIELD_IMPL(__COUNTER__ + 1)
0324 # define BOOST_ASIO_CORO_FORK BOOST_ASIO_CORO_FORK_IMPL(__COUNTER__ + 1)
0325 #else // defined(_MSC_VER)
0326 # define BOOST_ASIO_CORO_YIELD BOOST_ASIO_CORO_YIELD_IMPL(__LINE__)
0327 # define BOOST_ASIO_CORO_FORK BOOST_ASIO_CORO_FORK_IMPL(__LINE__)
0328 #endif // defined(_MSC_VER)
0329 
0330 #endif // BOOST_ASIO_COROUTINE_HPP