win_iocp_handle_service.ipp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. //
  2. // detail/impl/win_iocp_handle_service.ipp
  3. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com)
  6. // Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com)
  7. //
  8. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  9. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  10. //
  11. #ifndef BOOST_ASIO_DETAIL_IMPL_WIN_IOCP_HANDLE_SERVICE_IPP
  12. #define BOOST_ASIO_DETAIL_IMPL_WIN_IOCP_HANDLE_SERVICE_IPP
  13. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  14. # pragma once
  15. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  16. #include <boost/asio/detail/config.hpp>
  17. #if defined(BOOST_ASIO_HAS_IOCP)
  18. #include <boost/asio/detail/win_iocp_handle_service.hpp>
  19. #include <boost/asio/detail/push_options.hpp>
  20. namespace boost {
  21. namespace asio {
  22. namespace detail {
  23. class win_iocp_handle_service::overlapped_wrapper
  24. : public OVERLAPPED
  25. {
  26. public:
  27. explicit overlapped_wrapper(boost::system::error_code& ec)
  28. {
  29. Internal = 0;
  30. InternalHigh = 0;
  31. Offset = 0;
  32. OffsetHigh = 0;
  33. // Create a non-signalled manual-reset event, for GetOverlappedResult.
  34. hEvent = ::CreateEventW(0, TRUE, FALSE, 0);
  35. if (hEvent)
  36. {
  37. // As documented in GetQueuedCompletionStatus, setting the low order
  38. // bit of this event prevents our synchronous writes from being treated
  39. // as completion port events.
  40. DWORD_PTR tmp = reinterpret_cast<DWORD_PTR>(hEvent);
  41. hEvent = reinterpret_cast<HANDLE>(tmp | 1);
  42. }
  43. else
  44. {
  45. DWORD last_error = ::GetLastError();
  46. ec = boost::system::error_code(last_error,
  47. boost::asio::error::get_system_category());
  48. }
  49. }
  50. ~overlapped_wrapper()
  51. {
  52. if (hEvent)
  53. {
  54. ::CloseHandle(hEvent);
  55. }
  56. }
  57. };
  58. win_iocp_handle_service::win_iocp_handle_service(
  59. boost::asio::io_context& io_context)
  60. : service_base<win_iocp_handle_service>(io_context),
  61. iocp_service_(boost::asio::use_service<win_iocp_io_context>(io_context)),
  62. mutex_(),
  63. impl_list_(0)
  64. {
  65. }
  66. void win_iocp_handle_service::shutdown()
  67. {
  68. // Close all implementations, causing all operations to complete.
  69. boost::asio::detail::mutex::scoped_lock lock(mutex_);
  70. implementation_type* impl = impl_list_;
  71. while (impl)
  72. {
  73. close_for_destruction(*impl);
  74. impl = impl->next_;
  75. }
  76. }
  77. void win_iocp_handle_service::construct(
  78. win_iocp_handle_service::implementation_type& impl)
  79. {
  80. impl.handle_ = INVALID_HANDLE_VALUE;
  81. impl.safe_cancellation_thread_id_ = 0;
  82. // Insert implementation into linked list of all implementations.
  83. boost::asio::detail::mutex::scoped_lock lock(mutex_);
  84. impl.next_ = impl_list_;
  85. impl.prev_ = 0;
  86. if (impl_list_)
  87. impl_list_->prev_ = &impl;
  88. impl_list_ = &impl;
  89. }
  90. void win_iocp_handle_service::move_construct(
  91. win_iocp_handle_service::implementation_type& impl,
  92. win_iocp_handle_service::implementation_type& other_impl)
  93. {
  94. impl.handle_ = other_impl.handle_;
  95. other_impl.handle_ = INVALID_HANDLE_VALUE;
  96. impl.safe_cancellation_thread_id_ = other_impl.safe_cancellation_thread_id_;
  97. other_impl.safe_cancellation_thread_id_ = 0;
  98. // Insert implementation into linked list of all implementations.
  99. boost::asio::detail::mutex::scoped_lock lock(mutex_);
  100. impl.next_ = impl_list_;
  101. impl.prev_ = 0;
  102. if (impl_list_)
  103. impl_list_->prev_ = &impl;
  104. impl_list_ = &impl;
  105. }
  106. void win_iocp_handle_service::move_assign(
  107. win_iocp_handle_service::implementation_type& impl,
  108. win_iocp_handle_service& other_service,
  109. win_iocp_handle_service::implementation_type& other_impl)
  110. {
  111. close_for_destruction(impl);
  112. if (this != &other_service)
  113. {
  114. // Remove implementation from linked list of all implementations.
  115. boost::asio::detail::mutex::scoped_lock lock(mutex_);
  116. if (impl_list_ == &impl)
  117. impl_list_ = impl.next_;
  118. if (impl.prev_)
  119. impl.prev_->next_ = impl.next_;
  120. if (impl.next_)
  121. impl.next_->prev_= impl.prev_;
  122. impl.next_ = 0;
  123. impl.prev_ = 0;
  124. }
  125. impl.handle_ = other_impl.handle_;
  126. other_impl.handle_ = INVALID_HANDLE_VALUE;
  127. impl.safe_cancellation_thread_id_ = other_impl.safe_cancellation_thread_id_;
  128. other_impl.safe_cancellation_thread_id_ = 0;
  129. if (this != &other_service)
  130. {
  131. // Insert implementation into linked list of all implementations.
  132. boost::asio::detail::mutex::scoped_lock lock(other_service.mutex_);
  133. impl.next_ = other_service.impl_list_;
  134. impl.prev_ = 0;
  135. if (other_service.impl_list_)
  136. other_service.impl_list_->prev_ = &impl;
  137. other_service.impl_list_ = &impl;
  138. }
  139. }
  140. void win_iocp_handle_service::destroy(
  141. win_iocp_handle_service::implementation_type& impl)
  142. {
  143. close_for_destruction(impl);
  144. // Remove implementation from linked list of all implementations.
  145. boost::asio::detail::mutex::scoped_lock lock(mutex_);
  146. if (impl_list_ == &impl)
  147. impl_list_ = impl.next_;
  148. if (impl.prev_)
  149. impl.prev_->next_ = impl.next_;
  150. if (impl.next_)
  151. impl.next_->prev_= impl.prev_;
  152. impl.next_ = 0;
  153. impl.prev_ = 0;
  154. }
  155. boost::system::error_code win_iocp_handle_service::assign(
  156. win_iocp_handle_service::implementation_type& impl,
  157. const native_handle_type& handle, boost::system::error_code& ec)
  158. {
  159. if (is_open(impl))
  160. {
  161. ec = boost::asio::error::already_open;
  162. return ec;
  163. }
  164. if (iocp_service_.register_handle(handle, ec))
  165. return ec;
  166. impl.handle_ = handle;
  167. ec = boost::system::error_code();
  168. return ec;
  169. }
  170. boost::system::error_code win_iocp_handle_service::close(
  171. win_iocp_handle_service::implementation_type& impl,
  172. boost::system::error_code& ec)
  173. {
  174. if (is_open(impl))
  175. {
  176. BOOST_ASIO_HANDLER_OPERATION((iocp_service_.context(), "handle",
  177. &impl, reinterpret_cast<uintmax_t>(impl.handle_), "close"));
  178. if (!::CloseHandle(impl.handle_))
  179. {
  180. DWORD last_error = ::GetLastError();
  181. ec = boost::system::error_code(last_error,
  182. boost::asio::error::get_system_category());
  183. }
  184. else
  185. {
  186. ec = boost::system::error_code();
  187. }
  188. impl.handle_ = INVALID_HANDLE_VALUE;
  189. impl.safe_cancellation_thread_id_ = 0;
  190. }
  191. else
  192. {
  193. ec = boost::system::error_code();
  194. }
  195. return ec;
  196. }
  197. boost::system::error_code win_iocp_handle_service::cancel(
  198. win_iocp_handle_service::implementation_type& impl,
  199. boost::system::error_code& ec)
  200. {
  201. if (!is_open(impl))
  202. {
  203. ec = boost::asio::error::bad_descriptor;
  204. return ec;
  205. }
  206. BOOST_ASIO_HANDLER_OPERATION((iocp_service_.context(), "handle",
  207. &impl, reinterpret_cast<uintmax_t>(impl.handle_), "cancel"));
  208. if (FARPROC cancel_io_ex_ptr = ::GetProcAddress(
  209. ::GetModuleHandleA("KERNEL32"), "CancelIoEx"))
  210. {
  211. // The version of Windows supports cancellation from any thread.
  212. typedef BOOL (WINAPI* cancel_io_ex_t)(HANDLE, LPOVERLAPPED);
  213. cancel_io_ex_t cancel_io_ex = (cancel_io_ex_t)cancel_io_ex_ptr;
  214. if (!cancel_io_ex(impl.handle_, 0))
  215. {
  216. DWORD last_error = ::GetLastError();
  217. if (last_error == ERROR_NOT_FOUND)
  218. {
  219. // ERROR_NOT_FOUND means that there were no operations to be
  220. // cancelled. We swallow this error to match the behaviour on other
  221. // platforms.
  222. ec = boost::system::error_code();
  223. }
  224. else
  225. {
  226. ec = boost::system::error_code(last_error,
  227. boost::asio::error::get_system_category());
  228. }
  229. }
  230. else
  231. {
  232. ec = boost::system::error_code();
  233. }
  234. }
  235. else if (impl.safe_cancellation_thread_id_ == 0)
  236. {
  237. // No operations have been started, so there's nothing to cancel.
  238. ec = boost::system::error_code();
  239. }
  240. else if (impl.safe_cancellation_thread_id_ == ::GetCurrentThreadId())
  241. {
  242. // Asynchronous operations have been started from the current thread only,
  243. // so it is safe to try to cancel them using CancelIo.
  244. if (!::CancelIo(impl.handle_))
  245. {
  246. DWORD last_error = ::GetLastError();
  247. ec = boost::system::error_code(last_error,
  248. boost::asio::error::get_system_category());
  249. }
  250. else
  251. {
  252. ec = boost::system::error_code();
  253. }
  254. }
  255. else
  256. {
  257. // Asynchronous operations have been started from more than one thread,
  258. // so cancellation is not safe.
  259. ec = boost::asio::error::operation_not_supported;
  260. }
  261. return ec;
  262. }
  263. size_t win_iocp_handle_service::do_write(
  264. win_iocp_handle_service::implementation_type& impl, uint64_t offset,
  265. const boost::asio::const_buffer& buffer, boost::system::error_code& ec)
  266. {
  267. if (!is_open(impl))
  268. {
  269. ec = boost::asio::error::bad_descriptor;
  270. return 0;
  271. }
  272. // A request to write 0 bytes on a handle is a no-op.
  273. if (buffer.size() == 0)
  274. {
  275. ec = boost::system::error_code();
  276. return 0;
  277. }
  278. overlapped_wrapper overlapped(ec);
  279. if (ec)
  280. {
  281. return 0;
  282. }
  283. // Write the data.
  284. overlapped.Offset = offset & 0xFFFFFFFF;
  285. overlapped.OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
  286. BOOL ok = ::WriteFile(impl.handle_, buffer.data(),
  287. static_cast<DWORD>(buffer.size()), 0, &overlapped);
  288. if (!ok)
  289. {
  290. DWORD last_error = ::GetLastError();
  291. if (last_error != ERROR_IO_PENDING)
  292. {
  293. ec = boost::system::error_code(last_error,
  294. boost::asio::error::get_system_category());
  295. return 0;
  296. }
  297. }
  298. // Wait for the operation to complete.
  299. DWORD bytes_transferred = 0;
  300. ok = ::GetOverlappedResult(impl.handle_,
  301. &overlapped, &bytes_transferred, TRUE);
  302. if (!ok)
  303. {
  304. DWORD last_error = ::GetLastError();
  305. ec = boost::system::error_code(last_error,
  306. boost::asio::error::get_system_category());
  307. return 0;
  308. }
  309. ec = boost::system::error_code();
  310. return bytes_transferred;
  311. }
  312. void win_iocp_handle_service::start_write_op(
  313. win_iocp_handle_service::implementation_type& impl, uint64_t offset,
  314. const boost::asio::const_buffer& buffer, operation* op)
  315. {
  316. update_cancellation_thread_id(impl);
  317. iocp_service_.work_started();
  318. if (!is_open(impl))
  319. {
  320. iocp_service_.on_completion(op, boost::asio::error::bad_descriptor);
  321. }
  322. else if (buffer.size() == 0)
  323. {
  324. // A request to write 0 bytes on a handle is a no-op.
  325. iocp_service_.on_completion(op);
  326. }
  327. else
  328. {
  329. DWORD bytes_transferred = 0;
  330. op->Offset = offset & 0xFFFFFFFF;
  331. op->OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
  332. BOOL ok = ::WriteFile(impl.handle_, buffer.data(),
  333. static_cast<DWORD>(buffer.size()),
  334. &bytes_transferred, op);
  335. DWORD last_error = ::GetLastError();
  336. if (!ok && last_error != ERROR_IO_PENDING
  337. && last_error != ERROR_MORE_DATA)
  338. {
  339. iocp_service_.on_completion(op, last_error, bytes_transferred);
  340. }
  341. else
  342. {
  343. iocp_service_.on_pending(op);
  344. }
  345. }
  346. }
  347. size_t win_iocp_handle_service::do_read(
  348. win_iocp_handle_service::implementation_type& impl, uint64_t offset,
  349. const boost::asio::mutable_buffer& buffer, boost::system::error_code& ec)
  350. {
  351. if (!is_open(impl))
  352. {
  353. ec = boost::asio::error::bad_descriptor;
  354. return 0;
  355. }
  356. // A request to read 0 bytes on a stream handle is a no-op.
  357. if (buffer.size() == 0)
  358. {
  359. ec = boost::system::error_code();
  360. return 0;
  361. }
  362. overlapped_wrapper overlapped(ec);
  363. if (ec)
  364. {
  365. return 0;
  366. }
  367. // Read some data.
  368. overlapped.Offset = offset & 0xFFFFFFFF;
  369. overlapped.OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
  370. BOOL ok = ::ReadFile(impl.handle_, buffer.data(),
  371. static_cast<DWORD>(buffer.size()), 0, &overlapped);
  372. if (!ok)
  373. {
  374. DWORD last_error = ::GetLastError();
  375. if (last_error != ERROR_IO_PENDING && last_error != ERROR_MORE_DATA)
  376. {
  377. if (last_error == ERROR_HANDLE_EOF)
  378. {
  379. ec = boost::asio::error::eof;
  380. }
  381. else
  382. {
  383. ec = boost::system::error_code(last_error,
  384. boost::asio::error::get_system_category());
  385. }
  386. return 0;
  387. }
  388. }
  389. // Wait for the operation to complete.
  390. DWORD bytes_transferred = 0;
  391. ok = ::GetOverlappedResult(impl.handle_,
  392. &overlapped, &bytes_transferred, TRUE);
  393. if (!ok)
  394. {
  395. DWORD last_error = ::GetLastError();
  396. if (last_error == ERROR_HANDLE_EOF)
  397. {
  398. ec = boost::asio::error::eof;
  399. }
  400. else
  401. {
  402. ec = boost::system::error_code(last_error,
  403. boost::asio::error::get_system_category());
  404. }
  405. return (last_error == ERROR_MORE_DATA) ? bytes_transferred : 0;
  406. }
  407. ec = boost::system::error_code();
  408. return bytes_transferred;
  409. }
  410. void win_iocp_handle_service::start_read_op(
  411. win_iocp_handle_service::implementation_type& impl, uint64_t offset,
  412. const boost::asio::mutable_buffer& buffer, operation* op)
  413. {
  414. update_cancellation_thread_id(impl);
  415. iocp_service_.work_started();
  416. if (!is_open(impl))
  417. {
  418. iocp_service_.on_completion(op, boost::asio::error::bad_descriptor);
  419. }
  420. else if (buffer.size() == 0)
  421. {
  422. // A request to read 0 bytes on a handle is a no-op.
  423. iocp_service_.on_completion(op);
  424. }
  425. else
  426. {
  427. DWORD bytes_transferred = 0;
  428. op->Offset = offset & 0xFFFFFFFF;
  429. op->OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
  430. BOOL ok = ::ReadFile(impl.handle_, buffer.data(),
  431. static_cast<DWORD>(buffer.size()),
  432. &bytes_transferred, op);
  433. DWORD last_error = ::GetLastError();
  434. if (!ok && last_error != ERROR_IO_PENDING
  435. && last_error != ERROR_MORE_DATA)
  436. {
  437. iocp_service_.on_completion(op, last_error, bytes_transferred);
  438. }
  439. else
  440. {
  441. iocp_service_.on_pending(op);
  442. }
  443. }
  444. }
  445. void win_iocp_handle_service::update_cancellation_thread_id(
  446. win_iocp_handle_service::implementation_type& impl)
  447. {
  448. if (impl.safe_cancellation_thread_id_ == 0)
  449. impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId();
  450. else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId())
  451. impl.safe_cancellation_thread_id_ = ~DWORD(0);
  452. }
  453. void win_iocp_handle_service::close_for_destruction(implementation_type& impl)
  454. {
  455. if (is_open(impl))
  456. {
  457. BOOST_ASIO_HANDLER_OPERATION((iocp_service_.context(), "handle",
  458. &impl, reinterpret_cast<uintmax_t>(impl.handle_), "close"));
  459. ::CloseHandle(impl.handle_);
  460. impl.handle_ = INVALID_HANDLE_VALUE;
  461. impl.safe_cancellation_thread_id_ = 0;
  462. }
  463. }
  464. } // namespace detail
  465. } // namespace asio
  466. } // namespace boost
  467. #include <boost/asio/detail/pop_options.hpp>
  468. #endif // defined(BOOST_ASIO_HAS_IOCP)
  469. #endif // BOOST_ASIO_DETAIL_IMPL_WIN_IOCP_HANDLE_SERVICE_IPP