Initial implementation of move-enable accept/async_accept.

This commit adds support for the new form of accept and async_accept. In this
form, rather than taking a socket by reference, the newly accepted socket is
returned to the caller/callback as a movable socket object.
This commit is contained in:
Christopher Kohlhoff 2015-03-02 18:51:03 +11:00
parent 7eee8f1875
commit f475b21437
15 changed files with 579 additions and 13 deletions

View File

@ -1029,7 +1029,7 @@ public:
* @code
* asio::ip::tcp::acceptor acceptor(io_service);
* ...
* asio::ip::tcp::soocket socket(io_service);
* asio::ip::tcp::socket socket(io_service);
* asio::error_code ec;
* acceptor.accept(socket, ec);
* if (ec)
@ -1211,6 +1211,122 @@ public:
return this->get_service().async_accept(this->get_implementation(), peer,
&peer_endpoint, ASIO_MOVE_CAST(AcceptHandler)(handler));
}
#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
/// Accept a new connection.
/**
* This function is used to accept a new connection from a peer. The function
* call will block until a new connection has been accepted successfully or
* an error occurs.
*
* This overload requires that the Protocol template parameter satisfy the
* AcceptableProtocol type requirements.
*
* @returns A socket object representing the newly accepted connection.
*
* @throws asio::system_error Thrown on failure.
*
* @par Example
* @code
* asio::ip::tcp::acceptor acceptor(io_service);
* ...
* asio::ip::tcp::socket socket(acceptor.accept());
* @endcode
*/
typename Protocol::socket accept()
{
asio::error_code ec;
typename Protocol::socket peer(
this->get_service().accept(
this->get_implementation(), 0, 0, ec));
asio::detail::throw_error(ec, "accept");
return peer;
}
/// Accept a new connection.
/**
* This function is used to accept a new connection from a peer. The function
* call will block until a new connection has been accepted successfully or
* an error occurs.
*
* This overload requires that the Protocol template parameter satisfy the
* AcceptableProtocol type requirements.
*
* @param ec Set to indicate what error occurred, if any.
*
* @returns On success, a socket object representing the newly accepted
* connection. On error, a socket object where is_open() is false.
*
* @par Example
* @code
* asio::ip::tcp::acceptor acceptor(io_service);
* ...
* asio::ip::tcp::socket socket(acceptor.accept(ec));
* if (ec)
* {
* // An error occurred.
* }
* @endcode
*/
typename Protocol::socket accept(asio::error_code& ec)
{
return this->get_service().accept(this->get_implementation(), 0, 0, ec);
}
/// Start an asynchronous accept.
/**
* This function is used to asynchronously accept a new connection. The
* function call always returns immediately.
*
* This overload requires that the Protocol template parameter satisfy the
* AcceptableProtocol type requirements.
*
* @param handler The handler to be called when the accept operation
* completes. Copies will be made of the handler as required. The function
* signature of the handler must be:
* @code void handler(
* const asio::error_code& error, // Result of operation.
* typename Protocol::socket peer // On success, the newly accepted socket.
* ); @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* asio::io_service::post().
*
* @par Example
* @code
* void accept_handler(const asio::error_code& error,
* asio::ip::tcp::socket peer)
* {
* if (!error)
* {
* // Accept succeeded.
* }
* }
*
* ...
*
* asio::ip::tcp::acceptor acceptor(io_service);
* ...
* asio::ip::tcp::socket socket(io_service);
* acceptor.async_accept(socket, accept_handler);
* @endcode
*/
template <typename MoveAcceptHandler>
ASIO_INITFN_RESULT_TYPE(MoveAcceptHandler,
void (asio::error_code, typename Protocol::socket))
async_accept(ASIO_MOVE_ARG(MoveAcceptHandler) handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a MoveAcceptHandler.
ASIO_MOVE_ACCEPT_HANDLER_CHECK(MoveAcceptHandler,
handler, typename Protocol::socket) type_check;
return this->get_service().async_accept(this->get_implementation(),
static_cast<endpoint_type*>(0),
ASIO_MOVE_CAST(MoveAcceptHandler)(handler));
}
#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
};
} // namespace asio

