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