]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#293,!45] Add coroutine.hpp back to the kea sources
authorTomek Mrugalski <tomasz@isc.org>
Wed, 28 Nov 2018 16:52:00 +0000 (17:52 +0100)
committerTomek Mrugalski <tomasz@isc.org>
Wed, 28 Nov 2018 20:17:38 +0000 (21:17 +0100)
COPYING
Makefile.am
configure.ac
ext/Makefile.am
ext/coroutine/LICENSE_1_0.txt [new file with mode: 0644]
ext/coroutine/Makefile.am [new file with mode: 0644]
ext/coroutine/coroutine.hpp [new file with mode: 0644]
m4macros/ax_boost_for_kea.m4
src/lib/asiodns/io_fetch.h
src/lib/asiolink/io_asio_socket.h

diff --git a/COPYING b/COPYING
index 0fdf7c5f4599322b14d4d42a2b2b7a071d62bb14..cf5d72ea5bf3a8988611461c5b80fcc6b815daf9 100644 (file)
--- a/COPYING
+++ b/COPYING
@@ -600,3 +600,7 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice
    limitations under the License.
 
 --- end of Apache 2.0 license -----------------------------------------------
+
+The ext/coroutine code is externally maintained and distributed under
+the Boost Software License, Version 1.0.  (See its accompanying file
+LICENSE_1_0.txt.)
index 21f19fc4870fe6c4f19ff0949101a116898ceabd..8e25a6bcf2f0c294de94d63ecf0cb1e3f4c9aa7a 100644 (file)
@@ -102,6 +102,7 @@ endif
 if HAVE_BOTAN
                        botan/\* \
 endif
+                       ext/coroutine/\* \
                        gtest/\* \
                        include/\* \
                        lib/\eval/\* \
@@ -149,6 +150,9 @@ install-exec-hook:
 EXTRA_DIST  = tools/path_replacer.sh
 EXTRA_DIST += tools/mk_cfgrpt.sh
 
+#### include external sources in the distributed tarball:
+EXTRA_DIST += ext/coroutine/coroutine.hpp
+
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = dns++.pc
 
index 7b41e709093a26792e14c74fa2d9d6fa7add889b..824af9d2621a580cc4369d5a7cfe55af352ddede 100644 (file)
@@ -1290,6 +1290,10 @@ fi
 
 #
 # ASIO: we extensively use it as the C++ event management module.
+#
+# Use our 'coroutine' header from ext
+# CPPFLAGS="$CPPFLAGS -I\$(top_srcdir)/ext/coroutine"
+
 #
 # Doesn't seem to be required?
 CPPFLAGS="$CPPFLAGS -DBOOST_ASIO_HEADER_ONLY"
index b2685166c6b0c21d8600992da7109eb2c4bcfdd6..6cfbdee32f7d5667525e4c0f7a60f88a0319ff22 100644 (file)
@@ -1,4 +1,4 @@
-SUBDIRS = .
+SUBDIRS = coroutine
 
 if HAVE_GTEST_SOURCE
 SUBDIRS += gtest
