$include_dir="/home/hyper-archives/boost-commit/include"; include("$include_dir/msg-header.inc") ?>
Subject: [Boost-commit] svn:boost r84311 - in trunk: boost/asio boost/asio/impl libs/asio/example/spawn
From: chris_at_[hidden]
Date: 2013-05-16 21:38:48
Author: chris_kohlhoff
Date: 2013-05-16 21:38:47 EDT (Thu, 16 May 2013)
New Revision: 84311
URL: http://svn.boost.org/trac/boost/changeset/84311
Log:
Add the asio::spawn() function, a high-level wrapper for running
stackful coroutines. It is based on the Boost.Coroutine library.
Here is an example of its use:
  asio::spawn(my_strand, do_echo);
  // ...
  void do_echo(asio::yield_context yield)
  {
    try
    {
      char data[128];
      for (;;)
      {
        std::size_t length =
          my_socket.async_read_some(
            asio::buffer(data), yield);
        asio::async_write(my_socket,
            asio::buffer(data, length), yield);
      }
    }
    catch (std::exception& e)
    {
      // ...
    }
  }
The first argument to asio::spawn() may be a strand, io_service or
completion handler. This argument determines the context in which the
coroutine is permitted to execute. For example, a server's per-client
object may consist of multiple coroutines; they should all run on the
same strand so that no explicit synchronisation is required.
The second argument is a function object with signature (**):
  void coroutine(asio::yield_context yield);
that specifies the code to be run as part of the coroutine. The
parameter yield may be passed to an asynchronous operation in place of
the completion handler, as in:
  std::size_t length =
    my_socket.async_read_some(
      asio::buffer(data), yield);
This starts the asynchronous operation and suspends the coroutine. The
coroutine will be resumed automatically when the asynchronous operation
completes.
Where a completion handler signature has the form:
  void handler(error_code ec, result_type result);
the initiating function returns the result_type. In the async_read_some
example above, this is std::size_t. If the asynchronous operation fails,
the error_code is converted into a system_error exception and thrown.
Where a completion handler signature has the form:
  void handler(error_code ec);
the initiating function returns void. As above, an error is passed back
to the coroutine as a system_error exception.
To collect the error_code from an operation, rather than have it throw
an exception, associate the output variable with the yield_context as
follows:
  error_code ec;
  std::size_t length =
    my_socket.async_read_some(
      asio::buffer(data), yield[ec]);
**Note: if asio::spawn() is used with a custom completion handler of
type Handler, the function object signature is actually:
  
  void coroutine(asio::basic_yield_context<Handler> yield);
Added:
   trunk/boost/asio/impl/spawn.hpp   (contents, props changed)
   trunk/boost/asio/spawn.hpp   (contents, props changed)
   trunk/libs/asio/example/spawn/
   trunk/libs/asio/example/spawn/Jamfile.v2   (contents, props changed)
   trunk/libs/asio/example/spawn/echo_server.cpp   (contents, props changed)
