Skip to content

Commit

Permalink
Change spawn() to be a completion token-based async operation.
Browse files Browse the repository at this point in the history
Added new spawn() overloads that conform to the requirements for
asynchronous operations. These overloads also support cancellation. When
targeting C++11 and later these functions are implemented in terms of
Boost.Context directly.

The existing overloads have been retained but are deprecated.
  • Loading branch information
chriskohlhoff committed Jun 29, 2022
1 parent 74a94fe commit 5bbdc9b
Show file tree
Hide file tree
Showing 10 changed files with 1,869 additions and 379 deletions.
13 changes: 5 additions & 8 deletions doc/overview/spawn.qbk
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ running stackful coroutines. It is based on the Boost.Coroutine library. The
`spawn()` function enables programs to implement asynchronous logic in a
synchronous manner, as shown in the following example:

boost::asio::spawn(my_strand, do_echo);
boost::asio::spawn(my_strand, do_echo, boost::asio::detached);

// ...

Expand All @@ -37,10 +37,7 @@ synchronous manner, as shown in the following example:
}
}

The first argument to `spawn()` may be a
[link boost_asio.reference.io_context__strand `strand`],
[link boost_asio.reference.io_context `io_context`], or a
[link boost_asio.reference.Handler completion handler].
The first argument to `spawn()` may be an executor or execution context.
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
Expand Down Expand Up @@ -85,10 +82,10 @@ exception, associate the output variable with the `yield_context` as follows:
my_socket.async_read_some(
boost::asio::buffer(data), yield[ec]);

[*Note:] if `spawn()` is used with a custom completion handler of type
`Handler`, the function object signature is actually:
[*Note:] if `spawn()` is used with a specified executor of type `Executor`, the
function object signature is actually:

void coroutine(boost::asio::basic_yield_context<Handler> yield);
void coroutine(boost::asio::basic_yield_context<Executor> yield);

[heading See Also]

Expand Down
9 changes: 9 additions & 0 deletions doc/reference.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -1610,6 +1610,9 @@
<xsl:text>
</xsl:text><xsl:value-of select="type"/><xsl:text> </xsl:text>
<xsl:choose>
<xsl:when test="declname = 'A'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'Allocator'">
<xsl:value-of select="declname"/>
</xsl:when>
Expand Down Expand Up @@ -1712,6 +1715,9 @@
<xsl:when test="declname = 'Head'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'I'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'IoObjectService1'">
<xsl:value-of select="concat('``[link boost_asio.reference.IoObjectService ', declname, ']``')"/>
</xsl:when>
Expand Down Expand Up @@ -1853,6 +1859,9 @@
<xsl:when test="declname = 'SocketService1' or declname = 'SocketService2'">
<xsl:value-of select="concat('``[link boost_asio.reference.SocketService ', declname, ']``')"/>
</xsl:when>
<xsl:when test="declname = 'StackAllocator'">
<xsl:value-of select="declname"/>
</xsl:when>
<xsl:when test="declname = 'Stream'">
<xsl:value-of select="declname"/>
</xsl:when>
Expand Down
12 changes: 8 additions & 4 deletions example/cpp03/spawn/echo_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#include <boost/asio/detached.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/spawn.hpp>
Expand Down Expand Up @@ -39,10 +40,12 @@ class session : public boost::enable_shared_from_this<session>
{
boost::asio::spawn(strand_,
boost::bind(&session::echo,
shared_from_this(), boost::placeholders::_1));
shared_from_this(), boost::placeholders::_1),
boost::asio::detached_t());
boost::asio::spawn(strand_,
boost::bind(&session::timeout,
shared_from_this(), boost::placeholders::_1));
shared_from_this(), boost::placeholders::_1),
boost::asio::detached_t());
}

private:
Expand Down Expand Up @@ -108,8 +111,9 @@ int main(int argc, char* argv[])
boost::asio::io_context io_context;

boost::asio::spawn(io_context,
boost::bind(do_accept,
boost::ref(io_context), atoi(argv[1]), boost::placeholders::_1));
boost::bind(do_accept, boost::ref(io_context),
atoi(argv[1]), boost::placeholders::_1),
boost::asio::detached_t());

io_context.run();
}
Expand Down
8 changes: 6 additions & 2 deletions example/cpp03/spawn/parallel_grep.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#include <boost/asio/detached.hpp>
#include <boost/asio/dispatch.hpp>
#include <boost/asio/post.hpp>
#include <boost/asio/spawn.hpp>
Expand All @@ -19,6 +20,7 @@
#include <iostream>
#include <string>

using boost::asio::detached_t;
using boost::asio::dispatch;
using boost::asio::spawn;
using boost::asio::strand;
Expand Down Expand Up @@ -74,8 +76,10 @@ int main(int argc, char* argv[])
for (int argn = 2; argn < argc; ++argn)
{
std::string input_file = argv[argn];
spawn(pool, boost::bind(&search_file, search_string,
input_file, output_strand, boost::placeholders::_1));
spawn(pool,
boost::bind(&search_file, search_string,
input_file, output_strand, boost::placeholders::_1),
detached_t());
}

