Use ConnectEx where available.
N.B. This change conservatively limits the use of ConnectEx to connection-oriented IP sockets.
This commit is contained in:
parent
ac9babae60
commit
9fc74ea135
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
|
123
asio/include/asio/detail/win_iocp_socket_connect_op.hpp
Normal file
123
asio/include/asio/detail/win_iocp_socket_connect_op.hpp
Normal 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
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
@ -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_;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user