View File

@ -220,6 +220,132 @@ inline binder2<Handler, Arg1, Arg2> bind_handler(
ASIO_MOVE_CAST(Handler)(handler), arg1, arg2);
}
#if defined(ASIO_HAS_MOVE)
template <typename Handler, typename Arg1>
class move_binder1
{
public:
move_binder1(int, ASIO_MOVE_ARG(Handler) handler,
ASIO_MOVE_ARG(Arg1) arg1)
: handler_(ASIO_MOVE_CAST(Handler)(handler)),
arg1_(ASIO_MOVE_CAST(Arg1)(arg1))
{
}
move_binder1(move_binder1&& other)
: handler_(ASIO_MOVE_CAST(Handler)(other.handler_)),
arg1_(ASIO_MOVE_CAST(Arg1)(other.arg1_))
{
}
void operator()()
{
handler_(ASIO_MOVE_CAST(Arg1)(arg1_));
}
//private:
Handler handler_;
Arg1 arg1_;
};
template <typename Handler, typename Arg1>
inline void* asio_handler_allocate(std::size_t size,
move_binder1<Handler, Arg1>* this_handler)
{
return asio_handler_alloc_helpers::allocate(
size, this_handler->handler_);
}
template <typename Handler, typename Arg1>
inline void asio_handler_deallocate(void* pointer, std::size_t size,
move_binder1<Handler, Arg1>* this_handler)
{
asio_handler_alloc_helpers::deallocate(
pointer, size, this_handler->handler_);
}
template <typename Handler, typename Arg1>
inline bool asio_handler_is_continuation(
move_binder1<Handler, Arg1>* this_handler)
{
return asio_handler_cont_helpers::is_continuation(
this_handler->handler_);
}
template <typename Function, typename Handler, typename Arg1>
inline void asio_handler_invoke(ASIO_MOVE_ARG(Function) function,
move_binder1<Handler, Arg1>* this_handler)
{
asio_handler_invoke_helpers::invoke(
ASIO_MOVE_CAST(Function)(function), this_handler->handler_);
}
template <typename Handler, typename Arg1, typename Arg2>
class move_binder2
{
public:
move_binder2(int, ASIO_MOVE_ARG(Handler) handler,
const Arg1& arg1, ASIO_MOVE_ARG(Arg2) arg2)
: handler_(ASIO_MOVE_CAST(Handler)(handler)),
arg1_(arg1),
arg2_(ASIO_MOVE_CAST(Arg2)(arg2))
{
}
move_binder2(move_binder2&& other)
: handler_(ASIO_MOVE_CAST(Handler)(other.handler_)),
arg1_(ASIO_MOVE_CAST(Arg1)(other.arg1_)),
arg2_(ASIO_MOVE_CAST(Arg2)(other.arg2_))
{
}
void operator()()
{
handler_(static_cast<const Arg1&>(arg1_),
ASIO_MOVE_CAST(Arg2)(arg2_));
}
//private:
Handler handler_;
Arg1 arg1_;
Arg2 arg2_;
};
template <typename Handler, typename Arg1, typename Arg2>
inline void* asio_handler_allocate(std::size_t size,
move_binder2<Handler, Arg1, Arg2>* this_handler)
{
return asio_handler_alloc_helpers::allocate(
size, this_handler->handler_);
}
template <typename Handler, typename Arg1, typename Arg2>
inline void asio_handler_deallocate(void* pointer, std::size_t size,
move_binder2<Handler, Arg1, Arg2>* this_handler)
{
asio_handler_alloc_helpers::deallocate(
pointer, size, this_handler->handler_);
}
template <typename Handler, typename Arg1, typename Arg2>
inline bool asio_handler_is_continuation(
move_binder2<Handler, Arg1, Arg2>* this_handler)
{
return asio_handler_cont_helpers::is_continuation(
this_handler->handler_);
}
template <typename Function, typename Handler, typename Arg1, typename Arg2>
inline void asio_handler_invoke(ASIO_MOVE_ARG(Function) function,
move_binder2<Handler, Arg1, Arg2>* this_handler)
{
asio_handler_invoke_helpers::invoke(
ASIO_MOVE_CAST(Function)(function), this_handler->handler_);
}
#endif // defined(ASIO_HAS_MOVE)
} // namespace detail
template <typename Handler, typename Arg1, typename Allocator>
@ -270,6 +396,59 @@ struct associated_executor<detail::binder2<Handler, Arg1, Arg2>, Executor>
}
};
#if defined(ASIO_HAS_MOVE)
template <typename Handler, typename Arg1, typename Allocator>
struct associated_allocator<detail::move_binder1<Handler, Arg1>, Allocator>
{
typedef typename associated_allocator<Handler, Allocator>::type type;
static type get(const detail::move_binder1<Handler, Arg1>& h,
const Allocator& a = Allocator()) ASIO_NOEXCEPT
{
return associated_allocator<Handler, Allocator>::get(h.handler_, a);
}
};
template <typename Handler, typename Arg1, typename Arg2, typename Allocator>
struct associated_allocator<
detail::move_binder2<Handler, Arg1, Arg2>, Allocator>
{
typedef typename associated_allocator<Handler, Allocator>::type type;
static type get(const detail::move_binder2<Handler, Arg1, Arg2>& h,
const Allocator& a = Allocator()) ASIO_NOEXCEPT
{
return associated_allocator<Handler, Allocator>::get(h.handler_, a);
}
};
template <typename Handler, typename Arg1, typename Executor>
struct associated_executor<detail::move_binder1<Handler, Arg1>, Executor>
{
typedef typename associated_executor<Handler, Executor>::type type;
static type get(const detail::move_binder1<Handler, Arg1>& h,
const Executor& ex = Executor()) ASIO_NOEXCEPT
{
return associated_executor<Handler, Executor>::get(h.handler_, ex);
}
};
template <typename Handler, typename Arg1, typename Arg2, typename Executor>
struct associated_executor<detail::move_binder2<Handler, Arg1, Arg2>, Executor>
{
typedef typename associated_executor<Handler, Executor>::type type;
static type get(const detail::move_binder2<Handler, Arg1, Arg2>& h,
const Executor& ex = Executor()) ASIO_NOEXCEPT
{
return associated_executor<Handler, Executor>::get(h.handler_, ex);
}
};
#endif // defined(ASIO_HAS_MOVE)
} // namespace asio
#include "asio/detail/pop_options.hpp"