Added: trunk/boost/asio/impl/spawn.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/asio/impl/spawn.hpp	2013-05-16 21:38:47 EDT (Thu, 16 May 2013)
@@ -0,0 +1,323 @@
+//
+// impl/spawn.hpp
+// ~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2012 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_IMPL_SPAWN_HPP
+#define BOOST_ASIO_IMPL_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 <boost/asio/async_result.hpp>
+#include <boost/asio/detail/handler_alloc_helpers.hpp>
+#include <boost/asio/detail/handler_invoke_helpers.hpp>
+#include <boost/asio/detail/noncopyable.hpp>
+#include <boost/asio/detail/shared_ptr.hpp>
+#include <boost/asio/handler_type.hpp>
+
+#include <boost/asio/detail/push_options.hpp>
+
+namespace boost {
+namespace asio {
+namespace detail {
+
+  template <typename Handler, typename T>
+  class coro_handler
+  {
+  public:
+    coro_handler(basic_yield_context<Handler> ctx)
+      : coro_(ctx.coro_.lock()),
+        ca_(ctx.ca_),
+        handler_(ctx.handler_),
+        ec_(ctx.ec_),
+        value_(0)
+    {
+    }
+
+    void operator()(T value)
+    {
+      *ec_ = boost::system::error_code();
+      *value_ = value;
+      (*coro_)();
+    }
+
+    void operator()(boost::system::error_code ec, T value)
+    {
+      *ec_ = ec;
+      *value_ = value;
+      (*coro_)();
+    }
+
+  //private:
+    detail::shared_ptr<boost::coroutines::coroutine<void()> > coro_;
+    boost::coroutines::coroutine<void()>::caller_type& ca_;
+    Handler& handler_;
+    boost::system::error_code* ec_;
+    T* value_;
+  };
+
+  template <typename Handler>
+  class coro_handler<Handler, void>
+  {
+  public:
+    coro_handler(basic_yield_context<Handler> ctx)
+      : coro_(ctx.coro_.lock()),
+        ca_(ctx.ca_),
+        handler_(ctx.handler_),
+        ec_(ctx.ec_)
+    {
+    }
+
+    void operator()()
+    {
+      *ec_ = boost::system::error_code();
+      (*coro_)();
+    }
+
+    void operator()(boost::system::error_code ec)
+    {
+      *ec_ = ec;
+      (*coro_)();
+    }
+
+  //private:
+    detail::shared_ptr<boost::coroutines::coroutine<void()> > coro_;
+    boost::coroutines::coroutine<void()>::caller_type& ca_;
+    Handler& handler_;
+    boost::system::error_code* ec_;
+  };
+
+  template <typename Handler, typename T>
+  inline void* asio_handler_allocate(std::size_t size,
+      coro_handler<Handler, T>* this_handler)
+  {
+    return boost_asio_handler_alloc_helpers::allocate(
+        size, this_handler->handler_);
+  }
+
+  template <typename Handler, typename T>
+  inline void asio_handler_deallocate(void* pointer, std::size_t size,
+      coro_handler<Handler, T>* this_handler)
+  {
+    boost_asio_handler_alloc_helpers::deallocate(
+        pointer, size, this_handler->handler_);
+  }
+
+  template <typename Function, typename Handler, typename T>
+  inline void asio_handler_invoke(Function& function,
+      coro_handler<Handler, T>* this_handler)
+  {
+    boost_asio_handler_invoke_helpers::invoke(
+        function, this_handler->handler_);
+  }
+
+  template <typename Function, typename Handler, typename T>
+  inline void asio_handler_invoke(const Function& function,
+      coro_handler<Handler, T>* this_handler)
+  {
+    boost_asio_handler_invoke_helpers::invoke(
+        function, this_handler->handler_);
+  }
+
+} // namespace detail
+
+#if !defined(GENERATING_DOCUMENTATION)
+
+template <typename Handler, typename ReturnType>
+struct handler_type<basic_yield_context<Handler>, ReturnType()>
+{
+  typedef detail::coro_handler<Handler, void> type;
+};
+
+template <typename Handler, typename ReturnType, typename Arg1>
+struct handler_type<basic_yield_context<Handler>, ReturnType(Arg1)>
+{
+  typedef detail::coro_handler<Handler, Arg1> type;
+};
+
+template <typename Handler, typename ReturnType>
+struct handler_type<basic_yield_context<Handler>,
+    ReturnType(boost::system::error_code)>
+{
+  typedef detail::coro_handler<Handler, void> type;
+};
+
+template <typename Handler, typename ReturnType, typename Arg2>
+struct handler_type<basic_yield_context<Handler>,
+    ReturnType(boost::system::error_code, Arg2)>
+{
+  typedef detail::coro_handler<Handler, Arg2> type;
+};
+
+template <typename Handler, typename T>
+class async_result<detail::coro_handler<Handler, T> >
+{
+public:
+  typedef T type;
+
+  explicit async_result(detail::coro_handler<Handler, T>& h)
+    : ca_(h.ca_)
+  {
+    out_ec_ = h.ec_;
+    if (!out_ec_) h.ec_ = &ec_;
+    h.value_ = &value_;
+  }
+
+  type get()
+  {
+    ca_();
+    if (!out_ec_ && ec_) throw boost::system::system_error(ec_);
+    return value_;
+  }
+
+private:
+  boost::coroutines::coroutine<void()>::caller_type& ca_;
+  boost::system::error_code* out_ec_;
+  boost::system::error_code ec_;
+  type value_;
+};
+
+template <typename Handler>
+class async_result<detail::coro_handler<Handler, void> >
+{
+public:
+  typedef void type;
+
+  explicit async_result(detail::coro_handler<Handler, void>& h)
+    : ca_(h.ca_)
+  {
+    out_ec_ = h.ec_;
+    if (!out_ec_) h.ec_ = &ec_;
+  }
+
+  void get()
+  {
+    ca_();
+    if (!out_ec_ && ec_) throw boost::system::system_error(ec_);
+  }
+
+private:
+  boost::coroutines::coroutine<void()>::caller_type& ca_;
+  boost::system::error_code* out_ec_;
+  boost::system::error_code ec_;
+};
+
+namespace detail {
+
+  template <typename Handler, typename Function>
+  struct spawn_data : private noncopyable
+  {
+    spawn_data(BOOST_ASIO_MOVE_ARG(Handler) handler,
+        bool call_handler, BOOST_ASIO_MOVE_ARG(Function) function)
+      : handler_(BOOST_ASIO_MOVE_CAST(Handler)(handler)),
+        call_handler_(call_handler),
+        function_(BOOST_ASIO_MOVE_CAST(Function)(function))
+    {
+    }
+
+    weak_ptr<boost::coroutines::coroutine<void()> > coro_;
+    Handler handler_;
+    bool call_handler_;
+    Function function_;
+  };
+
+  template <typename Handler, typename Function>
+  struct coro_entry_point
+  {
+    void operator()(boost::coroutines::coroutine<void()>::caller_type& ca)
+    {
+      shared_ptr<spawn_data<Handler, Function> > data(data_);
+      ca(); // Yield until coroutine pointer has been initialised.
+      const basic_yield_context<Handler> yield(
+          data->coro_, ca, data->handler_);
+      (data->function_)(yield);
+      if (data->call_handler_)
+        (data->handler_)();
+    }
+
+    shared_ptr<spawn_data<Handler, Function> > data_;
+  };
+
+  template <typename Handler, typename Function>
+  struct spawn_helper
+  {
+    void operator()()
+    {
+      coro_entry_point<Handler, Function> entry_point = { data_ };
+      shared_ptr<boost::coroutines::coroutine<void()> > coro(
+          new boost::coroutines::coroutine<void()>(entry_point, attributes_));
+      data_->coro_ = coro;
+      (*coro)();
+    }
+
+    shared_ptr<spawn_data<Handler, Function> > data_;
+    boost::coroutines::attributes attributes_;
+  };
+
+  inline void default_spawn_handler() {}
+
+} // namespace detail
+
+template <typename Handler, typename Function>
+void spawn(BOOST_ASIO_MOVE_ARG(Handler) handler,
+    BOOST_ASIO_MOVE_ARG(Function) function,
+    const boost::coroutines::attributes& attributes)
+{
+  detail::spawn_helper<Handler, Function> helper;
+  helper.data_.reset(
+      new detail::spawn_data<Handler, Function>(
+        BOOST_ASIO_MOVE_CAST(Handler)(handler), true,
+        BOOST_ASIO_MOVE_CAST(Function)(function)));
+  helper.attributes_ = attributes;
+  boost_asio_handler_invoke_helpers::invoke(helper, helper.data_->handler_);
+}
+
+template <typename Handler, typename Function>
+void spawn(basic_yield_context<Handler> ctx,
+    BOOST_ASIO_MOVE_ARG(Function) function,
+    const boost::coroutines::attributes& attributes)
+{
+  Handler handler(ctx.handler_); // Explicit copy that might be moved from.
+  detail::spawn_helper<Handler, Function> helper;
+  helper.data_.reset(
+      new detail::spawn_data<Handler, Function>(
+        BOOST_ASIO_MOVE_CAST(Handler)(handler), false,
+        BOOST_ASIO_MOVE_CAST(Function)(function)));
+  helper.attributes_ = attributes;
+  boost_asio_handler_invoke_helpers::invoke(helper, helper.data_->handler_);
+}
+
+template <typename Function>
+void spawn(boost::asio::io_service::strand strand,
+    BOOST_ASIO_MOVE_ARG(Function) function,
+    const boost::coroutines::attributes& attributes)
+{
+  boost::asio::spawn(strand.wrap(&detail::default_spawn_handler),
+      BOOST_ASIO_MOVE_CAST(Function)(function), attributes);
+}
+
+template <typename Function>
+void spawn(boost::asio::io_service& io_service,
+    BOOST_ASIO_MOVE_ARG(Function) function,
+    const boost::coroutines::attributes& attributes)
+{
+  boost::asio::spawn(boost::asio::io_service::strand(io_service),
+      BOOST_ASIO_MOVE_CAST(Function)(function), attributes);
+}
+
+#endif // !defined(GENERATING_DOCUMENTATION)
+
+} // namespace asio
+} // namespace boost
+
+#include <boost/asio/detail/pop_options.hpp>
+
+#endif // BOOST_ASIO_IMPL_SPAWN_HPP
Added: trunk/boost/asio/spawn.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/asio/spawn.hpp	2013-05-16 21:38:47 EDT (Thu, 16 May 2013)
@@ -0,0 +1,99 @@
+//
+// spawn.hpp
+// ~~~~~~~~~
+//
+// Copyright (c) 2003-2012 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_SPAWN_HPP
+#define BOOST_ASIO_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 <boost/coroutine/coroutine.hpp>
+#include <boost/asio/detail/weak_ptr.hpp>
+#include <boost/asio/detail/wrapped_handler.hpp>
+#include <boost/asio/io_service.hpp>
+#include <boost/asio/strand.hpp>
+
+#include <boost/asio/detail/push_options.hpp>
+
+namespace boost {
+namespace asio {
+
+template <typename Handler>
+class basic_yield_context
+{
+public:
+  basic_yield_context(
+      const detail::weak_ptr<boost::coroutines::coroutine<void()> >& coro,
+      boost::coroutines::coroutine<void()>::caller_type& ca, Handler& handler)
+    : coro_(coro),
+      ca_(ca),
+      handler_(handler),
+      ec_(0)
+  {
+  }
+
+  basic_yield_context operator[](boost::system::error_code& ec)
+  {
+    basic_yield_context tmp(*this);
+    tmp.ec_ = &ec;
+    return tmp;
+  }
+
+#if defined(GENERATING_DOCUMENTATION)
+private:
+#endif // defined(GENERATING_DOCUMENTATION)
+  detail::weak_ptr<boost::coroutines::coroutine<void()> > coro_;
+  boost::coroutines::coroutine<void()>::caller_type& ca_;
+  Handler& handler_;
+  boost::system::error_code* ec_;
+};
+
+#if defined(GENERATING_DOCUMENTATION)
+typedef basic_yield_context<unspecified> yield_context;
+#else // defined(GENERATING_DOCUMENTATION)
+typedef basic_yield_context<
+  detail::wrapped_handler<
+    io_service::strand, void(*)()> > yield_context;
+#endif // defined(GENERATING_DOCUMENTATION)
+
+template <typename Handler, typename Function>
+void spawn(BOOST_ASIO_MOVE_ARG(Handler) handler,
+    BOOST_ASIO_MOVE_ARG(Function) function,
+    const boost::coroutines::attributes& attributes
+      = boost::coroutines::attributes());
+
+template <typename Handler, typename Function>
+void spawn(basic_yield_context<Handler> ctx,
+    BOOST_ASIO_MOVE_ARG(Function) function,
+    const boost::coroutines::attributes& attributes
+      = boost::coroutines::attributes());
+
+template <typename Function>
+void spawn(boost::asio::io_service::strand strand,
+    BOOST_ASIO_MOVE_ARG(Function) function,
+    const boost::coroutines::attributes& attributes
+      = boost::coroutines::attributes());
+
+template <typename Function>
+void spawn(boost::asio::io_service& io_service,
+    BOOST_ASIO_MOVE_ARG(Function) function,
+    const boost::coroutines::attributes& attributes
+      = boost::coroutines::attributes());
+
+} // namespace asio
+} // namespace boost
+
+#include <boost/asio/detail/pop_options.hpp>
+
+#include <boost/asio/impl/spawn.hpp>
+
+#endif // BOOST_ASIO_SPAWN_HPP
Added: trunk/libs/asio/example/spawn/Jamfile.v2
==============================================================================
--- (empty file)
+++ trunk/libs/asio/example/spawn/Jamfile.v2	2013-05-16 21:38:47 EDT (Thu, 16 May 2013)
@@ -0,0 +1,40 @@
+#
+# Copyright (c) 2003-2012 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)
+#
+
+import os ;
+
+if [ os.name ] = SOLARIS
+{
+  lib socket ;
+  lib nsl ;
+}
+else if [ os.name ] = NT
+{
+  lib ws2_32 ;
+  lib mswsock ;
+}
+else if [ os.name ] = HPUX
+{
+  lib ipv6 ;
+}
+
+exe server
+  : echo_server.cpp
+    /boost/context//boost_context
+    /boost/coroutine//boost_coroutine
+    /boost/system//boost_system
+  : <define>BOOST_ALL_NO_LIB=1
+    <threading>multi
+    <os>SOLARIS:<library>socket
+    <os>SOLARIS:<library>nsl
+    <os>NT:<define>_WIN32_WINNT=0x0501
+    <os>NT,<toolset>gcc:<library>ws2_32
+    <os>NT,<toolset>gcc:<library>mswsock
+    <os>NT,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS
+    <os>HPUX,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED
+    <os>HPUX:<library>ipv6
+  ;
Added: trunk/libs/asio/example/spawn/echo_server.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/asio/example/spawn/echo_server.cpp	2013-05-16 21:38:47 EDT (Thu, 16 May 2013)
@@ -0,0 +1,122 @@
+//
+// echo_server.cpp
+// ~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2012 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)
+//
+
+#include <boost/asio/deadline_timer.hpp>
+#include <boost/asio/io_service.hpp>
+#include <boost/asio/ip/tcp.hpp>
+#include <boost/asio/spawn.hpp>
+#include <boost/asio/write.hpp>
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include <iostream>
+
+using boost::asio::ip::tcp;
+
+class session : public boost::enable_shared_from_this<session>
+{
+public:
+  explicit session(boost::asio::io_service& io_service)
+    : strand_(io_service),
+      socket_(io_service),
+      timer_(io_service)
+  {
+  }
+
+  tcp::socket& socket()
+  {
+    return socket_;
+  }
+
+  void go()
+  {
+    boost::asio::spawn(strand_,
+        boost::bind(&session::echo,
+          shared_from_this(), _1));
+    boost::asio::spawn(strand_,
+        boost::bind(&session::timeout,
+          shared_from_this(), _1));
+  }
+
+private:
+  void echo(boost::asio::yield_context yield)
+  {
+    try
+    {
+      char data[128];
+      for (;;)
+      {
+        timer_.expires_from_now(boost::posix_time::seconds(10));
+        std::size_t n = socket_.async_read_some(boost::asio::buffer(data), yield);
+        boost::asio::async_write(socket_, boost::asio::buffer(data, n), yield);
+      }
+    }
+    catch (std::exception& e)
+    {
+      socket_.close();
+      timer_.cancel();
+    }
+  }
+
+  void timeout(boost::asio::yield_context yield)
+  {
+    while (socket_.is_open())
+    {
+      boost::system::error_code ignored_ec;
+      timer_.async_wait(yield[ignored_ec]);
+      if (timer_.expires_from_now() <= boost::posix_time::seconds(0))
+        socket_.close();
+    }
+  }
+
+  boost::asio::io_service::strand strand_;
+  tcp::socket socket_;
+  boost::asio::deadline_timer timer_;
+};
+
+void do_accept(boost::asio::io_service& io_service,
+    unsigned short port, boost::asio::yield_context yield)
+{
+  tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), port));
+
+  for (;;)
+  {
+    boost::system::error_code ec;
+    boost::shared_ptr<session> new_session(new session(io_service));
+    acceptor.async_accept(new_session->socket(), yield[ec]);
+    if (!ec) new_session->go();
+  }
+}
+
+int main(int argc, char* argv[])
+{
+  try
+  {
+    if (argc != 2)
+    {
+      std::cerr << "Usage: echo_server <port>\n";
+      return 1;
+    }
+
+    boost::asio::io_service io_service;
+
+    boost::asio::spawn(io_service,
+        boost::bind(do_accept,
+          boost::ref(io_service), atoi(argv[1]), _1));
+
+    io_service.run();
+  }
+  catch (std::exception& e)
+  {
+    std::cerr << "Exception: " << e.what() << "\n";
+  }
+
+  return 0;
+}