diff --git a/ext/coroutine/LICENSE_1_0.txt b/ext/coroutine/LICENSE_1_0.txt
new file mode 100644 (file)
index 0000000..36b7cd9
--- /dev/null
@@ -0,0 +1,23 @@
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/ext/coroutine/Makefile.am b/ext/coroutine/Makefile.am
new file mode 100644 (file)
index 0000000..ba7b542
--- /dev/null
@@ -0,0 +1 @@
+EXTRA_DIST = LICENSE_1_0.txt
diff --git a/ext/coroutine/coroutine.hpp b/ext/coroutine/coroutine.hpp
new file mode 100644 (file)
index 0000000..b195b12
--- /dev/null
@@ -0,0 +1,330 @@
+//
+// coroutine.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_COROUTINE_HPP
+#define BOOST_ASIO_COROUTINE_HPP
+
+namespace boost {
+namespace asio {
+namespace detail {
+
+class coroutine_ref;
+
+} // namespace detail
+
+/// Provides support for implementing stackless coroutines.
+/**
+ * The @c coroutine class may be used to implement stackless coroutines. The
+ * class itself is used to store the current state of the coroutine.
+ *
+ * Coroutines are copy-constructible and assignable, and the space overhead is
+ * a single int. They can be used as a base class:
+ *
+ * @code class session : coroutine
+ * {
+ *   ...
+ * }; @endcode
+ *
+ * or as a data member:
+ *
+ * @code class session
+ * {
+ *   ...
+ *   coroutine coro_;
+ * }; @endcode
+ *
+ * or even bound in as a function argument using lambdas or @c bind(). The
+ * important thing is that as the application maintains a copy of the object
+ * for as long as the coroutine must be kept alive.
+ *
+ * @par Pseudo-keywords
+ *
+ * A coroutine is used in conjunction with certain "pseudo-keywords", which
+ * are implemented as macros. These macros are defined by a header file:
+ *
+ * @code #include <boost/asio/yield.hpp>@endcode
+ *
+ * and may conversely be undefined as follows:
+ *
+ * @code #include <boost/asio/unyield.hpp>@endcode
+ *
+ * <b>reenter</b>
+ *
+ * The @c reenter macro is used to define the body of a coroutine. It takes a
+ * single argument: a pointer or reference to a coroutine object. For example,
+ * if the base class is a coroutine object you may write:
+ *
+ * @code reenter (this)
+ * {
+ *   ... coroutine body ...
+ * } @endcode
+ *
+ * and if a data member or other variable you can write:
+ *
+ * @code reenter (coro_)
+ * {
+ *   ... coroutine body ...
+ * } @endcode
+ *
+ * When @c reenter is executed at runtime, control jumps to the location of the
+ * last @c yield or @c fork.
+ *
+ * The coroutine body may also be a single statement, such as:
+ *
+ * @code reenter (this) for (;;)
+ * {
+ *   ...
+ * } @endcode
+ *
+ * @b Limitation: The @c reenter macro is implemented using a switch. This
+ * means that you must take care when using local variables within the
+ * coroutine body. The local variable is not allowed in a position where
+ * reentering the coroutine could bypass the variable definition.
+ *
+ * <b>yield <em>statement</em></b>
+ *
+ * This form of the @c yield keyword is often used with asynchronous operations:
+ *
+ * @code yield socket_->async_read_some(buffer(*buffer_), *this); @endcode
+ *
+ * This divides into four logical steps:
+ *
+ * @li @c yield saves the current state of the coroutine.
+ * @li The statement initiates the asynchronous operation.
+ * @li The resume point is defined immediately following the statement.
+ * @li Control is transferred to the end of the coroutine body.
+ *
+ * When the asynchronous operation completes, the function object is invoked
+ * and @c reenter causes control to transfer to the resume point. It is
+ * important to remember to carry the coroutine state forward with the
+ * asynchronous operation. In the above snippet, the current class is a
+ * function object object with a coroutine object as base class or data member.
+ *
+ * The statement may also be a compound statement, and this permits us to
+ * define local variables with limited scope:
+ *
+ * @code yield
+ * {
+ *   mutable_buffers_1 b = buffer(*buffer_);
+ *   socket_->async_read_some(b, *this);
+ * } @endcode
+ *
+ * <b>yield return <em>expression</em> ;</b>
+ *
+ * This form of @c yield is often used in generators or coroutine-based parsers.
+ * For example, the function object:
+ *
+ * @code struct interleave : coroutine
+ * {
+ *   istream& is1;
+ *   istream& is2;
+ *   char operator()(char c)
+ *   {
+ *     reenter (this) for (;;)
+ *     {
+ *       yield return is1.get();
+ *       yield return is2.get();
+ *     }
+ *   }
+ * }; @endcode
+ *
+ * defines a trivial coroutine that interleaves the characters from two input
+ * streams.
+ *
+ * This type of @c yield divides into three logical steps:
+ *
+ * @li @c yield saves the current state of the coroutine.
+ * @li The resume point is defined immediately following the semicolon.
+ * @li The value of the expression is returned from the function.
+ *
+ * <b>yield ;</b>
+ *
+ * This form of @c yield is equivalent to the following steps:
+ *
+ * @li @c yield saves the current state of the coroutine.
+ * @li The resume point is defined immediately following the semicolon.
+ * @li Control is transferred to the end of the coroutine body.
+ *
+ * This form might be applied when coroutines are used for cooperative
+ * threading and scheduling is explicitly managed. For example:
+ *
+ * @code struct task : coroutine
+ * {
+ *   ...
+ *   void operator()()
+ *   {
+ *     reenter (this)
+ *     {
+ *       while (... not finished ...)
+ *       {
+ *         ... do something ...
+ *         yield;
+ *         ... do some more ...
+ *         yield;
+ *       }
+ *     }
+ *   }
+ *   ...
+ * };
+ * ...
+ * task t1, t2;
+ * for (;;)
+ * {
+ *   t1();
+ *   t2();
+ * } @endcode
+ *
+ * <b>yield break ;</b>
+ *
+ * The final form of @c yield is used to explicitly terminate the coroutine.
+ * This form is comprised of two steps:
+ *
+ * @li @c yield sets the coroutine state to indicate termination.
+ * @li Control is transferred to the end of the coroutine body.
+ *
+ * Once terminated, calls to is_complete() return true and the coroutine cannot
+ * be reentered.
+ *
+ * Note that a coroutine may also be implicitly terminated if the coroutine
+ * body is exited without a yield, e.g. by return, throw or by running to the
+ * end of the body.
+ *
+ * <b>fork <em>statement</em></b>
+ *
+ * The @c fork pseudo-keyword is used when "forking" a coroutine, i.e. splitting
+ * it into two (or more) copies. One use of @c fork is in a server, where a new
+ * coroutine is created to handle each client connection:
+ * 
+ * @code reenter (this)
+ * {
+ *   do
+ *   {
+ *     socket_.reset(new tcp::socket(io_context_));
+ *     yield acceptor->async_accept(*socket_, *this);
+ *     fork server(*this)();
+ *   } while (is_parent());
+ *   ... client-specific handling follows ...
+ * } @endcode
+ * 
+ * The logical steps involved in a @c fork are:
+ * 
+ * @li @c fork saves the current state of the coroutine.
+ * @li The statement creates a copy of the coroutine and either executes it
+ *     immediately or schedules it for later execution.
+ * @li The resume point is defined immediately following the semicolon.
+ * @li For the "parent", control immediately continues from the next line.
+ *
+ * The functions is_parent() and is_child() can be used to differentiate
+ * between parent and child. You would use these functions to alter subsequent
+ * control flow.
+ *
+ * Note that @c fork doesn't do the actual forking by itself. It is the
+ * application's responsibility to create a clone of the coroutine and call it.
+ * The clone can be called immediately, as above, or scheduled for delayed
+ * execution using something like io_context::post().
+ *
+ * @par Alternate macro names
+ *
+ * If preferred, an application can use macro names that follow a more typical
+ * naming convention, rather than the pseudo-keywords. These are:
+ *
+ * @li @c BOOST_ASIO_CORO_REENTER instead of @c reenter
+ * @li @c BOOST_ASIO_CORO_YIELD instead of @c yield
+ * @li @c BOOST_ASIO_CORO_FORK instead of @c fork
+ */
+class coroutine
+{
+public:
+  /// Constructs a coroutine in its initial state.
+  coroutine() : value_(0) {}
+
+  /// Returns true if the coroutine is the child of a fork.
+  bool is_child() const { return value_ < 0; }
+
+  /// Returns true if the coroutine is the parent of a fork.
+  bool is_parent() const { return !is_child(); }
+
+  /// Returns true if the coroutine has reached its terminal state.
+  bool is_complete() const { return value_ == -1; }
+
+private:
+  friend class detail::coroutine_ref;
+  int value_;
+};
+
+
+namespace detail {
+
+class coroutine_ref
+{
+public:
+  coroutine_ref(coroutine& c) : value_(c.value_), modified_(false) {}
+  coroutine_ref(coroutine* c) : value_(c->value_), modified_(false) {}
+  ~coroutine_ref() { if (!modified_) value_ = -1; }
+  operator int() const { return value_; }
+  int& operator=(int v) { modified_ = true; return value_ = v; }
+private:
+  void operator=(const coroutine_ref&);
+  int& value_;
+  bool modified_;
+};
+
+} // namespace detail
+} // namespace asio
+} // namespace boost
+
+#define BOOST_ASIO_CORO_REENTER(c) \
+  switch (::boost::asio::detail::coroutine_ref _coro_value = c) \
+    case -1: if (_coro_value) \
+    { \
+      goto terminate_coroutine; \
+      terminate_coroutine: \
+      _coro_value = -1; \
+      goto bail_out_of_coroutine; \
+      bail_out_of_coroutine: \
+      break; \
+    } \
+    else /* fall-through */ case 0:
+
+#define BOOST_ASIO_CORO_YIELD_IMPL(n) \
+  for (_coro_value = (n);;) \
+    if (_coro_value == 0) \
+    { \
+      case (n): ; \
+      break; \
+    } \
+    else \
+      switch (_coro_value ? 0 : 1) \
+        for (;;) \
+          /* fall-through */ case -1: if (_coro_value) \
+            goto terminate_coroutine; \
+          else for (;;) \
+            /* fall-through */ case 1: if (_coro_value) \
+              goto bail_out_of_coroutine; \
+            else /* fall-through */ case 0:
+
+#define BOOST_ASIO_CORO_FORK_IMPL(n) \
+  for (_coro_value = -(n);; _coro_value = (n)) \
+    if (_coro_value == (n)) \
+    { \
+      case -(n): ; \
+      break; \
+    } \
+    else
+
+#if defined(_MSC_VER)
+# define BOOST_ASIO_CORO_YIELD BOOST_ASIO_CORO_YIELD_IMPL(__COUNTER__ + 1)
+# define BOOST_ASIO_CORO_FORK BOOST_ASIO_CORO_FORK_IMPL(__COUNTER__ + 1)
+#else // defined(_MSC_VER)
+# define BOOST_ASIO_CORO_YIELD BOOST_ASIO_CORO_YIELD_IMPL(__LINE__)
+# define BOOST_ASIO_CORO_FORK BOOST_ASIO_CORO_FORK_IMPL(__LINE__)
+#endif // defined(_MSC_VER)
+
+#endif // BOOST_ASIO_COROUTINE_HPP
index 9109c8c00844658b52e90753ac03a17dc1181b0f..8296e760b0aa821dd8669e80d0d491a086a71a46 100644 (file)
@@ -74,9 +74,11 @@ if test "${boost_include_path}" ; then
        BOOST_INCLUDES="-I${boost_include_path}"
        CPPFLAGS="$CPPFLAGS $BOOST_INCLUDES"
 fi