View File

@ -100,6 +100,16 @@ auto two_arg_handler_test(Handler h, Arg1* a1, Arg2* a2)
template <typename Handler>
char (&two_arg_handler_test(Handler, ...))[2];
template <typename Handler, typename Arg1, typename Arg2>
auto two_arg_move_handler_test(Handler h, Arg1* a1, Arg2* a2)
-> decltype(
sizeof(Handler(ASIO_MOVE_CAST(Handler)(h))),
((h)(*a1, ASIO_MOVE_CAST(Arg2)(*a2))),
char(0));
template <typename Handler>
char (&two_arg_move_handler_test(Handler, ...))[2];
# define ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT(expr, msg) \
static_assert(expr, msg);
@ -228,6 +238,33 @@ struct handler_type_requirements
asio::detail::lvref<const asio::error_code>()), \
char(0))> ASIO_UNUSED_TYPEDEF
#define ASIO_MOVE_ACCEPT_HANDLER_CHECK( \
handler_type, handler, socket_type) \
\
typedef ASIO_HANDLER_TYPE(handler_type, \
void(asio::error_code, socket_type)) \
asio_true_handler_type; \
\
ASIO_HANDLER_TYPE_REQUIREMENTS_ASSERT( \
sizeof(asio::detail::two_arg_move_handler_test( \
asio::detail::rvref< \
asio_true_handler_type>(), \
static_cast<const asio::error_code*>(0), \
static_cast<socket_type*>(0))) == 1, \
"MoveAcceptHandler type requirements not met") \
\
typedef asio::detail::handler_type_requirements< \
sizeof( \
asio::detail::argbyv( \
asio::detail::rvref< \
asio_true_handler_type>())) + \
sizeof( \
asio::detail::lvref< \
asio_true_handler_type>()( \
asio::detail::lvref<const asio::error_code>(), \
asio::detail::rvref<socket_type>()), \
char(0))> ASIO_UNUSED_TYPEDEF
#define ASIO_CONNECT_HANDLER_CHECK( \
handler_type, handler) \
\