// Join the thread pool to wait for all the spawned tasks to complete.
Expand Down
7 changes: 4 additions & 3 deletions example/cpp11/spawn/echo_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#include <boost/asio/detached.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/spawn.hpp>
Expand Down Expand Up @@ -49,7 +50,7 @@ class session : public std::enable_shared_from_this<session>
socket_.close();
timer_.cancel();
}
});
}, boost::asio::detached);

boost::asio::spawn(strand_,
[this, self](boost::asio::yield_context yield)
Expand All @@ -61,7 +62,7 @@ class session : public std::enable_shared_from_this<session>
if (timer_.expires_from_now() <= std::chrono::seconds(0))
socket_.close();
}
});
}, boost::asio::detached);
}

private:
Expand Down Expand Up @@ -98,7 +99,7 @@ int main(int argc, char* argv[])
std::make_shared<session>(io_context, std::move(socket))->go();
}
}
});
}, boost::asio::detached);

io_context.run();
}
Expand Down
4 changes: 3 additions & 1 deletion example/cpp11/spawn/parallel_grep.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#include <boost/asio/detached.hpp>
#include <boost/asio/dispatch.hpp>
#include <boost/asio/post.hpp>
#include <boost/asio/spawn.hpp>
Expand All @@ -17,6 +18,7 @@
#include <iostream>
#include <string>

using boost::asio::detached;
using boost::asio::dispatch;
using boost::asio::spawn;
using boost::asio::strand;
Expand Down Expand Up @@ -69,7 +71,7 @@ int main(int argc, char* argv[])
if (++line_num % 10 == 0)
post(yield);
}
});
}, detached);
}

// Join the thread pool to wait for all the spawned tasks to complete.
Expand Down
29 changes: 29 additions & 0 deletions include/boost/asio/detail/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -844,6 +844,35 @@
# endif // !defined(BOOST_ASIO_DISABLE_BOOST_DATE_TIME)
#endif // !defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)

// Boost support for the Coroutine library.
#if !defined(BOOST_ASIO_HAS_BOOST_COROUTINE)
# if !defined(BOOST_ASIO_DISABLE_BOOST_COROUTINE)
# define BOOST_ASIO_HAS_BOOST_COROUTINE 1
# endif // !defined(BOOST_ASIO_DISABLE_BOOST_COROUTINE)
#endif // !defined(BOOST_ASIO_HAS_BOOST_COROUTINE)

// Boost support for the Context library's fibers.
#if !defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
# if !defined(BOOST_ASIO_DISABLE_BOOST_CONTEXT_FIBER)
# if defined(__clang__)
# if (__cplusplus >= 201103)
# define BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER 1
# endif // (__cplusplus >= 201103)
# elif defined(__GNUC__)
# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8)) || (__GNUC__ > 4)
# if (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__)
# define BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER 1
# endif // (__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__)
# endif // ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8)) || (__GNUC__ > 4)
# endif // defined(__GNUC__)
# if defined(BOOST_ASIO_MSVC)
# if (_MSVC_LANG >= 201103)
# define BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER 1
# endif // (_MSC_LANG >= 201103)
# endif // defined(BOOST_ASIO_MSVC)
# endif // !defined(BOOST_ASIO_DISABLE_BOOST_CONTEXT_FIBER)
#endif // !defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)

// Standard library support for addressof.
#if !defined(BOOST_ASIO_HAS_STD_ADDRESSOF)
# if !defined(BOOST_ASIO_DISABLE_STD_ADDRESSOF)
Expand Down
42 changes: 42 additions & 0 deletions include/boost/asio/detail/exception.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//
// detail/exception.hpp
// ~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 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_DETAIL_EXCEPTION_HPP
#define BOOST_ASIO_DETAIL_EXCEPTION_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include <boost/asio/detail/config.hpp>

#if defined(BOOST_ASIO_HAS_STD_EXCEPTION)
# include <exception>
#else // defined(BOOST_ASIO_HAS_STD_EXCEPTION)
# include <boost/exception_ptr.hpp>
#endif // defined(BOOST_ASIO_HAS_STD_EXCEPTION)

namespace boost {
namespace asio {

#if defined(BOOST_ASIO_HAS_STD_EXCEPTION)
using std::exception_ptr;
using std::current_exception;
using std::rethrow_exception;
#else // defined(BOOST_ASIO_HAS_STD_EXCEPTION)
using boost::exception_ptr;
using boost::current_exception;
using boost::rethrow_exception;
#endif // defined(BOOST_ASIO_HAS_STD_EXCEPTION)

} // namespace asio
} // namespace boost

#endif // BOOST_ASIO_DETAIL_EXCEPTION_HPP
Loading

0 comments on commit 5bbdc9b

Please sign in to comment.