| // |
| // detail/impl/win_iocp_socket_service_base.ipp |
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| // |
| // Copyright (c) 2003-2010 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_IMPL_WIN_IOCP_SOCKET_SERVICE_BASE_IPP |
| #define BOOST_ASIO_DETAIL_IMPL_WIN_IOCP_SOCKET_SERVICE_BASE_IPP |
| |
| #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_IOCP) |
| |
| #include <boost/asio/detail/win_iocp_socket_service_base.hpp> |
| |
| #include <boost/asio/detail/push_options.hpp> |
| |
| namespace boost { |
| namespace asio { |
| namespace detail { |
| |
| win_iocp_socket_service_base::win_iocp_socket_service_base( |
| boost::asio::io_service& io_service) |
| : io_service_(io_service), |
| iocp_service_(use_service<win_iocp_io_service>(io_service)), |
| reactor_(0), |
| mutex_(), |
| impl_list_(0) |
| { |
| } |
| |
| void win_iocp_socket_service_base::shutdown_service() |
| { |
| // Close all implementations, causing all operations to complete. |
| boost::asio::detail::mutex::scoped_lock lock(mutex_); |
| base_implementation_type* impl = impl_list_; |
| while (impl) |
| { |
| boost::system::error_code ignored_ec; |
| close_for_destruction(*impl); |
| impl = impl->next_; |
| } |
| } |
| |
| void win_iocp_socket_service_base::construct( |
| win_iocp_socket_service_base::base_implementation_type& impl) |
| { |
| impl.socket_ = invalid_socket; |
| impl.state_ = 0; |
| impl.cancel_token_.reset(); |
| #if defined(BOOST_ASIO_ENABLE_CANCELIO) |
| impl.safe_cancellation_thread_id_ = 0; |
| #endif // defined(BOOST_ASIO_ENABLE_CANCELIO) |
| |
| // Insert implementation into linked list of all implementations. |
| boost::asio::detail::mutex::scoped_lock lock(mutex_); |
| impl.next_ = impl_list_; |
| impl.prev_ = 0; |
| if (impl_list_) |
| impl_list_->prev_ = &impl; |
| impl_list_ = &impl; |
| } |
| |
| void win_iocp_socket_service_base::destroy( |
| win_iocp_socket_service_base::base_implementation_type& impl) |
| { |
| close_for_destruction(impl); |
| |
| // Remove implementation from linked list of all implementations. |
| boost::asio::detail::mutex::scoped_lock lock(mutex_); |
| if (impl_list_ == &impl) |
| impl_list_ = impl.next_; |
| if (impl.prev_) |
| impl.prev_->next_ = impl.next_; |
| if (impl.next_) |
| impl.next_->prev_= impl.prev_; |
| impl.next_ = 0; |
| impl.prev_ = 0; |
| } |
| |
| boost::system::error_code win_iocp_socket_service_base::close( |
| win_iocp_socket_service_base::base_implementation_type& impl, |
| boost::system::error_code& ec) |
| { |
| if (is_open(impl)) |
| { |
| // Check if the reactor was created, in which case we need to close the |
| // socket on the reactor as well to cancel any operations that might be |
| // running there. |
| reactor* r = static_cast<reactor*>( |
| interlocked_compare_exchange_pointer( |
| reinterpret_cast<void**>(&reactor_), 0, 0)); |
| if (r) |
| r->close_descriptor(impl.socket_, impl.reactor_data_); |
| } |
| |
| if (socket_ops::close(impl.socket_, impl.state_, false, ec) == 0) |
| { |
| impl.socket_ = invalid_socket; |
| impl.state_ = 0; |
| impl.cancel_token_.reset(); |
| #if defined(BOOST_ASIO_ENABLE_CANCELIO) |
| impl.safe_cancellation_thread_id_ = 0; |
| #endif // defined(BOOST_ASIO_ENABLE_CANCELIO) |
| } |
| |
| return ec; |
| } |
| |
| boost::system::error_code win_iocp_socket_service_base::cancel( |
| win_iocp_socket_service_base::base_implementation_type& impl, |
| boost::system::error_code& ec) |
| { |
| if (!is_open(impl)) |
| { |
| ec = boost::asio::error::bad_descriptor; |
| return ec; |
| } |
| else if (FARPROC cancel_io_ex_ptr = ::GetProcAddress( |
| ::GetModuleHandleA("KERNEL32"), "CancelIoEx")) |
| { |
| // The version of Windows supports cancellation from any thread. |
| typedef BOOL (WINAPI* cancel_io_ex_t)(HANDLE, LPOVERLAPPED); |
| cancel_io_ex_t cancel_io_ex = (cancel_io_ex_t)cancel_io_ex_ptr; |
| socket_type sock = impl.socket_; |
| HANDLE sock_as_handle = reinterpret_cast<HANDLE>(sock); |
| if (!cancel_io_ex(sock_as_handle, 0)) |
| { |
| DWORD last_error = ::GetLastError(); |
| if (last_error == ERROR_NOT_FOUND) |
| { |
| // ERROR_NOT_FOUND means that there were no operations to be |
| // cancelled. We swallow this error to match the behaviour on other |
| // platforms. |
| ec = boost::system::error_code(); |
| } |
| else |
| { |
| ec = boost::system::error_code(last_error, |
| boost::asio::error::get_system_category()); |
| } |
| } |
| else |
| { |
| ec = boost::system::error_code(); |
| } |
| } |
| #if defined(BOOST_ASIO_ENABLE_CANCELIO) |
| else if (impl.safe_cancellation_thread_id_ == 0) |
| { |
| // No operations have been started, so there's nothing to cancel. |
| ec = boost::system::error_code(); |
| } |
| else if (impl.safe_cancellation_thread_id_ == ::GetCurrentThreadId()) |
| { |
| // Asynchronous operations have been started from the current thread only, |
| // so it is safe to try to cancel them using CancelIo. |
| socket_type sock = impl.socket_; |
| HANDLE sock_as_handle = reinterpret_cast<HANDLE>(sock); |
| if (!::CancelIo(sock_as_handle)) |
| { |
| DWORD last_error = ::GetLastError(); |
| ec = boost::system::error_code(last_error, |
| boost::asio::error::get_system_category()); |
| } |
| else |
| { |
| ec = boost::system::error_code(); |
| } |
| } |
| else |
| { |
| // Asynchronous operations have been started from more than one thread, |
| // so cancellation is not safe. |
| ec = boost::asio::error::operation_not_supported; |
| } |
| #else // defined(BOOST_ASIO_ENABLE_CANCELIO) |
| else |
| { |
| // Cancellation is not supported as CancelIo may not be used. |
| ec = boost::asio::error::operation_not_supported; |
| } |
| #endif // defined(BOOST_ASIO_ENABLE_CANCELIO) |
| |
| // Cancel any operations started via the reactor. |
| if (!ec) |
| { |
| reactor* r = static_cast<reactor*>( |
| interlocked_compare_exchange_pointer( |
| reinterpret_cast<void**>(&reactor_), 0, 0)); |
| if (r) |
| r->cancel_ops(impl.socket_, impl.reactor_data_); |
| } |
| |
| return ec; |
| } |
| |
| boost::system::error_code win_iocp_socket_service_base::do_open( |
| win_iocp_socket_service_base::base_implementation_type& impl, |
| int family, int type, int protocol, boost::system::error_code& ec) |
| { |
| if (is_open(impl)) |
| { |
| ec = boost::asio::error::already_open; |
| return ec; |
| } |
| |
| socket_holder sock(socket_ops::socket(family, type, protocol, ec)); |
| if (sock.get() == invalid_socket) |
| return ec; |
| |
| HANDLE sock_as_handle = reinterpret_cast<HANDLE>(sock.get()); |
| if (iocp_service_.register_handle(sock_as_handle, ec)) |
| return ec; |
| |
| impl.socket_ = sock.release(); |
| switch (type) |
| { |
| case SOCK_STREAM: impl.state_ = socket_ops::stream_oriented; break; |
| case SOCK_DGRAM: impl.state_ = socket_ops::datagram_oriented; break; |
| default: impl.state_ = 0; break; |
| } |
| impl.cancel_token_.reset(static_cast<void*>(0), socket_ops::noop_deleter()); |
| ec = boost::system::error_code(); |
| return ec; |
| } |
| |
| boost::system::error_code win_iocp_socket_service_base::do_assign( |
| win_iocp_socket_service_base::base_implementation_type& impl, |
| int type, socket_type native_socket, boost::system::error_code& ec) |
| { |
| if (is_open(impl)) |
| { |
| ec = boost::asio::error::already_open; |
| return ec; |
| } |
| |
| HANDLE sock_as_handle = reinterpret_cast<HANDLE>(native_socket); |
| if (iocp_service_.register_handle(sock_as_handle, ec)) |
| return ec; |
| |
| impl.socket_ = native_socket; |
| switch (type) |
| { |
| case SOCK_STREAM: impl.state_ = socket_ops::stream_oriented; break; |
| case SOCK_DGRAM: impl.state_ = socket_ops::datagram_oriented; break; |
| default: impl.state_ = 0; break; |
| } |
| impl.cancel_token_.reset(static_cast<void*>(0), socket_ops::noop_deleter()); |
| ec = boost::system::error_code(); |
| return ec; |
| } |
| |
| void win_iocp_socket_service_base::start_send_op( |
| win_iocp_socket_service_base::base_implementation_type& impl, |
| WSABUF* buffers, std::size_t buffer_count, |
| socket_base::message_flags flags, bool noop, operation* op) |
| { |
| update_cancellation_thread_id(impl); |
| iocp_service_.work_started(); |
| |
| if (noop) |
| iocp_service_.on_completion(op); |
| else if (!is_open(impl)) |
| iocp_service_.on_completion(op, boost::asio::error::bad_descriptor); |
| else |
| { |
| DWORD bytes_transferred = 0; |
| int result = ::WSASend(impl.socket_, buffers, |
| static_cast<DWORD>(buffer_count), &bytes_transferred, flags, op, 0); |
| DWORD last_error = ::WSAGetLastError(); |
| if (last_error == ERROR_PORT_UNREACHABLE) |
| last_error = WSAECONNREFUSED; |
| if (result != 0 && last_error != WSA_IO_PENDING) |
| iocp_service_.on_completion(op, last_error, bytes_transferred); |
| else |
| iocp_service_.on_pending(op); |
| } |
| } |
| |
| void win_iocp_socket_service_base::start_send_to_op( |
| win_iocp_socket_service_base::base_implementation_type& impl, |
| WSABUF* buffers, std::size_t buffer_count, |
| const socket_addr_type* addr, int addrlen, |
| socket_base::message_flags flags, operation* op) |
| { |
| update_cancellation_thread_id(impl); |
| iocp_service_.work_started(); |
| |
| if (!is_open(impl)) |
| iocp_service_.on_completion(op, boost::asio::error::bad_descriptor); |
| else |
| { |
| DWORD bytes_transferred = 0; |
| int result = ::WSASendTo(impl.socket_, buffers, |
| static_cast<DWORD>(buffer_count), |
| &bytes_transferred, flags, addr, addrlen, op, 0); |
| DWORD last_error = ::WSAGetLastError(); |
| if (last_error == ERROR_PORT_UNREACHABLE) |
| last_error = WSAECONNREFUSED; |
| if (result != 0 && last_error != WSA_IO_PENDING) |
| iocp_service_.on_completion(op, last_error, bytes_transferred); |
| else |
| iocp_service_.on_pending(op); |
| } |
| } |
| |
| void win_iocp_socket_service_base::start_receive_op( |
| win_iocp_socket_service_base::base_implementation_type& impl, |
| WSABUF* buffers, std::size_t buffer_count, |
| socket_base::message_flags flags, bool noop, operation* op) |
| { |
| update_cancellation_thread_id(impl); |
| iocp_service_.work_started(); |
| |
| if (noop) |
| iocp_service_.on_completion(op); |
| else if (!is_open(impl)) |
| iocp_service_.on_completion(op, boost::asio::error::bad_descriptor); |
| else |
| { |
| DWORD bytes_transferred = 0; |
| DWORD recv_flags = flags; |
| int result = ::WSARecv(impl.socket_, buffers, |
| static_cast<DWORD>(buffer_count), |
| &bytes_transferred, &recv_flags, op, 0); |
| DWORD last_error = ::WSAGetLastError(); |
| if (last_error == ERROR_NETNAME_DELETED) |
| last_error = WSAECONNRESET; |
| else if (last_error == ERROR_PORT_UNREACHABLE) |
| last_error = WSAECONNREFUSED; |
| if (result != 0 && last_error != WSA_IO_PENDING) |
| iocp_service_.on_completion(op, last_error, bytes_transferred); |
| else |
| iocp_service_.on_pending(op); |
| } |
| } |
| |
| void win_iocp_socket_service_base::start_null_buffers_receive_op( |
| win_iocp_socket_service_base::base_implementation_type& impl, |
| socket_base::message_flags flags, reactor_op* op) |
| { |
| if ((impl.state_ & socket_ops::stream_oriented) != 0) |
| { |
| // For stream sockets on Windows, we may issue a 0-byte overlapped |
| // WSARecv to wait until there is data available on the socket. |
| ::WSABUF buf = { 0, 0 }; |
| start_receive_op(impl, &buf, 1, flags, false, op); |
| } |
| else |
| { |
| start_reactor_op(impl, |
| (flags & socket_base::message_out_of_band) |
| ? reactor::except_op : reactor::read_op, |
| op); |
| } |
| } |
| |
| void win_iocp_socket_service_base::start_receive_from_op( |
| win_iocp_socket_service_base::base_implementation_type& impl, |
| WSABUF* buffers, std::size_t buffer_count, socket_addr_type* addr, |
| socket_base::message_flags flags, int* addrlen, operation* op) |
| { |
| update_cancellation_thread_id(impl); |
| iocp_service_.work_started(); |
| |
| if (!is_open(impl)) |
| iocp_service_.on_completion(op, boost::asio::error::bad_descriptor); |
| else |
| { |
| DWORD bytes_transferred = 0; |
| DWORD recv_flags = flags; |
| int result = ::WSARecvFrom(impl.socket_, buffers, |
| static_cast<DWORD>(buffer_count), |
| &bytes_transferred, &recv_flags, addr, addrlen, op, 0); |
| DWORD last_error = ::WSAGetLastError(); |
| if (last_error == ERROR_PORT_UNREACHABLE) |
| last_error = WSAECONNREFUSED; |
| if (result != 0 && last_error != WSA_IO_PENDING) |
| iocp_service_.on_completion(op, last_error, bytes_transferred); |
| else |
| iocp_service_.on_pending(op); |
| } |
| } |
| |
| void win_iocp_socket_service_base::start_accept_op( |
| win_iocp_socket_service_base::base_implementation_type& impl, |
| bool peer_is_open, socket_holder& new_socket, int family, int type, |
| int protocol, void* output_buffer, DWORD address_length, operation* op) |
| { |
| update_cancellation_thread_id(impl); |
| iocp_service_.work_started(); |
| |
| if (!is_open(impl)) |
| iocp_service_.on_completion(op, boost::asio::error::bad_descriptor); |
| else if (peer_is_open) |
| iocp_service_.on_completion(op, boost::asio::error::already_open); |
| else |
| { |
| boost::system::error_code ec; |
| new_socket.reset(socket_ops::socket(family, type, protocol, ec)); |
| if (new_socket.get() == invalid_socket) |
| iocp_service_.on_completion(op, ec); |
| else |
| { |
| DWORD bytes_read = 0; |
| BOOL result = ::AcceptEx(impl.socket_, new_socket.get(), output_buffer, |
| 0, address_length, address_length, &bytes_read, 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); |
| } |
| } |
| } |
| |
| void win_iocp_socket_service_base::restart_accept_op( |
| socket_type s, socket_holder& new_socket, int family, int type, |
| int protocol, void* output_buffer, DWORD address_length, operation* op) |
| { |
| new_socket.reset(); |
| iocp_service_.work_started(); |
| |
| boost::system::error_code ec; |
| new_socket.reset(socket_ops::socket(family, type, protocol, ec)); |
| if (new_socket.get() == invalid_socket) |
| iocp_service_.on_completion(op, ec); |
| else |
| { |
| DWORD bytes_read = 0; |
| BOOL result = ::AcceptEx(s, new_socket.get(), output_buffer, |
| 0, address_length, address_length, &bytes_read, 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); |
| } |
| } |
| |
| void win_iocp_socket_service_base::start_reactor_op( |
| win_iocp_socket_service_base::base_implementation_type& impl, |
| int op_type, reactor_op* op) |
| { |
| reactor& r = get_reactor(); |
| update_cancellation_thread_id(impl); |
| |
| if (is_open(impl)) |
| { |
| r.start_op(op_type, impl.socket_, impl.reactor_data_, op, false); |
| return; |
| } |
| else |
| op->ec_ = boost::asio::error::bad_descriptor; |
| |
| iocp_service_.post_immediate_completion(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) |
| { |
| reactor& r = get_reactor(); |
| update_cancellation_thread_id(impl); |
| |
| if ((impl.state_ & socket_ops::non_blocking) != 0 |
| || socket_ops::set_internal_non_blocking( |
| impl.socket_, impl.state_, op->ec_)) |
| { |
| if (socket_ops::connect(impl.socket_, addr, addrlen, op->ec_) != 0) |
| { |
| if (op->ec_ == boost::asio::error::in_progress |
| || op->ec_ == boost::asio::error::would_block) |
| { |
| op->ec_ = boost::system::error_code(); |
| r.start_op(reactor::connect_op, impl.socket_, |
| impl.reactor_data_, op, false); |
| return; |
| } |
| } |
| } |
| |
| r.post_immediate_completion(op); |
| } |
| |
| void win_iocp_socket_service_base::close_for_destruction( |
| win_iocp_socket_service_base::base_implementation_type& impl) |
| { |
| if (is_open(impl)) |
| { |
| // Check if the reactor was created, in which case we need to close the |
| // socket on the reactor as well to cancel any operations that might be |
| // running there. |
| reactor* r = static_cast<reactor*>( |
| interlocked_compare_exchange_pointer( |
| reinterpret_cast<void**>(&reactor_), 0, 0)); |
| if (r) |
| r->close_descriptor(impl.socket_, impl.reactor_data_); |
| } |
| |
| boost::system::error_code ignored_ec; |
| socket_ops::close(impl.socket_, impl.state_, true, ignored_ec); |
| impl.socket_ = invalid_socket; |
| impl.state_ = 0; |
| impl.cancel_token_.reset(); |
| #if defined(BOOST_ASIO_ENABLE_CANCELIO) |
| impl.safe_cancellation_thread_id_ = 0; |
| #endif // defined(BOOST_ASIO_ENABLE_CANCELIO) |
| } |
| |
| void win_iocp_socket_service_base::update_cancellation_thread_id( |
| win_iocp_socket_service_base::base_implementation_type& impl) |
| { |
| #if defined(BOOST_ASIO_ENABLE_CANCELIO) |
| if (impl.safe_cancellation_thread_id_ == 0) |
| impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); |
| else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId()) |
| impl.safe_cancellation_thread_id_ = ~DWORD(0); |
| #else // defined(BOOST_ASIO_ENABLE_CANCELIO) |
| (void)impl; |
| #endif // defined(BOOST_ASIO_ENABLE_CANCELIO) |
| } |
| |
| reactor& win_iocp_socket_service_base::get_reactor() |
| { |
| reactor* r = static_cast<reactor*>( |
| interlocked_compare_exchange_pointer( |
| reinterpret_cast<void**>(&reactor_), 0, 0)); |
| if (!r) |
| { |
| r = &(use_service<reactor>(io_service_)); |
| interlocked_exchange_pointer(reinterpret_cast<void**>(&reactor_), r); |
| } |
| return *r; |
| } |
| |
| void* win_iocp_socket_service_base::interlocked_compare_exchange_pointer( |
| void** dest, void* exch, void* cmp) |
| { |
| #if defined(_M_IX86) |
| return reinterpret_cast<void*>(InterlockedCompareExchange( |
| reinterpret_cast<PLONG>(dest), reinterpret_cast<LONG>(exch), |
| reinterpret_cast<LONG>(cmp))); |
| #else |
| return InterlockedCompareExchangePointer(dest, exch, cmp); |
| #endif |
| } |
| |
| void* win_iocp_socket_service_base::interlocked_exchange_pointer( |
| void** dest, void* val) |
| { |
| #if defined(_M_IX86) |
| return reinterpret_cast<void*>(InterlockedExchange( |
| reinterpret_cast<PLONG>(dest), reinterpret_cast<LONG>(val))); |
| #else |
| return InterlockedExchangePointer(dest, val); |
| #endif |
| } |
| |
| } // namespace detail |
| } // namespace asio |
| } // namespace boost |
| |
| #include <boost/asio/detail/pop_options.hpp> |
| |
| #endif // defined(BOOST_ASIO_HAS_IOCP) |
| |
| #endif // BOOST_ASIO_DETAIL_IMPL_WIN_IOCP_SOCKET_SERVICE_BASE_IPP |