View File

@ -29,7 +29,8 @@ namespace detail {
reactive_socket_service_base::reactive_socket_service_base(
asio::io_service& io_service)
: reactor_(use_service<reactor>(io_service))
: io_service_(io_service),
reactor_(use_service<reactor>(io_service))
{
reactor_.init_task();
}

View File

@ -130,6 +130,70 @@ private:
Handler handler_;
};
#if defined(ASIO_HAS_MOVE)
template <typename Protocol, typename Handler>
class reactive_socket_move_accept_op :
private Protocol::socket,
public reactive_socket_accept_op_base<typename Protocol::socket, Protocol>
{
public:
ASIO_DEFINE_HANDLER_PTR(reactive_socket_move_accept_op);
reactive_socket_move_accept_op(io_service& ios, socket_type socket,
socket_ops::state_type state, const Protocol& protocol,
typename Protocol::endpoint* peer_endpoint, Handler& handler)
: Protocol::socket(ios),
reactive_socket_accept_op_base<typename Protocol::socket, Protocol>(
socket, state, *this, protocol, peer_endpoint,
&reactive_socket_move_accept_op::do_complete),
handler_(ASIO_MOVE_CAST(Handler)(handler))
{
handler_work<Handler>::start(handler_);
}
static void do_complete(void* owner, operation* base,
const asio::error_code& /*ec*/,
std::size_t /*bytes_transferred*/)
{
// Take ownership of the handler object.
reactive_socket_move_accept_op* o(
static_cast<reactive_socket_move_accept_op*>(base));
ptr p = { asio::detail::addressof(o->handler_), o, o };
typename Protocol::socket peer(
ASIO_MOVE_CAST(typename Protocol::socket)(*o));
handler_work<Handler> w(o->handler_);
ASIO_HANDLER_COMPLETION((o));
// Make a copy of the handler so that the memory can be deallocated before
// the upcall is made. Even if we're not about to make an upcall, a
// sub-object of the handler may be the true owner of the memory associated
// with the handler. Consequently, a local copy of the handler is required
// to ensure that any owning sub-object remains valid until after we have
// deallocated the memory here.
detail::move_binder2<Handler, asio::error_code, typename Protocol::socket>
handler(0, ASIO_MOVE_CAST(Handler)(o->handler_), o->ec_,
ASIO_MOVE_CAST(typename Protocol::socket)(peer));
p.h = asio::detail::addressof(handler.handler_);
p.reset();
// Make the upcall if required.
if (owner)
{
fenced_block b(fenced_block::half);
ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_));
w.complete(handler, handler.handler_);
ASIO_HANDLER_INVOCATION_END;
}
}
private:
Handler handler_;
};
#endif // defined(ASIO_HAS_MOVE)
} // namespace detail
} // namespace asio

View File

