Use ConnectEx where available.

N.B. This change conservatively limits the use of ConnectEx to
connection-oriented IP sockets.
This commit is contained in:
Christopher Kohlhoff 2014-05-02 22:13:35 +10:00
parent ac9babae60
commit 9fc74ea135
6 changed files with 240 additions and 6 deletions

View File

@ -520,6 +520,22 @@ void sync_connect(socket_type s, const socket_addr_type* addr,
asio::error::get_system_category());
}
#if defined(ASIO_HAS_IOCP)
void complete_iocp_connect(socket_type s, asio::error_code& ec)
{
if (!ec)
{
// Need to set the SO_UPDATE_CONNECT_CONTEXT option so that getsockname
// and getpeername will work on the connected socket.
socket_ops::state_type state = 0;
socket_ops::setsockopt(s, state, SOL_SOCKET,
SO_UPDATE_CONNECT_CONTEXT, 0, 0, ec);
}
}
#endif // defined(ASIO_HAS_IOCP)
bool non_blocking_connect(socket_type s, asio::error_code& ec)
{
// Check if the connect operation has finished. This is required since we may

View File

@ -31,6 +31,7 @@ win_iocp_socket_service_base::win_iocp_socket_service_base(
: io_service_(io_service),
iocp_service_(use_service<win_iocp_io_service>(io_service)),
reactor_(0),
connect_ex_(0),
mutex_(),
impl_list_(0)
{
@ -544,8 +545,51 @@ void win_iocp_socket_service_base::start_reactor_op(
void win_iocp_socket_service_base::start_connect_op(
win_iocp_socket_service_base::base_implementation_type& impl,
reactor_op* op, const socket_addr_type* addr, std::size_t addrlen)
int family, int type, const socket_addr_type* addr,
std::size_t addrlen, win_iocp_socket_connect_op_base* op)
{
// If ConnectEx is available, use that.
if (family == ASIO_OS_DEF(AF_INET)
|| family == ASIO_OS_DEF(AF_INET6))
{
if (connect_ex_fn connect_ex = get_connect_ex(impl, type))
{
union address_union
{
socket_addr_type base;
sockaddr_in4_type v4;
sockaddr_in6_type v6;
} a;
using namespace std; // For memset.
memset(&a, 0, sizeof(a));
a.base.sa_family = family;
socket_ops::bind(impl.socket_, &a.base,
family == ASIO_OS_DEF(AF_INET)
? sizeof(a.v4) : sizeof(a.v6), op->ec_);
if (op->ec_ && op->ec_ != asio::error::invalid_argument)
{
iocp_service_.post_immediate_completion(op, false);
return;
}
op->connect_ex_ = true;
update_cancellation_thread_id(impl);
iocp_service_.work_started();
BOOL result = connect_ex(impl.socket_,
addr, static_cast<int>(addrlen), 0, 0, 0, op);
DWORD last_error = ::WSAGetLastError();
if (!result && last_error != WSA_IO_PENDING)
iocp_service_.on_completion(op, last_error);
else
iocp_service_.on_pending(op);
return;
}
}
// Otherwise, fall back to a reactor-based implementation.
reactor& r = get_reactor();
update_cancellation_thread_id(impl);
@ -622,6 +666,35 @@ reactor& win_iocp_socket_service_base::get_reactor()
return *r;
}
win_iocp_socket_service_base::connect_ex_fn
win_iocp_socket_service_base::get_connect_ex(
win_iocp_socket_service_base::base_implementation_type& impl, int type)
{
if (type != ASIO_OS_DEF(SOCK_STREAM)
&& type != ASIO_OS_DEF(SOCK_SEQPACKET))
return 0;
void* ptr = interlocked_compare_exchange_pointer(&connect_ex_, 0, 0);
if (!ptr)
{
GUID guid = { 0x25a207b9, 0xddf3, 0x4660,
{ 0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e } };
DWORD bytes = 0;
if (::WSAIoctl(impl.socket_, SIO_GET_EXTENSION_FUNCTION_POINTER,
&guid, sizeof(guid), &ptr, sizeof(ptr), &bytes, 0, 0) != 0)
{
// Set connect_ex_ to a special value to indicate that ConnectEx is
// unavailable. That way we won't bother trying to look it up again.
ptr = this;
}
interlocked_exchange_pointer(&connect_ex_, ptr);
}
return reinterpret_cast<connect_ex_fn>(ptr == this ? 0 : ptr);
}
void* win_iocp_socket_service_base::interlocked_compare_exchange_pointer(
void** dest, void* exch, void* cmp)
{

View File

@ -107,6 +107,13 @@ ASIO_DECL int connect(socket_type s, const socket_addr_type* addr,
ASIO_DECL void sync_connect(socket_type s, const socket_addr_type* addr,
std::size_t addrlen, asio::error_code& ec);
#if defined(ASIO_HAS_IOCP)
ASIO_DECL void complete_iocp_connect(socket_type s,
asio::error_code& ec);
#endif // defined(ASIO_HAS_IOCP)
ASIO_DECL bool non_blocking_connect(socket_type s,
asio::error_code& ec);

View File

@ -0,0 +1,123 @@
//
// detail/win_iocp_socket_connect_op.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2014 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 ASIO_DETAIL_WIN_IOCP_SOCKET_CONNECT_OP_HPP
#define ASIO_DETAIL_WIN_IOCP_SOCKET_CONNECT_OP_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#if defined(ASIO_HAS_IOCP)
#include "asio/detail/addressof.hpp"
#include "asio/detail/bind_handler.hpp"
#include "asio/detail/fenced_block.hpp"
#include "asio/detail/handler_alloc_helpers.hpp"
#include "asio/detail/handler_invoke_helpers.hpp"
#include "asio/detail/reactor_op.hpp"
#include "asio/detail/socket_ops.hpp"
#include "asio/error.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail {
class win_iocp_socket_connect_op_base : public reactor_op
{
public:
win_iocp_socket_connect_op_base(socket_type socket, func_type complete_func)
: reactor_op(&win_iocp_socket_connect_op_base::do_perform, complete_func),
socket_(socket),
connect_ex_(false)
{
}
static bool do_perform(reactor_op* base)
{
win_iocp_socket_connect_op_base* o(
static_cast<win_iocp_socket_connect_op_base*>(base));
return socket_ops::non_blocking_connect(o->socket_, o->ec_);
}
socket_type socket_;
bool connect_ex_;
};
template <typename Handler>
class win_iocp_socket_connect_op : public win_iocp_socket_connect_op_base
{
public:
ASIO_DEFINE_HANDLER_PTR(win_iocp_socket_connect_op);
win_iocp_socket_connect_op(socket_type socket, Handler& handler)
: win_iocp_socket_connect_op_base(socket,
&win_iocp_socket_connect_op::do_complete),
handler_(ASIO_MOVE_CAST(Handler)(handler))
{
}
static void do_complete(io_service_impl* owner, operation* base,
const asio::error_code& result_ec,
std::size_t /*bytes_transferred*/)
{
asio::error_code ec(result_ec);
// Take ownership of the operation object.
win_iocp_socket_connect_op* o(static_cast<win_iocp_socket_connect_op*>(base));
ptr p = { asio::detail::addressof(o->handler_), o, o };
if (owner)
{
if (o->connect_ex_)
socket_ops::complete_iocp_connect(o->socket_, ec);
else
ec = o->ec_;
}
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::binder1<Handler, asio::error_code>
handler(o->handler_, ec);
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_));
asio_handler_invoke_helpers::invoke(handler, handler.handler_);
ASIO_HANDLER_INVOCATION_END;
}
}
private:
Handler handler_;
};
} // namespace detail
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // defined(ASIO_HAS_IOCP)
#endif // ASIO_DETAIL_WIN_IOCP_SOCKET_CONNECT_OP_HPP

View File

@ -31,7 +31,6 @@
#include "asio/detail/handler_invoke_helpers.hpp"
#include "asio/detail/mutex.hpp"
#include "asio/detail/operation.hpp"
#include "asio/detail/reactive_socket_connect_op.hpp"
#include "asio/detail/reactor.hpp"
#include "asio/detail/reactor_op.hpp"
#include "asio/detail/socket_holder.hpp"
@ -40,6 +39,7 @@
#include "asio/detail/win_iocp_io_service.hpp"
#include "asio/detail/win_iocp_null_buffers_op.hpp"
#include "asio/detail/win_iocp_socket_accept_op.hpp"
#include "asio/detail/win_iocp_socket_connect_op.hpp"
#include "asio/detail/win_iocp_socket_recvfrom_op.hpp"
#include "asio/detail/win_iocp_socket_send_op.hpp"
#include "asio/detail/win_iocp_socket_service_base.hpp"
@ -501,7 +501,7 @@ public:
const endpoint_type& peer_endpoint, Handler& handler)
{
// Allocate and construct an operation to wrap the handler.
typedef reactive_socket_connect_op<Handler> op;
typedef win_iocp_socket_connect_op<Handler> op;
typename op::ptr p = { asio::detail::addressof(handler),
asio_handler_alloc_helpers::allocate(
sizeof(op), handler), 0 };
@ -509,8 +509,8 @@ public:
ASIO_HANDLER_CREATION((p.p, "socket", &impl, "async_connect"));
start_connect_op(impl, p.p, peer_endpoint.data(),
static_cast<int>(peer_endpoint.size()));
start_connect_op(impl, impl.protocol_.family(), impl.protocol_.type(),
peer_endpoint.data(), static_cast<int>(peer_endpoint.size()), p.p);
p.v = p.p = 0;
}
};

