1// 2// detail/impl/win_iocp_socket_service_base.ipp 3// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4// 5// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6// 7// Distributed under the Boost Software License, Version 1.0. (See accompanying 8// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9// 10 11#ifndef BOOST_ASIO_DETAIL_IMPL_WIN_IOCP_SOCKET_SERVICE_BASE_IPP 12#define BOOST_ASIO_DETAIL_IMPL_WIN_IOCP_SOCKET_SERVICE_BASE_IPP 13 14#if defined(_MSC_VER) && (_MSC_VER >= 1200) 15# pragma once 16#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 17 18#include <boost/asio/detail/config.hpp> 19 20#if defined(BOOST_ASIO_HAS_IOCP) 21 22#include <boost/asio/detail/win_iocp_socket_service_base.hpp> 23 24#include <boost/asio/detail/push_options.hpp> 25 26namespace boost { 27namespace asio { 28namespace detail { 29 30win_iocp_socket_service_base::win_iocp_socket_service_base( 31 execution_context& context) 32 : context_(context), 33 iocp_service_(use_service<win_iocp_io_context>(context)), 34 reactor_(0), 35 connect_ex_(0), 36 nt_set_info_(0), 37 mutex_(), 38 impl_list_(0) 39{ 40} 41 42void win_iocp_socket_service_base::base_shutdown() 43{ 44 // Close all implementations, causing all operations to complete. 45 boost::asio::detail::mutex::scoped_lock lock(mutex_); 46 base_implementation_type* impl = impl_list_; 47 while (impl) 48 { 49 close_for_destruction(*impl); 50 impl = impl->next_; 51 } 52} 53 54void win_iocp_socket_service_base::construct( 55 win_iocp_socket_service_base::base_implementation_type& impl) 56{ 57 impl.socket_ = invalid_socket; 58 impl.state_ = 0; 59 impl.cancel_token_.reset(); 60#if defined(BOOST_ASIO_ENABLE_CANCELIO) 61 impl.safe_cancellation_thread_id_ = 0; 62#endif // defined(BOOST_ASIO_ENABLE_CANCELIO) 63 64 // Insert implementation into linked list of all implementations. 65 boost::asio::detail::mutex::scoped_lock lock(mutex_); 66 impl.next_ = impl_list_; 67 impl.prev_ = 0; 68 if (impl_list_) 69 impl_list_->prev_ = &impl; 70 impl_list_ = &impl; 71} 72 73void win_iocp_socket_service_base::base_move_construct( 74 win_iocp_socket_service_base::base_implementation_type& impl, 75 win_iocp_socket_service_base::base_implementation_type& other_impl) 76 BOOST_ASIO_NOEXCEPT 77{ 78 impl.socket_ = other_impl.socket_; 79 other_impl.socket_ = invalid_socket; 80 81 impl.state_ = other_impl.state_; 82 other_impl.state_ = 0; 83 84 impl.cancel_token_ = other_impl.cancel_token_; 85 other_impl.cancel_token_.reset(); 86 87#if defined(BOOST_ASIO_ENABLE_CANCELIO) 88 impl.safe_cancellation_thread_id_ = other_impl.safe_cancellation_thread_id_; 89 other_impl.safe_cancellation_thread_id_ = 0; 90#endif // defined(BOOST_ASIO_ENABLE_CANCELIO) 91 92 // Insert implementation into linked list of all implementations. 93 boost::asio::detail::mutex::scoped_lock lock(mutex_); 94 impl.next_ = impl_list_; 95 impl.prev_ = 0; 96 if (impl_list_) 97 impl_list_->prev_ = &impl; 98 impl_list_ = &impl; 99} 100 101void win_iocp_socket_service_base::base_move_assign( 102 win_iocp_socket_service_base::base_implementation_type& impl, 103 win_iocp_socket_service_base& other_service, 104 win_iocp_socket_service_base::base_implementation_type& other_impl) 105{ 106 close_for_destruction(impl); 107 108 if (this != &other_service) 109 { 110 // Remove implementation from linked list of all implementations. 111 boost::asio::detail::mutex::scoped_lock lock(mutex_); 112 if (impl_list_ == &impl) 113 impl_list_ = impl.next_; 114 if (impl.prev_) 115 impl.prev_->next_ = impl.next_; 116 if (impl.next_) 117 impl.next_->prev_= impl.prev_; 118 impl.next_ = 0; 119 impl.prev_ = 0; 120 } 121 122 impl.socket_ = other_impl.socket_; 123 other_impl.socket_ = invalid_socket; 124 125 impl.state_ = other_impl.state_; 126 other_impl.state_ = 0; 127 128 impl.cancel_token_ = other_impl.cancel_token_; 129 other_impl.cancel_token_.reset(); 130 131#if defined(BOOST_ASIO_ENABLE_CANCELIO) 132 impl.safe_cancellation_thread_id_ = other_impl.safe_cancellation_thread_id_; 133 other_impl.safe_cancellation_thread_id_ = 0; 134#endif // defined(BOOST_ASIO_ENABLE_CANCELIO) 135 136 if (this != &other_service) 137 { 138 // Insert implementation into linked list of all implementations. 139 boost::asio::detail::mutex::scoped_lock lock(other_service.mutex_); 140 impl.next_ = other_service.impl_list_; 141 impl.prev_ = 0; 142 if (other_service.impl_list_) 143 other_service.impl_list_->prev_ = &impl; 144 other_service.impl_list_ = &impl; 145 } 146} 147 148void win_iocp_socket_service_base::destroy( 149 win_iocp_socket_service_base::base_implementation_type& impl) 150{ 151 close_for_destruction(impl); 152 153 // Remove implementation from linked list of all implementations. 154 boost::asio::detail::mutex::scoped_lock lock(mutex_); 155 if (impl_list_ == &impl) 156 impl_list_ = impl.next_; 157 if (impl.prev_) 158 impl.prev_->next_ = impl.next_; 159 if (impl.next_) 160 impl.next_->prev_= impl.prev_; 161 impl.next_ = 0; 162 impl.prev_ = 0; 163} 164 165boost::system::error_code win_iocp_socket_service_base::close( 166 win_iocp_socket_service_base::base_implementation_type& impl, 167 boost::system::error_code& ec) 168{ 169 if (is_open(impl)) 170 { 171 BOOST_ASIO_HANDLER_OPERATION((iocp_service_.context(), 172 "socket", &impl, impl.socket_, "close")); 173 174 // Check if the reactor was created, in which case we need to close the 175 // socket on the reactor as well to cancel any operations that might be 176 // running there. 177 select_reactor* r = static_cast<select_reactor*>( 178 interlocked_compare_exchange_pointer( 179 reinterpret_cast<void**>(&reactor_), 0, 0)); 180 if (r) 181 r->deregister_descriptor(impl.socket_, impl.reactor_data_, true); 182 183 socket_ops::close(impl.socket_, impl.state_, false, ec); 184 185 if (r) 186 r->cleanup_descriptor_data(impl.reactor_data_); 187 } 188 else 189 { 190 ec = boost::system::error_code(); 191 } 192 193 impl.socket_ = invalid_socket; 194 impl.state_ = 0; 195 impl.cancel_token_.reset(); 196#if defined(BOOST_ASIO_ENABLE_CANCELIO) 197 impl.safe_cancellation_thread_id_ = 0; 198#endif // defined(BOOST_ASIO_ENABLE_CANCELIO) 199 200 return ec; 201} 202 203socket_type win_iocp_socket_service_base::release( 204 win_iocp_socket_service_base::base_implementation_type& impl, 205 boost::system::error_code& ec) 206{ 207 if (!is_open(impl)) 208 return invalid_socket; 209 210 cancel(impl, ec); 211 if (ec) 212 return invalid_socket; 213 214 nt_set_info_fn fn = get_nt_set_info(); 215 if (fn == 0) 216 { 217 ec = boost::asio::error::operation_not_supported; 218 return invalid_socket; 219 } 220 221 HANDLE sock_as_handle = reinterpret_cast<HANDLE>(impl.socket_); 222 ULONG_PTR iosb[2] = { 0, 0 }; 223 void* info[2] = { 0, 0 }; 224 if (fn(sock_as_handle, iosb, &info, sizeof(info), 225 61 /* FileReplaceCompletionInformation */)) 226 { 227 ec = boost::asio::error::operation_not_supported; 228 return invalid_socket; 229 } 230 231 socket_type tmp = impl.socket_; 232 impl.socket_ = invalid_socket; 233 return tmp; 234} 235 236boost::system::error_code win_iocp_socket_service_base::cancel( 237 win_iocp_socket_service_base::base_implementation_type& impl, 238 boost::system::error_code& ec) 239{ 240 if (!is_open(impl)) 241 { 242 ec = boost::asio::error::bad_descriptor; 243 return ec; 244 } 245 246 BOOST_ASIO_HANDLER_OPERATION((iocp_service_.context(), 247 "socket", &impl, impl.socket_, "cancel")); 248 249 if (FARPROC cancel_io_ex_ptr = ::GetProcAddress( 250 ::GetModuleHandleA("KERNEL32"), "CancelIoEx")) 251 { 252 // The version of Windows supports cancellation from any thread. 253 typedef BOOL (WINAPI* cancel_io_ex_t)(HANDLE, LPOVERLAPPED); 254 cancel_io_ex_t cancel_io_ex = reinterpret_cast<cancel_io_ex_t>( 255 reinterpret_cast<void*>(cancel_io_ex_ptr)); 256 socket_type sock = impl.socket_; 257 HANDLE sock_as_handle = reinterpret_cast<HANDLE>(sock); 258 if (!cancel_io_ex(sock_as_handle, 0)) 259 { 260 DWORD last_error = ::GetLastError(); 261 if (last_error == ERROR_NOT_FOUND) 262 { 263 // ERROR_NOT_FOUND means that there were no operations to be 264 // cancelled. We swallow this error to match the behaviour on other 265 // platforms. 266 ec = boost::system::error_code(); 267 } 268 else 269 { 270 ec = boost::system::error_code(last_error, 271 boost::asio::error::get_system_category()); 272 } 273 } 274 else 275 { 276 ec = boost::system::error_code(); 277 } 278 } 279#if defined(BOOST_ASIO_ENABLE_CANCELIO) 280 else if (impl.safe_cancellation_thread_id_ == 0) 281 { 282 // No operations have been started, so there's nothing to cancel. 283 ec = boost::system::error_code(); 284 } 285 else if (impl.safe_cancellation_thread_id_ == ::GetCurrentThreadId()) 286 { 287 // Asynchronous operations have been started from the current thread only, 288 // so it is safe to try to cancel them using CancelIo. 289 socket_type sock = impl.socket_; 290 HANDLE sock_as_handle = reinterpret_cast<HANDLE>(sock); 291 if (!::CancelIo(sock_as_handle)) 292 { 293 DWORD last_error = ::GetLastError(); 294 ec = boost::system::error_code(last_error, 295 boost::asio::error::get_system_category()); 296 } 297 else 298 { 299 ec = boost::system::error_code(); 300 } 301 } 302 else 303 { 304 // Asynchronous operations have been started from more than one thread, 305 // so cancellation is not safe. 306 ec = boost::asio::error::operation_not_supported; 307 } 308#else // defined(BOOST_ASIO_ENABLE_CANCELIO) 309 else 310 { 311 // Cancellation is not supported as CancelIo may not be used. 312 ec = boost::asio::error::operation_not_supported; 313 } 314#endif // defined(BOOST_ASIO_ENABLE_CANCELIO) 315 316 // Cancel any operations started via the reactor. 317 if (!ec) 318 { 319 select_reactor* r = static_cast<select_reactor*>( 320 interlocked_compare_exchange_pointer( 321 reinterpret_cast<void**>(&reactor_), 0, 0)); 322 if (r) 323 r->cancel_ops(impl.socket_, impl.reactor_data_); 324 } 325 326 return ec; 327} 328 329boost::system::error_code win_iocp_socket_service_base::do_open( 330 win_iocp_socket_service_base::base_implementation_type& impl, 331 int family, int type, int protocol, boost::system::error_code& ec) 332{ 333 if (is_open(impl)) 334 { 335 ec = boost::asio::error::already_open; 336 return ec; 337 } 338 339 socket_holder sock(socket_ops::socket(family, type, protocol, ec)); 340 if (sock.get() == invalid_socket) 341 return ec; 342 343 HANDLE sock_as_handle = reinterpret_cast<HANDLE>(sock.get()); 344 if (iocp_service_.register_handle(sock_as_handle, ec)) 345 return ec; 346 347 impl.socket_ = sock.release(); 348 switch (type) 349 { 350 case SOCK_STREAM: impl.state_ = socket_ops::stream_oriented; break; 351 case SOCK_DGRAM: impl.state_ = socket_ops::datagram_oriented; break; 352 default: impl.state_ = 0; break; 353 } 354 impl.cancel_token_.reset(static_cast<void*>(0), socket_ops::noop_deleter()); 355 ec = boost::system::error_code(); 356 return ec; 357} 358 359boost::system::error_code win_iocp_socket_service_base::do_assign( 360 win_iocp_socket_service_base::base_implementation_type& impl, 361 int type, socket_type native_socket, boost::system::error_code& ec) 362{ 363 if (is_open(impl)) 364 { 365 ec = boost::asio::error::already_open; 366 return ec; 367 } 368 369 HANDLE sock_as_handle = reinterpret_cast<HANDLE>(native_socket); 370 if (iocp_service_.register_handle(sock_as_handle, ec)) 371 return ec; 372 373 impl.socket_ = native_socket; 374 switch (type) 375 { 376 case SOCK_STREAM: impl.state_ = socket_ops::stream_oriented; break; 377 case SOCK_DGRAM: impl.state_ = socket_ops::datagram_oriented; break; 378 default: impl.state_ = 0; break; 379 } 380 impl.cancel_token_.reset(static_cast<void*>(0), socket_ops::noop_deleter()); 381 ec = boost::system::error_code(); 382 return ec; 383} 384 385void win_iocp_socket_service_base::start_send_op( 386 win_iocp_socket_service_base::base_implementation_type& impl, 387 WSABUF* buffers, std::size_t buffer_count, 388 socket_base::message_flags flags, bool noop, operation* op) 389{ 390 update_cancellation_thread_id(impl); 391 iocp_service_.work_started(); 392 393 if (noop) 394 iocp_service_.on_completion(op); 395 else if (!is_open(impl)) 396 iocp_service_.on_completion(op, boost::asio::error::bad_descriptor); 397 else 398 { 399 DWORD bytes_transferred = 0; 400 int result = ::WSASend(impl.socket_, buffers, 401 static_cast<DWORD>(buffer_count), &bytes_transferred, flags, op, 0); 402 DWORD last_error = ::WSAGetLastError(); 403 if (last_error == ERROR_PORT_UNREACHABLE) 404 last_error = WSAECONNREFUSED; 405 if (result != 0 && last_error != WSA_IO_PENDING) 406 iocp_service_.on_completion(op, last_error, bytes_transferred); 407 else 408 iocp_service_.on_pending(op); 409 } 410} 411 412void win_iocp_socket_service_base::start_send_to_op( 413 win_iocp_socket_service_base::base_implementation_type& impl, 414 WSABUF* buffers, std::size_t buffer_count, 415 const socket_addr_type* addr, int addrlen, 416 socket_base::message_flags flags, operation* op) 417{ 418 update_cancellation_thread_id(impl); 419 iocp_service_.work_started(); 420 421 if (!is_open(impl)) 422 iocp_service_.on_completion(op, boost::asio::error::bad_descriptor); 423 else 424 { 425 DWORD bytes_transferred = 0; 426 int result = ::WSASendTo(impl.socket_, buffers, 427 static_cast<DWORD>(buffer_count), 428 &bytes_transferred, flags, addr, addrlen, op, 0); 429 DWORD last_error = ::WSAGetLastError(); 430 if (last_error == ERROR_PORT_UNREACHABLE) 431 last_error = WSAECONNREFUSED; 432 if (result != 0 && last_error != WSA_IO_PENDING) 433 iocp_service_.on_completion(op, last_error, bytes_transferred); 434 else 435 iocp_service_.on_pending(op); 436 } 437} 438 439void win_iocp_socket_service_base::start_receive_op( 440 win_iocp_socket_service_base::base_implementation_type& impl, 441 WSABUF* buffers, std::size_t buffer_count, 442 socket_base::message_flags flags, bool noop, operation* op) 443{ 444 update_cancellation_thread_id(impl); 445 iocp_service_.work_started(); 446 447 if (noop) 448 iocp_service_.on_completion(op); 449 else if (!is_open(impl)) 450 iocp_service_.on_completion(op, boost::asio::error::bad_descriptor); 451 else 452 { 453 DWORD bytes_transferred = 0; 454 DWORD recv_flags = flags; 455 int result = ::WSARecv(impl.socket_, buffers, 456 static_cast<DWORD>(buffer_count), 457 &bytes_transferred, &recv_flags, op, 0); 458 DWORD last_error = ::WSAGetLastError(); 459 if (last_error == ERROR_NETNAME_DELETED) 460 last_error = WSAECONNRESET; 461 else if (last_error == ERROR_PORT_UNREACHABLE) 462 last_error = WSAECONNREFUSED; 463 if (result != 0 && last_error != WSA_IO_PENDING) 464 iocp_service_.on_completion(op, last_error, bytes_transferred); 465 else 466 iocp_service_.on_pending(op); 467 } 468} 469 470void win_iocp_socket_service_base::start_null_buffers_receive_op( 471 win_iocp_socket_service_base::base_implementation_type& impl, 472 socket_base::message_flags flags, reactor_op* op) 473{ 474 if ((impl.state_ & socket_ops::stream_oriented) != 0) 475 { 476 // For stream sockets on Windows, we may issue a 0-byte overlapped 477 // WSARecv to wait until there is data available on the socket. 478 ::WSABUF buf = { 0, 0 }; 479 start_receive_op(impl, &buf, 1, flags, false, op); 480 } 481 else 482 { 483 start_reactor_op(impl, 484 (flags & socket_base::message_out_of_band) 485 ? select_reactor::except_op : select_reactor::read_op, 486 op); 487 } 488} 489 490void win_iocp_socket_service_base::start_receive_from_op( 491 win_iocp_socket_service_base::base_implementation_type& impl, 492 WSABUF* buffers, std::size_t buffer_count, socket_addr_type* addr, 493 socket_base::message_flags flags, int* addrlen, operation* op) 494{ 495 update_cancellation_thread_id(impl); 496 iocp_service_.work_started(); 497 498 if (!is_open(impl)) 499 iocp_service_.on_completion(op, boost::asio::error::bad_descriptor); 500 else 501 { 502 DWORD bytes_transferred = 0; 503 DWORD recv_flags = flags; 504 int result = ::WSARecvFrom(impl.socket_, buffers, 505 static_cast<DWORD>(buffer_count), 506 &bytes_transferred, &recv_flags, addr, addrlen, op, 0); 507 DWORD last_error = ::WSAGetLastError(); 508 if (last_error == ERROR_PORT_UNREACHABLE) 509 last_error = WSAECONNREFUSED; 510 if (result != 0 && last_error != WSA_IO_PENDING) 511 iocp_service_.on_completion(op, last_error, bytes_transferred); 512 else 513 iocp_service_.on_pending(op); 514 } 515} 516 517void win_iocp_socket_service_base::start_accept_op( 518 win_iocp_socket_service_base::base_implementation_type& impl, 519 bool peer_is_open, socket_holder& new_socket, int family, int type, 520 int protocol, void* output_buffer, DWORD address_length, operation* op) 521{ 522 update_cancellation_thread_id(impl); 523 iocp_service_.work_started(); 524 525 if (!is_open(impl)) 526 iocp_service_.on_completion(op, boost::asio::error::bad_descriptor); 527 else if (peer_is_open) 528 iocp_service_.on_completion(op, boost::asio::error::already_open); 529 else 530 { 531 boost::system::error_code ec; 532 new_socket.reset(socket_ops::socket(family, type, protocol, ec)); 533 if (new_socket.get() == invalid_socket) 534 iocp_service_.on_completion(op, ec); 535 else 536 { 537 DWORD bytes_read = 0; 538 BOOL result = ::AcceptEx(impl.socket_, new_socket.get(), output_buffer, 539 0, address_length, address_length, &bytes_read, op); 540 DWORD last_error = ::WSAGetLastError(); 541 if (!result && last_error != WSA_IO_PENDING) 542 iocp_service_.on_completion(op, last_error); 543 else 544 iocp_service_.on_pending(op); 545 } 546 } 547} 548 549void win_iocp_socket_service_base::restart_accept_op( 550 socket_type s, socket_holder& new_socket, int family, int type, 551 int protocol, void* output_buffer, DWORD address_length, operation* op) 552{ 553 new_socket.reset(); 554 iocp_service_.work_started(); 555 556 boost::system::error_code ec; 557 new_socket.reset(socket_ops::socket(family, type, protocol, ec)); 558 if (new_socket.get() == invalid_socket) 559 iocp_service_.on_completion(op, ec); 560 else 561 { 562 DWORD bytes_read = 0; 563 BOOL result = ::AcceptEx(s, new_socket.get(), output_buffer, 564 0, address_length, address_length, &bytes_read, op); 565 DWORD last_error = ::WSAGetLastError(); 566 if (!result && last_error != WSA_IO_PENDING) 567 iocp_service_.on_completion(op, last_error); 568 else 569 iocp_service_.on_pending(op); 570 } 571} 572 573void win_iocp_socket_service_base::start_reactor_op( 574 win_iocp_socket_service_base::base_implementation_type& impl, 575 int op_type, reactor_op* op) 576{ 577 select_reactor& r = get_reactor(); 578 update_cancellation_thread_id(impl); 579 580 if (is_open(impl)) 581 { 582 r.start_op(op_type, impl.socket_, impl.reactor_data_, op, false, false); 583 return; 584 } 585 else 586 op->ec_ = boost::asio::error::bad_descriptor; 587 588 iocp_service_.post_immediate_completion(op, false); 589} 590 591void win_iocp_socket_service_base::start_connect_op( 592 win_iocp_socket_service_base::base_implementation_type& impl, 593 int family, int type, const socket_addr_type* addr, 594 std::size_t addrlen, win_iocp_socket_connect_op_base* op) 595{ 596 // If ConnectEx is available, use that. 597 if (family == BOOST_ASIO_OS_DEF(AF_INET) 598 || family == BOOST_ASIO_OS_DEF(AF_INET6)) 599 { 600 if (connect_ex_fn connect_ex = get_connect_ex(impl, type)) 601 { 602 union address_union 603 { 604 socket_addr_type base; 605 sockaddr_in4_type v4; 606 sockaddr_in6_type v6; 607 } a; 608 609 using namespace std; // For memset. 610 memset(&a, 0, sizeof(a)); 611 a.base.sa_family = family; 612 613 socket_ops::bind(impl.socket_, &a.base, 614 family == BOOST_ASIO_OS_DEF(AF_INET) 615 ? sizeof(a.v4) : sizeof(a.v6), op->ec_); 616 if (op->ec_ && op->ec_ != boost::asio::error::invalid_argument) 617 { 618 iocp_service_.post_immediate_completion(op, false); 619 return; 620 } 621 622 op->connect_ex_ = true; 623 update_cancellation_thread_id(impl); 624 iocp_service_.work_started(); 625 626 BOOL result = connect_ex(impl.socket_, 627 addr, static_cast<int>(addrlen), 0, 0, 0, op); 628 DWORD last_error = ::WSAGetLastError(); 629 if (!result && last_error != WSA_IO_PENDING) 630 iocp_service_.on_completion(op, last_error); 631 else 632 iocp_service_.on_pending(op); 633 return; 634 } 635 } 636 637 // Otherwise, fall back to a reactor-based implementation. 638 select_reactor& r = get_reactor(); 639 update_cancellation_thread_id(impl); 640 641 if ((impl.state_ & socket_ops::non_blocking) != 0 642 || socket_ops::set_internal_non_blocking( 643 impl.socket_, impl.state_, true, op->ec_)) 644 { 645 if (socket_ops::connect(impl.socket_, addr, addrlen, op->ec_) != 0) 646 { 647 if (op->ec_ == boost::asio::error::in_progress 648 || op->ec_ == boost::asio::error::would_block) 649 { 650 op->ec_ = boost::system::error_code(); 651 r.start_op(select_reactor::connect_op, impl.socket_, 652 impl.reactor_data_, op, false, false); 653 return; 654 } 655 } 656 } 657 658 r.post_immediate_completion(op, false); 659} 660 661void win_iocp_socket_service_base::close_for_destruction( 662 win_iocp_socket_service_base::base_implementation_type& impl) 663{ 664 if (is_open(impl)) 665 { 666 BOOST_ASIO_HANDLER_OPERATION((iocp_service_.context(), 667 "socket", &impl, impl.socket_, "close")); 668 669 // Check if the reactor was created, in which case we need to close the 670 // socket on the reactor as well to cancel any operations that might be 671 // running there. 672 select_reactor* r = static_cast<select_reactor*>( 673 interlocked_compare_exchange_pointer( 674 reinterpret_cast<void**>(&reactor_), 0, 0)); 675 if (r) 676 r->deregister_descriptor(impl.socket_, impl.reactor_data_, true); 677 678 boost::system::error_code ignored_ec; 679 socket_ops::close(impl.socket_, impl.state_, true, ignored_ec); 680 681 if (r) 682 r->cleanup_descriptor_data(impl.reactor_data_); 683 } 684 685 impl.socket_ = invalid_socket; 686 impl.state_ = 0; 687 impl.cancel_token_.reset(); 688#if defined(BOOST_ASIO_ENABLE_CANCELIO) 689 impl.safe_cancellation_thread_id_ = 0; 690#endif // defined(BOOST_ASIO_ENABLE_CANCELIO) 691} 692 693void win_iocp_socket_service_base::update_cancellation_thread_id( 694 win_iocp_socket_service_base::base_implementation_type& impl) 695{ 696#if defined(BOOST_ASIO_ENABLE_CANCELIO) 697 if (impl.safe_cancellation_thread_id_ == 0) 698 impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); 699 else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId()) 700 impl.safe_cancellation_thread_id_ = ~DWORD(0); 701#else // defined(BOOST_ASIO_ENABLE_CANCELIO) 702 (void)impl; 703#endif // defined(BOOST_ASIO_ENABLE_CANCELIO) 704} 705 706select_reactor& win_iocp_socket_service_base::get_reactor() 707{ 708 select_reactor* r = static_cast<select_reactor*>( 709 interlocked_compare_exchange_pointer( 710 reinterpret_cast<void**>(&reactor_), 0, 0)); 711 if (!r) 712 { 713 r = &(use_service<select_reactor>(context_)); 714 interlocked_exchange_pointer(reinterpret_cast<void**>(&reactor_), r); 715 } 716 return *r; 717} 718 719win_iocp_socket_service_base::connect_ex_fn 720win_iocp_socket_service_base::get_connect_ex( 721 win_iocp_socket_service_base::base_implementation_type& impl, int type) 722{ 723#if defined(BOOST_ASIO_DISABLE_CONNECTEX) 724 (void)impl; 725 (void)type; 726 return 0; 727#else // defined(BOOST_ASIO_DISABLE_CONNECTEX) 728 if (type != BOOST_ASIO_OS_DEF(SOCK_STREAM) 729 && type != BOOST_ASIO_OS_DEF(SOCK_SEQPACKET)) 730 return 0; 731 732 void* ptr = interlocked_compare_exchange_pointer(&connect_ex_, 0, 0); 733 if (!ptr) 734 { 735 GUID guid = { 0x25a207b9, 0xddf3, 0x4660, 736 { 0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e } }; 737 738 DWORD bytes = 0; 739 if (::WSAIoctl(impl.socket_, SIO_GET_EXTENSION_FUNCTION_POINTER, 740 &guid, sizeof(guid), &ptr, sizeof(ptr), &bytes, 0, 0) != 0) 741 { 742 // Set connect_ex_ to a special value to indicate that ConnectEx is 743 // unavailable. That way we won't bother trying to look it up again. 744 ptr = this; 745 } 746 747 interlocked_exchange_pointer(&connect_ex_, ptr); 748 } 749 750 return reinterpret_cast<connect_ex_fn>(ptr == this ? 0 : ptr); 751#endif // defined(BOOST_ASIO_DISABLE_CONNECTEX) 752} 753 754win_iocp_socket_service_base::nt_set_info_fn 755win_iocp_socket_service_base::get_nt_set_info() 756{ 757 void* ptr = interlocked_compare_exchange_pointer(&nt_set_info_, 0, 0); 758 if (!ptr) 759 { 760 if (HMODULE h = ::GetModuleHandleA("NTDLL.DLL")) 761 ptr = reinterpret_cast<void*>(GetProcAddress(h, "NtSetInformationFile")); 762 763 // On failure, set nt_set_info_ to a special value to indicate that the 764 // NtSetInformationFile function is unavailable. That way we won't bother 765 // trying to look it up again. 766 interlocked_exchange_pointer(&nt_set_info_, ptr ? ptr : this); 767 } 768 769 return reinterpret_cast<nt_set_info_fn>(ptr == this ? 0 : ptr); 770} 771 772void* win_iocp_socket_service_base::interlocked_compare_exchange_pointer( 773 void** dest, void* exch, void* cmp) 774{ 775#if defined(_M_IX86) 776 return reinterpret_cast<void*>(InterlockedCompareExchange( 777 reinterpret_cast<PLONG>(dest), reinterpret_cast<LONG>(exch), 778 reinterpret_cast<LONG>(cmp))); 779#else 780 return InterlockedCompareExchangePointer(dest, exch, cmp); 781#endif 782} 783 784void* win_iocp_socket_service_base::interlocked_exchange_pointer( 785 void** dest, void* val) 786{ 787#if defined(_M_IX86) 788 return reinterpret_cast<void*>(InterlockedExchange( 789 reinterpret_cast<PLONG>(dest), reinterpret_cast<LONG>(val))); 790#else 791 return InterlockedExchangePointer(dest, val); 792#endif 793} 794 795} // namespace detail 796} // namespace asio 797} // namespace boost 798 799#include <boost/asio/detail/pop_options.hpp> 800 801#endif // defined(BOOST_ASIO_HAS_IOCP) 802 803#endif // BOOST_ASIO_DETAIL_IMPL_WIN_IOCP_SOCKET_SERVICE_BASE_IPP 804