@ -388,8 +388,33 @@ public:
return ec;
}
// Start an asynchronous accept. The peer and peer_endpoint objects
// must be valid until the accept's handler is invoked.
// Accept a new connection.
typename Protocol::socket accept(implementation_type& impl,
io_service* peer_io_service, endpoint_type* peer_endpoint,
asio::error_code& ec)
{
typename Protocol::socket peer(
peer_io_service ? *peer_io_service : io_service_);
std::size_t addr_len = peer_endpoint ? peer_endpoint->capacity() : 0;
socket_holder new_socket(socket_ops::sync_accept(impl.socket_,
impl.state_, peer_endpoint ? peer_endpoint->data() : 0,
peer_endpoint ? &addr_len : 0, ec));
// On success, assign new connection to peer socket object.
if (new_socket.get() != invalid_socket)
{
if (peer_endpoint)
peer_endpoint->resize(addr_len);
if (!peer.assign(impl.protocol_, new_socket.get(), ec))
new_socket.release();
}
return peer;
}
// Start an asynchronous accept. The peer and peer_endpoint objects must be
// valid until the accept's handler is invoked.
template <typename Socket, typename Handler>
void async_accept(implementation_type& impl, Socket& peer,
endpoint_type* peer_endpoint, Handler& handler)
@ -410,6 +435,30 @@ public:
p.v = p.p = 0;
}
#if defined(ASIO_HAS_MOVE)
// Start an asynchronous accept. The peer_endpoint object must be valid until
// the accept's handler is invoked.
template <typename Handler>
void async_accept(implementation_type& impl,
endpoint_type* peer_endpoint, Handler& handler)
{
bool is_continuation =
asio_handler_cont_helpers::is_continuation(handler);
// Allocate and construct an operation to wrap the handler.
typedef reactive_socket_move_accept_op<Protocol, Handler> op;
typename op::ptr p = { asio::detail::addressof(handler),
op::ptr::allocate(handler), 0 };
p.p = new (p.v) op(io_service_, impl.socket_, impl.state_,
impl.protocol_, peer_endpoint, handler);
ASIO_HANDLER_CREATION((p.p, "socket", &impl, "async_accept"));
start_accept_op(impl, p.p, is_continuation, false);
p.v = p.p = 0;
}
#endif // defined(ASIO_HAS_MOVE)
// Connect the socket to the specified endpoint.
asio::error_code connect(implementation_type& impl,
const endpoint_type& peer_endpoint, asio::error_code& ec)

View File

@ -490,6 +490,9 @@ protected:
reactor_op* op, bool is_continuation,
const socket_addr_type* addr, size_t addrlen);
// The io_service that owns this socket service.
io_service& io_service_;
// The selector that performs event demultiplexing for the service.
reactor& reactor_;
};

View File

@ -457,6 +457,31 @@ public:
return ec;
}
// Accept a new connection.
typename Protocol::socket accept(implementation_type& impl,
io_service* peer_io_service, endpoint_type* peer_endpoint,
asio::error_code& ec)
{
typename Protocol::socket peer(
peer_io_service ? *peer_io_service : get_io_service());
std::size_t addr_len = peer_endpoint ? peer_endpoint->capacity() : 0;
socket_holder new_socket(socket_ops::sync_accept(impl.socket_,
impl.state_, peer_endpoint ? peer_endpoint->data() : 0,
peer_endpoint ? &addr_len : 0, ec));
// On success, assign new connection to peer socket object.
if (new_socket.get() != invalid_socket)
{
if (peer_endpoint)
peer_endpoint->resize(addr_len);
if (!peer.assign(impl.protocol_, new_socket.get(), ec))
new_socket.release();
}
return ec;
}
// Start an asynchronous accept. The peer and peer_endpoint objects
// must be valid until the accept's handler is invoked.
template <typename Socket, typename Handler>

View File

@ -276,6 +276,16 @@ public:
return service_impl_.accept(impl, peer, peer_endpoint, ec);
}
#if defined(ASIO_HAS_MOVE)
/// Accept a new connection.
typename Protocol::socket accept(implementation_type& impl,
io_service* peer_io_service, endpoint_type* peer_endpoint,
asio::error_code& ec)
{
return service_impl_.accept(impl, peer_io_service, peer_endpoint, ec);
}
#endif // defined(ASIO_HAS_MOVE)
/// Start an asynchronous accept.
template <typename Protocol1, typename SocketService, typename AcceptHandler>
ASIO_INITFN_RESULT_TYPE(AcceptHandler,
@ -294,6 +304,24 @@ public:
return init.result.get();
}
#if defined(ASIO_HAS_MOVE)
/// Start an asynchronous accept.
template <typename MoveAcceptHandler>
ASIO_INITFN_RESULT_TYPE(MoveAcceptHandler,
void (asio::error_code, typename Protocol::socket))
async_accept(implementation_type& impl,
endpoint_type* peer_endpoint,
ASIO_MOVE_ARG(MoveAcceptHandler) handler)
{
async_completion<MoveAcceptHandler,
void (asio::error_code, typename Protocol::socket)> init(handler);
service_impl_.async_accept(impl, peer_endpoint, init.handler);
return init.result.get();
}
#endif // defined(ASIO_HAS_MOVE)
private:
// Destroy all user-defined handler objects owned by the service.
void shutdown_service()

