Add ability to release ownership of the underlying native socket.

This commit adds release() member functions to basic_socket and
basic_socket_acceptor.

Note: When using I/O completion ports on Windows, these functions fail
with error::operation_not_supported for versions of Windows prior to
Windows 8.1.
This commit is contained in:
Christopher Kohlhoff 2017-02-22 22:46:15 +11:00
parent d28ec54750
commit d3ca90a05c
17 changed files with 271 additions and 0 deletions

View File

@ -449,6 +449,58 @@ public:
ASIO_SYNC_OP_VOID_RETURN(ec);
}
/// Release ownership of the underlying native socket.
/**
* This function causes all outstanding asynchronous connect, send and receive
* operations to finish immediately, and the handlers for cancelled operations
* will be passed the asio::error::operation_aborted error. Ownership
* of the native socket is then transferred to the caller.
*
* @throws asio::system_error Thrown on failure.
*
* @note This function is unsupported on Windows versions prior to Windows
* 8.1, and will fail with asio::error::operation_not_supported on
* these platforms.
*/
#if defined(ASIO_MSVC) && (ASIO_MSVC >= 1400) \
&& (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0603)
__declspec(deprecated("This function always fails with "
"operation_not_supported when used on Windows versions "
"prior to Windows 8.1."))
#endif
native_handle_type release()
{
asio::error_code ec;
native_handle_type s = this->get_service().release(
this->get_implementation(), ec);
asio::detail::throw_error(ec, "release");
return s;
}
/// Release ownership of the underlying native socket.
/**
* This function causes all outstanding asynchronous connect, send and receive
* operations to finish immediately, and the handlers for cancelled operations
* will be passed the asio::error::operation_aborted error. Ownership
* of the native socket is then transferred to the caller.
*
* @param ec Set to indicate what error occurred, if any.
*
* @note This function is unsupported on Windows versions prior to Windows
* 8.1, and will fail with asio::error::operation_not_supported on
* these platforms.
*/
#if defined(ASIO_MSVC) && (ASIO_MSVC >= 1400) \
&& (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0603)
__declspec(deprecated("This function always fails with "
"operation_not_supported when used on Windows versions "
"prior to Windows 8.1."))
#endif
native_handle_type release(asio::error_code& ec)
{
return this->get_service().release(this->get_implementation(), ec);
}
/// Get the native socket representation.
/**
* This function may be used to obtain the underlying representation of the

View File

@ -566,6 +566,58 @@ public:
ASIO_SYNC_OP_VOID_RETURN(ec);
}
/// Release ownership of the underlying native acceptor.
/**
* This function causes all outstanding asynchronous accept operations to
* finish immediately, and the handlers for cancelled operations will be
* passed the asio::error::operation_aborted error. Ownership of the
* native acceptor is then transferred to the caller.
*
* @throws asio::system_error Thrown on failure.
*
* @note This function is unsupported on Windows versions prior to Windows
* 8.1, and will fail with asio::error::operation_not_supported on
* these platforms.
*/
#if defined(ASIO_MSVC) && (ASIO_MSVC >= 1400) \
&& (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0603)
__declspec(deprecated("This function always fails with "
"operation_not_supported when used on Windows versions "
"prior to Windows 8.1."))
#endif
native_handle_type release()
{
asio::error_code ec;
native_handle_type s = this->get_service().release(
this->get_implementation(), ec);
asio::detail::throw_error(ec, "release");
return s;
}
/// Release ownership of the underlying native acceptor.
/**
* This function causes all outstanding asynchronous accept operations to
* finish immediately, and the handlers for cancelled operations will be
* passed the asio::error::operation_aborted error. Ownership of the
* native acceptor is then transferred to the caller.
*
* @param ec Set to indicate what error occurred, if any.
*
* @note This function is unsupported on Windows versions prior to Windows
* 8.1, and will fail with asio::error::operation_not_supported on
* these platforms.
*/
#if defined(ASIO_MSVC) && (ASIO_MSVC >= 1400) \
&& (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0603)
__declspec(deprecated("This function always fails with "
"operation_not_supported when used on Windows versions "
"prior to Windows 8.1."))
#endif
native_handle_type release(asio::error_code& ec)
{
return this->get_service().release(this->get_implementation(), ec);
}
/// Get the native acceptor representation.
/**
* This function may be used to obtain the underlying representation of the

View File

@ -167,6 +167,13 @@ public:
ASIO_SYNC_OP_VOID_RETURN(ec);
}
/// Release ownership of the underlying socket.
native_handle_type release(implementation_type& impl,
asio::error_code& ec)
{
return service_impl_.release(impl, ec);
}
/// Get the native socket implementation.
native_handle_type native_handle(implementation_type& impl)
{

View File

@ -121,6 +121,25 @@ asio::error_code reactive_socket_service_base::close(
return ec;
}
socket_type reactive_socket_service_base::release(
reactive_socket_service_base::base_implementation_type& impl,
asio::error_code& ec)
{
if (!is_open(impl))
return invalid_socket;
cancel(impl, ec);
if (ec)
return invalid_socket;
reactor_.deregister_descriptor(impl.socket_, impl.reactor_data_,
(impl.state_ & socket_ops::possible_dup) == 0);
socket_type tmp = impl.socket_;
impl.socket_ = invalid_socket;
return tmp;
}
asio::error_code reactive_socket_service_base::cancel(
reactive_socket_service_base::base_implementation_type& impl,
asio::error_code& ec)

View File

@ -32,6 +32,7 @@ win_iocp_socket_service_base::win_iocp_socket_service_base(
iocp_service_(use_service<win_iocp_io_context>(io_context)),
reactor_(0),
connect_ex_(0),
nt_set_info_(0),
mutex_(),
impl_list_(0)
{
@ -191,6 +192,39 @@ asio::error_code win_iocp_socket_service_base::close(
return ec;
}
socket_type win_iocp_socket_service_base::release(
win_iocp_socket_service_base::base_implementation_type& impl,
asio::error_code& ec)
{
if (!is_open(impl))
return invalid_socket;
cancel(impl, ec);
if (ec)
return invalid_socket;
nt_set_info_fn fn = get_nt_set_info();
if (fn == 0)
{
ec = asio::error::operation_not_supported;
return invalid_socket;
}
HANDLE sock_as_handle = reinterpret_cast<HANDLE>(impl.socket_);
ULONG_PTR iosb[2] = { 0, 0 };
void* info[2] = { 0, 0 };
if (fn(sock_as_handle, iosb, &info, sizeof(info),
61 /* FileReplaceCompletionInformation */))
{
ec = asio::error::operation_not_supported;
return invalid_socket;
}
socket_type tmp = impl.socket_;
impl.socket_ = invalid_socket;
return tmp;
}
asio::error_code win_iocp_socket_service_base::cancel(
win_iocp_socket_service_base::base_implementation_type& impl,
asio::error_code& ec)
@ -704,6 +738,24 @@ win_iocp_socket_service_base::get_connect_ex(
#endif // defined(ASIO_DISABLE_CONNECTEX)
}
win_iocp_socket_service_base::nt_set_info_fn
win_iocp_socket_service_base::get_nt_set_info()
{
void* ptr = interlocked_compare_exchange_pointer(&nt_set_info_, 0, 0);
if (!ptr)
{
if (HMODULE h = GetModuleHandle("NTDLL.DLL"))
ptr = GetProcAddress(h, "NtSetInformationFile");
// On failure, set nt_set_info_ to a special value to indicate that the
// NtSetInformationFile function is unavailable. That way we won't bother
// trying to look it up again.
interlocked_exchange_pointer(&nt_set_info_, ptr ? ptr : this);
}
return reinterpret_cast<nt_set_info_fn>(ptr == this ? 0 : ptr);
}
void* win_iocp_socket_service_base::interlocked_compare_exchange_pointer(
void** dest, void* exch, void* cmp)
{

View File

@ -148,6 +148,23 @@ asio::error_code winrt_ssocket_service_base::close(
return ec;
}
winrt_ssocket_service_base::native_handle_type
winrt_ssocket_service_base::release(
winrt_ssocket_service_base::base_implementation_type& impl,
asio::error_code& ec)
{
if (!is_open(impl))
return nullptr;
cancel(impl, ec);
if (ec)
return nullptr;
native_handle_type tmp = impl.socket_;
impl.socket_ = nullptr;
return tmp;
}
std::size_t winrt_ssocket_service_base::do_get_endpoint(
const base_implementation_type& impl, bool local,
void* addr, std::size_t addr_len, asio::error_code& ec) const

View File

@ -119,6 +119,14 @@ public:
return ec;
}
// Release ownership of the socket.
native_handle_type release(implementation_type&,
asio::error_code& ec)
{
ec = asio::error::operation_not_supported;
return 0;
}
// Get the native socket representation.
native_handle_type native_handle(implementation_type&)
{

View File

@ -93,6 +93,10 @@ public:
ASIO_DECL asio::error_code close(
base_implementation_type& impl, asio::error_code& ec);
// Release ownership of the socket.
ASIO_DECL socket_type release(
base_implementation_type& impl, asio::error_code& ec);
// Get the native socket representation.
native_handle_type native_handle(base_implementation_type& impl)
{

View File

@ -116,6 +116,10 @@ public:
ASIO_DECL asio::error_code close(
base_implementation_type& impl, asio::error_code& ec);
// Release ownership of the socket.
ASIO_DECL socket_type release(
base_implementation_type& impl, asio::error_code& ec);
// Cancel all operations associated with the socket.
ASIO_DECL asio::error_code cancel(
base_implementation_type& impl, asio::error_code& ec);
@ -528,6 +532,15 @@ protected:
ASIO_DECL connect_ex_fn get_connect_ex(
base_implementation_type& impl, int type);
// The type of a NtSetInformationFile function pointer.
typedef LONG (NTAPI *nt_set_info_fn)(HANDLE, ULONG_PTR*, void*, ULONG, ULONG);
// Helper function to get the NtSetInformationFile function pointer. If no
// NtSetInformationFile pointer has been obtained yet, one is obtained using
// GetProcAddress and the pointer is cached. Returns a null pointer if
// NtSetInformationFile is not available.
ASIO_DECL nt_set_info_fn get_nt_set_info();
// Helper function to emulate InterlockedCompareExchangePointer functionality
// for:
// - very old Platform SDKs; and
@ -554,6 +567,9 @@ protected:
// Pointer to ConnectEx implementation.
void* connect_ex_;
// Pointer to NtSetInformationFile implementation.
void* nt_set_info_;
// Mutex to protect access to the linked list of implementations.
asio::detail::mutex mutex_;

View File

@ -92,6 +92,10 @@ public:
ASIO_DECL asio::error_code close(
base_implementation_type& impl, asio::error_code& ec);
// Release ownership of the socket.
ASIO_DECL native_handle_type release(
base_implementation_type& impl, asio::error_code& ec);
// Get the native socket representation.
native_handle_type native_handle(base_implementation_type& impl)
{

View File

@ -167,6 +167,13 @@ public:
ASIO_SYNC_OP_VOID_RETURN(ec);
}
/// Release ownership of the underlying socket.
native_handle_type release(implementation_type& impl,
asio::error_code& ec)
{
return service_impl_.release(impl, ec);
}
/// Get the native socket implementation.
native_handle_type native_handle(implementation_type& impl)
{

View File

@ -169,6 +169,13 @@ public:
ASIO_SYNC_OP_VOID_RETURN(ec);
}
/// Release ownership of the underlying socket.
native_handle_type release(implementation_type& impl,
asio::error_code& ec)
{
return service_impl_.release(impl, ec);
}
/// Get the native socket implementation.
native_handle_type native_handle(implementation_type& impl)
{

View File

@ -188,6 +188,13 @@ public:
ASIO_SYNC_OP_VOID_RETURN(ec);
}
/// Release ownership of the underlying acceptor.
native_handle_type release(implementation_type& impl,
asio::error_code& ec)
{
return service_impl_.release(impl, ec);
}
/// Get the native acceptor implementation.
native_handle_type native_handle(implementation_type& impl)
{

View File

@ -167,6 +167,13 @@ public:
ASIO_SYNC_OP_VOID_RETURN(ec);
}
/// Release ownership of the underlying socket.
native_handle_type release(implementation_type& impl,
asio::error_code& ec)
{
return service_impl_.release(impl, ec);
}
/// Get the native socket implementation.
native_handle_type native_handle(implementation_type& impl)
{

View File

@ -156,6 +156,9 @@ void test()
socket1.close();
socket1.close(ec);
socket1.release();
socket1.release(ec);
ip::icmp::socket::native_handle_type native_socket4
= socket1.native_handle();
(void)native_socket4;

View File

@ -304,6 +304,9 @@ void test()
socket1.close();
socket1.close(ec);
socket1.release();
socket1.release(ec);
ip::tcp::socket::native_handle_type native_socket4
= socket1.native_handle();
(void)native_socket4;
@ -870,6 +873,9 @@ void test()
acceptor1.close();
acceptor1.close(ec);
acceptor1.release();
acceptor1.release(ec);
ip::tcp::acceptor::native_handle_type native_acceptor4
= acceptor1.native_handle();
(void)native_acceptor4;

View File

@ -172,6 +172,9 @@ void test()
socket1.close();
socket1.close(ec);
socket1.release();
socket1.release(ec);
ip::udp::socket::native_handle_type native_socket4
= socket1.native_handle();
(void)native_socket4;