-AC_CHECK_HEADERS([boost/shared_ptr.hpp boost/foreach.hpp boost/interprocess/sync/interprocess_upgradable_mutex.hpp boost/date_time/posix_time/posix_time_types.hpp boost/bind.hpp boost/function.hpp boost/asio/coroutine.hpp boost/asio.hpp boost/asio/ip/address.hpp boost/system/error_code.hpp],,
+AC_CHECK_HEADERS([boost/shared_ptr.hpp boost/foreach.hpp boost/interprocess/sync/interprocess_upgradable_mutex.hpp boost/date_time/posix_time/posix_time_types.hpp boost/bind.hpp boost/function.hpp boost/asio.hpp boost/asio/ip/address.hpp boost/system/error_code.hpp],,
   AC_MSG_ERROR([Missing required header files.]))
 
+AC_CHECK_HEADER(boost/asio/coroutine.hpp,,AC_MSG_RESULT(not found, using built-in header.))
+
 # clang can cause false positives with -Werror without -Qunused-arguments.
 # it can be triggered if used with ccache.
 AC_CHECK_DECL([__clang__], [CLANG_CXXFLAGS="-Qunused-arguments"], [])
index 16c5f1a13b0053f1b2e69f0f367bb4bc70b48837..906f63483288bfa6a1fdb5c89fba437b7c08cacf 100644 (file)
@@ -7,11 +7,17 @@
 #ifndef IO_FETCH_H
 #define IO_FETCH_H 1
 
+#include <config.h>
+
 #include <boost/shared_array.hpp>
 #include <boost/shared_ptr.hpp>
 #include <boost/date_time/posix_time/posix_time_types.hpp>
 
+#ifdef HAVE_BOOST_ASIO_COROUTINE_HPP
 #include <boost/asio/coroutine.hpp>
+#else
+#include <ext/coroutine/coroutine.hpp>
+#endif
 
 #include <boost/system/error_code.hpp>
 #include <asiolink/io_address.h>
index 8d7939e53c6492b8566d6abd1397c669a536e1c7..e583566e9898e8ceaaa2267e7a8c8813b1b15be8 100644 (file)
@@ -10,6 +10,8 @@
 // IMPORTANT NOTE: only very few ASIO headers files can be included in
 // this file.  In particular, asio.hpp should never be included here.
 // See the description of the namespace below.
+#include <config.h>
+
 #include <unistd.h>             // for some network system calls
 
 #include <functional>
 #include <asiolink/io_error.h>
 #include <asiolink/io_socket.h>
 
+#ifndef HAVE_BOOST_ASIO_COROUTINE_HPP
+#include <ext/coroutine/coroutine.hpp>
+#else
 #include <boost/asio/coroutine.hpp>
+#endif
 
 namespace isc {
 namespace asiolink {