Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:28:35

0001 //
0002 // detail/impl/win_iocp_handle_service.ipp
0003 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0004 //
0005 // Copyright (c) 2003-2023 Christopher M. Kohlhoff (chris at kohlhoff dot com)
0006 // Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com)
0007 //
0008 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0009 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0010 //
0011 
0012 #ifndef BOOST_ASIO_DETAIL_IMPL_WIN_IOCP_HANDLE_SERVICE_IPP
0013 #define BOOST_ASIO_DETAIL_IMPL_WIN_IOCP_HANDLE_SERVICE_IPP
0014 
0015 #if defined(_MSC_VER) && (_MSC_VER >= 1200)
0016 # pragma once
0017 #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
0018 
0019 #include <boost/asio/detail/config.hpp>
0020 
0021 #if defined(BOOST_ASIO_HAS_IOCP)
0022 
0023 #include <boost/asio/detail/win_iocp_handle_service.hpp>
0024 
0025 #include <boost/asio/detail/push_options.hpp>
0026 
0027 namespace boost {
0028 namespace asio {
0029 namespace detail {
0030 
0031 class win_iocp_handle_service::overlapped_wrapper
0032   : public OVERLAPPED
0033 {
0034 public:
0035   explicit overlapped_wrapper(boost::system::error_code& ec)
0036   {
0037     Internal = 0;
0038     InternalHigh = 0;
0039     Offset = 0;
0040     OffsetHigh = 0;
0041 
0042     // Create a non-signalled manual-reset event, for GetOverlappedResult.
0043     hEvent = ::CreateEventW(0, TRUE, FALSE, 0);
0044     if (hEvent)
0045     {
0046       // As documented in GetQueuedCompletionStatus, setting the low order
0047       // bit of this event prevents our synchronous writes from being treated
0048       // as completion port events.
0049       DWORD_PTR tmp = reinterpret_cast<DWORD_PTR>(hEvent);
0050       hEvent = reinterpret_cast<HANDLE>(tmp | 1);
0051     }
0052     else
0053     {
0054       DWORD last_error = ::GetLastError();
0055       ec = boost::system::error_code(last_error,
0056           boost::asio::error::get_system_category());
0057     }
0058   }
0059 
0060   ~overlapped_wrapper()
0061   {
0062     if (hEvent)
0063     {
0064       ::CloseHandle(hEvent);
0065     }
0066   }
0067 };
0068 
0069 win_iocp_handle_service::win_iocp_handle_service(execution_context& context)
0070   : execution_context_service_base<win_iocp_handle_service>(context),
0071     iocp_service_(boost::asio::use_service<win_iocp_io_context>(context)),
0072     nt_set_info_(0),
0073     mutex_(),
0074     impl_list_(0)
0075 {
0076 }
0077 
0078 void win_iocp_handle_service::shutdown()
0079 {
0080   // Close all implementations, causing all operations to complete.
0081   boost::asio::detail::mutex::scoped_lock lock(mutex_);
0082   implementation_type* impl = impl_list_;
0083   while (impl)
0084   {
0085     close_for_destruction(*impl);
0086     impl = impl->next_;
0087   }
0088 }
0089 
0090 void win_iocp_handle_service::construct(
0091     win_iocp_handle_service::implementation_type& impl)
0092 {
0093   impl.handle_ = INVALID_HANDLE_VALUE;
0094   impl.safe_cancellation_thread_id_ = 0;
0095 
0096   // Insert implementation into linked list of all implementations.
0097   boost::asio::detail::mutex::scoped_lock lock(mutex_);
0098   impl.next_ = impl_list_;
0099   impl.prev_ = 0;
0100   if (impl_list_)
0101     impl_list_->prev_ = &impl;
0102   impl_list_ = &impl;
0103 }
0104 
0105 void win_iocp_handle_service::move_construct(
0106     win_iocp_handle_service::implementation_type& impl,
0107     win_iocp_handle_service::implementation_type& other_impl)
0108 {
0109   impl.handle_ = other_impl.handle_;
0110   other_impl.handle_ = INVALID_HANDLE_VALUE;
0111 
0112   impl.safe_cancellation_thread_id_ = other_impl.safe_cancellation_thread_id_;
0113   other_impl.safe_cancellation_thread_id_ = 0;
0114 
0115   // Insert implementation into linked list of all implementations.
0116   boost::asio::detail::mutex::scoped_lock lock(mutex_);
0117   impl.next_ = impl_list_;
0118   impl.prev_ = 0;
0119   if (impl_list_)
0120     impl_list_->prev_ = &impl;
0121   impl_list_ = &impl;
0122 }
0123 
0124 void win_iocp_handle_service::move_assign(
0125     win_iocp_handle_service::implementation_type& impl,
0126     win_iocp_handle_service& other_service,
0127     win_iocp_handle_service::implementation_type& other_impl)
0128 {
0129   close_for_destruction(impl);
0130 
0131   if (this != &other_service)
0132   {
0133     // Remove implementation from linked list of all implementations.
0134     boost::asio::detail::mutex::scoped_lock lock(mutex_);
0135     if (impl_list_ == &impl)
0136       impl_list_ = impl.next_;
0137     if (impl.prev_)
0138       impl.prev_->next_ = impl.next_;
0139     if (impl.next_)
0140       impl.next_->prev_= impl.prev_;
0141     impl.next_ = 0;
0142     impl.prev_ = 0;
0143   }
0144 
0145   impl.handle_ = other_impl.handle_;
0146   other_impl.handle_ = INVALID_HANDLE_VALUE;
0147 
0148   impl.safe_cancellation_thread_id_ = other_impl.safe_cancellation_thread_id_;
0149   other_impl.safe_cancellation_thread_id_ = 0;
0150 
0151   if (this != &other_service)
0152   {
0153     // Insert implementation into linked list of all implementations.
0154     boost::asio::detail::mutex::scoped_lock lock(other_service.mutex_);
0155     impl.next_ = other_service.impl_list_;
0156     impl.prev_ = 0;
0157     if (other_service.impl_list_)
0158       other_service.impl_list_->prev_ = &impl;
0159     other_service.impl_list_ = &impl;
0160   }
0161 }
0162 
0163 void win_iocp_handle_service::destroy(
0164     win_iocp_handle_service::implementation_type& impl)
0165 {
0166   close_for_destruction(impl);
0167   
0168   // Remove implementation from linked list of all implementations.
0169   boost::asio::detail::mutex::scoped_lock lock(mutex_);
0170   if (impl_list_ == &impl)
0171     impl_list_ = impl.next_;
0172   if (impl.prev_)
0173     impl.prev_->next_ = impl.next_;
0174   if (impl.next_)
0175     impl.next_->prev_= impl.prev_;
0176   impl.next_ = 0;
0177   impl.prev_ = 0;
0178 }
0179 
0180 boost::system::error_code win_iocp_handle_service::assign(
0181     win_iocp_handle_service::implementation_type& impl,
0182     const native_handle_type& handle, boost::system::error_code& ec)
0183 {
0184   if (is_open(impl))
0185   {
0186     ec = boost::asio::error::already_open;
0187     BOOST_ASIO_ERROR_LOCATION(ec);
0188     return ec;
0189   }
0190 
0191   if (iocp_service_.register_handle(handle, ec))
0192   {
0193     BOOST_ASIO_ERROR_LOCATION(ec);
0194     return ec;
0195   }
0196 
0197   impl.handle_ = handle;
0198   ec = boost::system::error_code();
0199   return ec;
0200 }
0201 
0202 boost::system::error_code win_iocp_handle_service::close(
0203     win_iocp_handle_service::implementation_type& impl,
0204     boost::system::error_code& ec)
0205 {
0206   if (is_open(impl))
0207   {
0208     BOOST_ASIO_HANDLER_OPERATION((iocp_service_.context(), "handle",
0209           &impl, reinterpret_cast<uintmax_t>(impl.handle_), "close"));
0210 
0211     if (!::CloseHandle(impl.handle_))
0212     {
0213       DWORD last_error = ::GetLastError();
0214       ec = boost::system::error_code(last_error,
0215           boost::asio::error::get_system_category());
0216     }
0217     else
0218     {
0219       ec = boost::system::error_code();
0220     }
0221 
0222     impl.handle_ = INVALID_HANDLE_VALUE;
0223     impl.safe_cancellation_thread_id_ = 0;
0224   }
0225   else
0226   {
0227     ec = boost::system::error_code();
0228   }
0229 
0230   BOOST_ASIO_ERROR_LOCATION(ec);
0231   return ec;
0232 }
0233 
0234 win_iocp_handle_service::native_handle_type win_iocp_handle_service::release(
0235     win_iocp_handle_service::implementation_type& impl,
0236     boost::system::error_code& ec)
0237 {
0238   if (!is_open(impl))
0239     return INVALID_HANDLE_VALUE;
0240 
0241   cancel(impl, ec);
0242   if (ec)
0243   {
0244     BOOST_ASIO_ERROR_LOCATION(ec);
0245     return INVALID_HANDLE_VALUE;
0246   }
0247 
0248   nt_set_info_fn fn = get_nt_set_info();
0249   if (fn == 0)
0250   {
0251     ec = boost::asio::error::operation_not_supported;
0252     BOOST_ASIO_ERROR_LOCATION(ec);
0253     return INVALID_HANDLE_VALUE;
0254   }
0255 
0256   ULONG_PTR iosb[2] = { 0, 0 };
0257   void* info[2] = { 0, 0 };
0258   if (fn(impl.handle_, iosb, &info, sizeof(info),
0259         61 /* FileReplaceCompletionInformation */))
0260   {
0261     ec = boost::asio::error::operation_not_supported;
0262     BOOST_ASIO_ERROR_LOCATION(ec);
0263     return INVALID_HANDLE_VALUE;
0264   }
0265 
0266   native_handle_type tmp = impl.handle_;
0267   impl.handle_ = INVALID_HANDLE_VALUE;
0268   return tmp;
0269 }
0270 
0271 boost::system::error_code win_iocp_handle_service::cancel(
0272     win_iocp_handle_service::implementation_type& impl,
0273     boost::system::error_code& ec)
0274 {
0275   if (!is_open(impl))
0276   {
0277     ec = boost::asio::error::bad_descriptor;
0278     BOOST_ASIO_ERROR_LOCATION(ec);
0279     return ec;
0280   }
0281 
0282   BOOST_ASIO_HANDLER_OPERATION((iocp_service_.context(), "handle",
0283         &impl, reinterpret_cast<uintmax_t>(impl.handle_), "cancel"));
0284 
0285   if (FARPROC cancel_io_ex_ptr = ::GetProcAddress(
0286         ::GetModuleHandleA("KERNEL32"), "CancelIoEx"))
0287   {
0288     // The version of Windows supports cancellation from any thread.
0289     typedef BOOL (WINAPI* cancel_io_ex_t)(HANDLE, LPOVERLAPPED);
0290     cancel_io_ex_t cancel_io_ex = reinterpret_cast<cancel_io_ex_t>(
0291         reinterpret_cast<void*>(cancel_io_ex_ptr));
0292     if (!cancel_io_ex(impl.handle_, 0))
0293     {
0294       DWORD last_error = ::GetLastError();
0295       if (last_error == ERROR_NOT_FOUND)
0296       {
0297         // ERROR_NOT_FOUND means that there were no operations to be
0298         // cancelled. We swallow this error to match the behaviour on other
0299         // platforms.
0300         ec = boost::system::error_code();
0301       }
0302       else
0303       {
0304         ec = boost::system::error_code(last_error,
0305             boost::asio::error::get_system_category());
0306       }
0307     }
0308     else
0309     {
0310       ec = boost::system::error_code();
0311     }
0312   }
0313   else if (impl.safe_cancellation_thread_id_ == 0)
0314   {
0315     // No operations have been started, so there's nothing to cancel.
0316     ec = boost::system::error_code();
0317   }
0318   else if (impl.safe_cancellation_thread_id_ == ::GetCurrentThreadId())
0319   {
0320     // Asynchronous operations have been started from the current thread only,
0321     // so it is safe to try to cancel them using CancelIo.
0322     if (!::CancelIo(impl.handle_))
0323     {
0324       DWORD last_error = ::GetLastError();
0325       ec = boost::system::error_code(last_error,
0326           boost::asio::error::get_system_category());
0327     }
0328     else
0329     {
0330       ec = boost::system::error_code();
0331     }
0332   }
0333   else
0334   {
0335     // Asynchronous operations have been started from more than one thread,
0336     // so cancellation is not safe.
0337     ec = boost::asio::error::operation_not_supported;
0338   }
0339 
0340   BOOST_ASIO_ERROR_LOCATION(ec);
0341   return ec;
0342 }
0343 
0344 size_t win_iocp_handle_service::do_write(
0345     win_iocp_handle_service::implementation_type& impl, uint64_t offset,
0346     const boost::asio::const_buffer& buffer, boost::system::error_code& ec)
0347 {
0348   if (!is_open(impl))
0349   {
0350     ec = boost::asio::error::bad_descriptor;
0351     BOOST_ASIO_ERROR_LOCATION(ec);
0352     return 0;
0353   }
0354 
0355   // A request to write 0 bytes on a handle is a no-op.
0356   if (buffer.size() == 0)
0357   {
0358     ec = boost::system::error_code();
0359     return 0;
0360   }
0361 
0362   overlapped_wrapper overlapped(ec);
0363   if (ec)
0364   {
0365     BOOST_ASIO_ERROR_LOCATION(ec);
0366     return 0;
0367   }
0368 
0369   // Write the data. 
0370   overlapped.Offset = offset & 0xFFFFFFFF;
0371   overlapped.OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
0372   BOOL ok = ::WriteFile(impl.handle_, buffer.data(),
0373       static_cast<DWORD>(buffer.size()), 0, &overlapped);
0374   if (!ok) 
0375   {
0376     DWORD last_error = ::GetLastError();
0377     if (last_error != ERROR_IO_PENDING)
0378     {
0379       ec = boost::system::error_code(last_error,
0380           boost::asio::error::get_system_category());
0381       BOOST_ASIO_ERROR_LOCATION(ec);
0382       return 0;
0383     }
0384   }
0385 
0386   // Wait for the operation to complete.
0387   DWORD bytes_transferred = 0;
0388   ok = ::GetOverlappedResult(impl.handle_,
0389       &overlapped, &bytes_transferred, TRUE);
0390   if (!ok)
0391   {
0392     DWORD last_error = ::GetLastError();
0393     ec = boost::system::error_code(last_error,
0394         boost::asio::error::get_system_category());
0395     BOOST_ASIO_ERROR_LOCATION(ec);
0396     return 0;
0397   }
0398 
0399   ec = boost::system::error_code();
0400   return bytes_transferred;
0401 }
0402 
0403 void win_iocp_handle_service::start_write_op(
0404     win_iocp_handle_service::implementation_type& impl, uint64_t offset,
0405     const boost::asio::const_buffer& buffer, operation* op)
0406 {
0407   update_cancellation_thread_id(impl);
0408   iocp_service_.work_started();
0409 
0410   if (!is_open(impl))
0411   {
0412     iocp_service_.on_completion(op, boost::asio::error::bad_descriptor);
0413   }
0414   else if (buffer.size() == 0)
0415   {
0416     // A request to write 0 bytes on a handle is a no-op.
0417     iocp_service_.on_completion(op);
0418   }
0419   else
0420   {
0421     DWORD bytes_transferred = 0;
0422     op->Offset = offset & 0xFFFFFFFF;
0423     op->OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
0424     BOOL ok = ::WriteFile(impl.handle_, buffer.data(),
0425         static_cast<DWORD>(buffer.size()),
0426         &bytes_transferred, op);
0427     DWORD last_error = ::GetLastError();
0428     if (!ok && last_error != ERROR_IO_PENDING
0429         && last_error != ERROR_MORE_DATA)
0430     {
0431       iocp_service_.on_completion(op, last_error, bytes_transferred);
0432     }
0433     else
0434     {
0435       iocp_service_.on_pending(op);
0436     }
0437   }
0438 }
0439 
0440 size_t win_iocp_handle_service::do_read(
0441     win_iocp_handle_service::implementation_type& impl, uint64_t offset,
0442     const boost::asio::mutable_buffer& buffer, boost::system::error_code& ec)
0443 {
0444   if (!is_open(impl))
0445   {
0446     ec = boost::asio::error::bad_descriptor;
0447     BOOST_ASIO_ERROR_LOCATION(ec);
0448     return 0;
0449   }
0450   
0451   // A request to read 0 bytes on a stream handle is a no-op.
0452   if (buffer.size() == 0)
0453   {
0454     ec = boost::system::error_code();
0455     return 0;
0456   }
0457 
0458   overlapped_wrapper overlapped(ec);
0459   if (ec)
0460   {
0461     BOOST_ASIO_ERROR_LOCATION(ec);
0462     return 0;
0463   }
0464 
0465   // Read some data.
0466   overlapped.Offset = offset & 0xFFFFFFFF;
0467   overlapped.OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
0468   BOOL ok = ::ReadFile(impl.handle_, buffer.data(),
0469       static_cast<DWORD>(buffer.size()), 0, &overlapped);
0470   if (!ok) 
0471   {
0472     DWORD last_error = ::GetLastError();
0473     if (last_error != ERROR_IO_PENDING && last_error != ERROR_MORE_DATA)
0474     {
0475       if (last_error == ERROR_HANDLE_EOF)
0476       {
0477         ec = boost::asio::error::eof;
0478       }
0479       else
0480       {
0481         ec = boost::system::error_code(last_error,
0482             boost::asio::error::get_system_category());
0483       }
0484       BOOST_ASIO_ERROR_LOCATION(ec);
0485       return 0;
0486     }
0487   }
0488 
0489   // Wait for the operation to complete.
0490   DWORD bytes_transferred = 0;
0491   ok = ::GetOverlappedResult(impl.handle_,
0492       &overlapped, &bytes_transferred, TRUE);
0493   if (!ok)
0494   {
0495     DWORD last_error = ::GetLastError();
0496     if (last_error == ERROR_HANDLE_EOF)
0497     {
0498       ec = boost::asio::error::eof;
0499     }
0500     else
0501     {
0502       ec = boost::system::error_code(last_error,
0503           boost::asio::error::get_system_category());
0504     }
0505     BOOST_ASIO_ERROR_LOCATION(ec);
0506     return (last_error == ERROR_MORE_DATA) ? bytes_transferred : 0;
0507   }
0508 
0509   ec = boost::system::error_code();
0510   return bytes_transferred;
0511 }
0512 
0513 void win_iocp_handle_service::start_read_op(
0514     win_iocp_handle_service::implementation_type& impl, uint64_t offset,
0515     const boost::asio::mutable_buffer& buffer, operation* op)
0516 {
0517   update_cancellation_thread_id(impl);
0518   iocp_service_.work_started();
0519 
0520   if (!is_open(impl))
0521   {
0522     iocp_service_.on_completion(op, boost::asio::error::bad_descriptor);
0523   }
0524   else if (buffer.size() == 0)
0525   {
0526     // A request to read 0 bytes on a handle is a no-op.
0527     iocp_service_.on_completion(op);
0528   }
0529   else
0530   {
0531     DWORD bytes_transferred = 0;
0532     op->Offset = offset & 0xFFFFFFFF;
0533     op->OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
0534     BOOL ok = ::ReadFile(impl.handle_, buffer.data(),
0535         static_cast<DWORD>(buffer.size()),
0536         &bytes_transferred, op);
0537     DWORD last_error = ::GetLastError();
0538     if (!ok && last_error != ERROR_IO_PENDING
0539         && last_error != ERROR_MORE_DATA)
0540     {
0541       iocp_service_.on_completion(op, last_error, bytes_transferred);
0542     }
0543     else
0544     {
0545       iocp_service_.on_pending(op);
0546     }
0547   }
0548 }
0549 
0550 void win_iocp_handle_service::update_cancellation_thread_id(
0551     win_iocp_handle_service::implementation_type& impl)
0552 {
0553   if (impl.safe_cancellation_thread_id_ == 0)
0554     impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId();
0555   else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId())
0556     impl.safe_cancellation_thread_id_ = ~DWORD(0);
0557 }
0558 
0559 void win_iocp_handle_service::close_for_destruction(implementation_type& impl)
0560 {
0561   if (is_open(impl))
0562   {
0563     BOOST_ASIO_HANDLER_OPERATION((iocp_service_.context(), "handle",
0564           &impl, reinterpret_cast<uintmax_t>(impl.handle_), "close"));
0565 
0566     ::CloseHandle(impl.handle_);
0567     impl.handle_ = INVALID_HANDLE_VALUE;
0568     impl.safe_cancellation_thread_id_ = 0;
0569   }
0570 }
0571 
0572 win_iocp_handle_service::nt_set_info_fn
0573 win_iocp_handle_service::get_nt_set_info()
0574 {
0575   void* ptr = interlocked_compare_exchange_pointer(&nt_set_info_, 0, 0);
0576   if (!ptr)
0577   {
0578     if (HMODULE h = ::GetModuleHandleA("NTDLL.DLL"))
0579       ptr = reinterpret_cast<void*>(GetProcAddress(h, "NtSetInformationFile"));
0580 
0581     // On failure, set nt_set_info_ to a special value to indicate that the
0582     // NtSetInformationFile function is unavailable. That way we won't bother
0583     // trying to look it up again.
0584     interlocked_exchange_pointer(&nt_set_info_, ptr ? ptr : this);
0585   }
0586 
0587   return reinterpret_cast<nt_set_info_fn>(ptr == this ? 0 : ptr);
0588 }
0589 
0590 void* win_iocp_handle_service::interlocked_compare_exchange_pointer(
0591     void** dest, void* exch, void* cmp)
0592 {
0593 #if defined(_M_IX86)
0594   return reinterpret_cast<void*>(InterlockedCompareExchange(
0595         reinterpret_cast<PLONG>(dest), reinterpret_cast<LONG>(exch),
0596         reinterpret_cast<LONG>(cmp)));
0597 #else
0598   return InterlockedCompareExchangePointer(dest, exch, cmp);
0599 #endif
0600 }
0601 
0602 void* win_iocp_handle_service::interlocked_exchange_pointer(
0603     void** dest, void* val)
0604 {
0605 #if defined(_M_IX86)
0606   return reinterpret_cast<void*>(InterlockedExchange(
0607         reinterpret_cast<PLONG>(dest), reinterpret_cast<LONG>(val)));
0608 #else
0609   return InterlockedExchangePointer(dest, val);
0610 #endif
0611 }
0612 
0613 } // namespace detail
0614 } // namespace asio
0615 } // namespace boost
0616 
0617 #include <boost/asio/detail/pop_options.hpp>
0618 
0619 #endif // defined(BOOST_ASIO_HAS_IOCP)
0620 
0621 #endif // BOOST_ASIO_DETAIL_IMPL_WIN_IOCP_HANDLE_SERVICE_IPP