View File

@ -37,6 +37,7 @@
#include "asio/detail/socket_types.hpp"
#include "asio/detail/win_iocp_io_service.hpp"
#include "asio/detail/win_iocp_null_buffers_op.hpp"
#include "asio/detail/win_iocp_socket_connect_op.hpp"
#include "asio/detail/win_iocp_socket_send_op.hpp"
#include "asio/detail/win_iocp_socket_recv_op.hpp"
#include "asio/detail/win_iocp_socket_recvmsg_op.hpp"
@ -450,7 +451,8 @@ protected:
// Start the asynchronous connect operation using the reactor.
ASIO_DECL void start_connect_op(base_implementation_type& impl,
reactor_op* op, const socket_addr_type* addr, std::size_t addrlen);
int family, int type, const socket_addr_type* remote_addr,
std::size_t remote_addrlen, win_iocp_socket_connect_op_base* op);
// Helper function to close a socket when the associated object is being
// destroyed.
@ -465,6 +467,16 @@ protected:
// this service.
ASIO_DECL reactor& get_reactor();
// The type of a ConnectEx function pointer, as old SDKs may not provide it.
typedef BOOL (PASCAL *connect_ex_fn)(SOCKET,
const socket_addr_type*, int, void*, DWORD, DWORD*, OVERLAPPED*);
// Helper function to get the ConnectEx pointer. If no ConnectEx pointer has
// been obtained yet, one is obtained using WSAIoctl and the pointer is
// cached. Returns a null pointer if ConnectEx is not available.
ASIO_DECL connect_ex_fn get_connect_ex(
base_implementation_type& impl, int type);
// Helper function to emulate InterlockedCompareExchangePointer functionality
// for:
// - very old Platform SDKs; and
@ -488,6 +500,9 @@ protected:
// only if needed.
reactor* reactor_;
// Pointer to ConnectEx implementation.
void* connect_ex_;
// Mutex to protect access to the linked list of implementations.
asio::detail::mutex mutex_;