View File

@ -346,6 +346,7 @@
</simplelist>
<bridgehead renderas="sect3">Type Requirements</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="asio.reference.AcceptableProtocol">AcceptableProtocol</link></member>
<member><link linkend="asio.reference.AcceptHandler">AcceptHandler</link></member>
<member><link linkend="asio.reference.ComposedConnectHandler">ComposedConnectHandler</link></member>
<member><link linkend="asio.reference.ConnectHandler">ConnectHandler</link></member>

View File

@ -37,6 +37,7 @@
[xinclude quickref.xml]
[include requirements/asynchronous_operations.qbk]
[include requirements/AcceptableProtocol.qbk]
[include requirements/AcceptHandler.qbk]
[include requirements/AsyncRandomAccessReadDevice.qbk]
[include requirements/AsyncRandomAccessWriteDevice.qbk]

View File

@ -0,0 +1,25 @@
[/
/ Copyright (c) 2003-2015 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)
/]
[section:AcceptableProtocol Acceptable protocol requirements]
An acceptable protocol must meet the requirements for a [link
asio.reference.Protocol protocol] as well as the additional requirements listed
below.
In the table below, `X` denotes an acceptable protocol class.
[table AcceptableProtocol requirements
[[expression] [return type] [assertion/note\npre/post-conditions]]
[
[`X::socket`]
[`
[The type of a socket for the protocol.]
]
]
[endsect]

View File

@ -0,0 +1,41 @@
[/
/ Copyright (c) 2003-2015 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)
/]
[section:MoveAcceptHandler Move accept handler requirements]
A move accept handler must meet the requirements for a [link
asio.reference.Handler handler]. A value `h` of a move accept handler class
should work correctly in the expression `h(ec, s)`, where `ec` is an lvalue of
type `const error_code` and `s` is an lvalue of the nested type
`Protocol::socket` for the type `Protocol` of the socket class template.
[heading Examples]
A free function as a move accept handler:
void connect_handler(
const asio::error_code& ec,
asio::ip::tcp::socket s)
{
...
}
A move accept handler function object:
struct connect_handler
{
...
void operator()(
const asio::error_code& ec,
asio::ip::tcp::socket s)
{
...
}
...
};
[endsect]

View File

@ -66,8 +66,7 @@ class server
{
public:
server(asio::io_service& io_service, short port)
: acceptor_(io_service, tcp::endpoint(tcp::v4(), port)),
socket_(io_service)
: acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
{
do_accept();
}
@ -75,12 +74,12 @@ public:
private:
void do_accept()
{
acceptor_.async_accept(socket_,
[this](std::error_code ec)
acceptor_.async_accept(
[this](std::error_code ec, tcp::socket socket)
{
if (!ec)
{
std::make_shared<session>(std::move(socket_))->start();
std::make_shared<session>(std::move(socket))->start();
}
do_accept();
@ -88,7 +87,6 @@ private:
}
tcp::acceptor acceptor_;
tcp::socket socket_;
};
int main(int argc, char* argv[])

View File

@ -47,9 +47,7 @@ void server(asio::io_service& io_service, unsigned short port)
tcp::acceptor a(io_service, tcp::endpoint(tcp::v4(), port));
for (;;)
{
tcp::socket sock(io_service);
a.accept(sock);
std::thread(session, std::move(sock)).detach();
std::thread(session, a.accept()).detach();
}
}