/Src/Dependencies/Boost/boost/asio/detail/impl/win_iocp_io_service.ipp

http://hadesmem.googlecode.com/ · C++ Header · 511 lines · 398 code · 67 blank · 46 comment · 65 complexity · ab5926baecf85083e0f164da43b3890e MD5 · raw file

  1. //
  2. // detail/impl/win_iocp_io_service.ipp
  3. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2003-2011 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. #ifndef BOOST_ASIO_DETAIL_IMPL_WIN_IOCP_IO_SERVICE_IPP
  11. #define BOOST_ASIO_DETAIL_IMPL_WIN_IOCP_IO_SERVICE_IPP
  12. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  13. # pragma once
  14. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  15. #include <boost/asio/detail/config.hpp>
  16. #if defined(BOOST_ASIO_HAS_IOCP)
  17. #include <boost/limits.hpp>
  18. #include <boost/asio/error.hpp>
  19. #include <boost/asio/io_service.hpp>
  20. #include <boost/asio/detail/handler_alloc_helpers.hpp>
  21. #include <boost/asio/detail/handler_invoke_helpers.hpp>
  22. #include <boost/asio/detail/throw_error.hpp>
  23. #include <boost/asio/detail/win_iocp_io_service.hpp>
  24. #include <boost/asio/detail/push_options.hpp>
  25. namespace boost {
  26. namespace asio {
  27. namespace detail {
  28. struct win_iocp_io_service::work_finished_on_block_exit
  29. {
  30. ~work_finished_on_block_exit()
  31. {
  32. io_service_->work_finished();
  33. }
  34. win_iocp_io_service* io_service_;
  35. };
  36. struct win_iocp_io_service::timer_thread_function
  37. {
  38. void operator()()
  39. {
  40. while (::InterlockedExchangeAdd(&io_service_->shutdown_, 0) == 0)
  41. {
  42. if (::WaitForSingleObject(io_service_->waitable_timer_.handle,
  43. INFINITE) == WAIT_OBJECT_0)
  44. {
  45. ::InterlockedExchange(&io_service_->dispatch_required_, 1);
  46. ::PostQueuedCompletionStatus(io_service_->iocp_.handle,
  47. 0, wake_for_dispatch, 0);
  48. }
  49. }
  50. }
  51. win_iocp_io_service* io_service_;
  52. };
  53. win_iocp_io_service::win_iocp_io_service(boost::asio::io_service& io_service)
  54. : boost::asio::detail::service_base<win_iocp_io_service>(io_service),
  55. iocp_(),
  56. outstanding_work_(0),
  57. stopped_(0),
  58. shutdown_(0),
  59. dispatch_required_(0)
  60. {
  61. BOOST_ASIO_HANDLER_TRACKING_INIT;
  62. }
  63. void win_iocp_io_service::init(size_t concurrency_hint)
  64. {
  65. iocp_.handle = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0,
  66. static_cast<DWORD>((std::min<size_t>)(concurrency_hint, DWORD(~0))));
  67. if (!iocp_.handle)
  68. {
  69. DWORD last_error = ::GetLastError();
  70. boost::system::error_code ec(last_error,
  71. boost::asio::error::get_system_category());
  72. boost::asio::detail::throw_error(ec, "iocp");
  73. }
  74. }
  75. void win_iocp_io_service::shutdown_service()
  76. {
  77. ::InterlockedExchange(&shutdown_, 1);
  78. if (timer_thread_.get())
  79. {
  80. LARGE_INTEGER timeout;
  81. timeout.QuadPart = 1;
  82. ::SetWaitableTimer(waitable_timer_.handle, &timeout, 1, 0, 0, FALSE);
  83. }
  84. while (::InterlockedExchangeAdd(&outstanding_work_, 0) > 0)
  85. {
  86. op_queue<win_iocp_operation> ops;
  87. timer_queues_.get_all_timers(ops);
  88. ops.push(completed_ops_);
  89. if (!ops.empty())
  90. {
  91. while (win_iocp_operation* op = ops.front())
  92. {
  93. ops.pop();
  94. ::InterlockedDecrement(&outstanding_work_);
  95. op->destroy();
  96. }
  97. }
  98. else
  99. {
  100. DWORD bytes_transferred = 0;
  101. dword_ptr_t completion_key = 0;
  102. LPOVERLAPPED overlapped = 0;
  103. ::GetQueuedCompletionStatus(iocp_.handle, &bytes_transferred,
  104. &completion_key, &overlapped, gqcs_timeout);
  105. if (overlapped)
  106. {
  107. ::InterlockedDecrement(&outstanding_work_);
  108. static_cast<win_iocp_operation*>(overlapped)->destroy();
  109. }
  110. }
  111. }
  112. if (timer_thread_.get())
  113. timer_thread_->join();
  114. }
  115. boost::system::error_code win_iocp_io_service::register_handle(
  116. HANDLE handle, boost::system::error_code& ec)
  117. {
  118. if (::CreateIoCompletionPort(handle, iocp_.handle, 0, 0) == 0)
  119. {
  120. DWORD last_error = ::GetLastError();
  121. ec = boost::system::error_code(last_error,
  122. boost::asio::error::get_system_category());
  123. }
  124. else
  125. {
  126. ec = boost::system::error_code();
  127. }
  128. return ec;
  129. }
  130. size_t win_iocp_io_service::run(boost::system::error_code& ec)
  131. {
  132. if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0)
  133. {
  134. stop();
  135. ec = boost::system::error_code();
  136. return 0;
  137. }
  138. call_stack<win_iocp_io_service>::context ctx(this);
  139. size_t n = 0;
  140. while (do_one(true, ec))
  141. if (n != (std::numeric_limits<size_t>::max)())
  142. ++n;
  143. return n;
  144. }
  145. size_t win_iocp_io_service::run_one(boost::system::error_code& ec)
  146. {
  147. if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0)
  148. {
  149. stop();
  150. ec = boost::system::error_code();
  151. return 0;
  152. }
  153. call_stack<win_iocp_io_service>::context ctx(this);
  154. return do_one(true, ec);
  155. }
  156. size_t win_iocp_io_service::poll(boost::system::error_code& ec)
  157. {
  158. if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0)
  159. {
  160. stop();
  161. ec = boost::system::error_code();
  162. return 0;
  163. }
  164. call_stack<win_iocp_io_service>::context ctx(this);
  165. size_t n = 0;
  166. while (do_one(false, ec))
  167. if (n != (std::numeric_limits<size_t>::max)())
  168. ++n;
  169. return n;
  170. }
  171. size_t win_iocp_io_service::poll_one(boost::system::error_code& ec)
  172. {
  173. if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0)
  174. {
  175. stop();
  176. ec = boost::system::error_code();
  177. return 0;
  178. }
  179. call_stack<win_iocp_io_service>::context ctx(this);
  180. return do_one(false, ec);
  181. }
  182. void win_iocp_io_service::stop()
  183. {
  184. if (::InterlockedExchange(&stopped_, 1) == 0)
  185. {
  186. if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, 0))
  187. {
  188. DWORD last_error = ::GetLastError();
  189. boost::system::error_code ec(last_error,
  190. boost::asio::error::get_system_category());
  191. boost::asio::detail::throw_error(ec, "pqcs");
  192. }
  193. }
  194. }
  195. void win_iocp_io_service::post_deferred_completion(win_iocp_operation* op)
  196. {
  197. // Flag the operation as ready.
  198. op->ready_ = 1;
  199. // Enqueue the operation on the I/O completion port.
  200. if (!::PostQueuedCompletionStatus(iocp_.handle,
  201. 0, overlapped_contains_result, op))
  202. {
  203. // Out of resources. Put on completed queue instead.
  204. mutex::scoped_lock lock(dispatch_mutex_);
  205. completed_ops_.push(op);
  206. ::InterlockedExchange(&dispatch_required_, 1);
  207. }
  208. }
  209. void win_iocp_io_service::post_deferred_completions(
  210. op_queue<win_iocp_operation>& ops)
  211. {
  212. while (win_iocp_operation* op = ops.front())
  213. {
  214. ops.pop();
  215. // Flag the operation as ready.
  216. op->ready_ = 1;
  217. // Enqueue the operation on the I/O completion port.
  218. if (!::PostQueuedCompletionStatus(iocp_.handle,
  219. 0, overlapped_contains_result, op))
  220. {
  221. // Out of resources. Put on completed queue instead.
  222. mutex::scoped_lock lock(dispatch_mutex_);
  223. completed_ops_.push(op);
  224. completed_ops_.push(ops);
  225. ::InterlockedExchange(&dispatch_required_, 1);
  226. }
  227. }
  228. }
  229. void win_iocp_io_service::abandon_operations(
  230. op_queue<win_iocp_operation>& ops)
  231. {
  232. while (win_iocp_operation* op = ops.front())
  233. {
  234. ops.pop();
  235. ::InterlockedDecrement(&outstanding_work_);
  236. op->destroy();
  237. }
  238. }
  239. void win_iocp_io_service::on_pending(win_iocp_operation* op)
  240. {
  241. if (::InterlockedCompareExchange(&op->ready_, 1, 0) == 1)
  242. {
  243. // Enqueue the operation on the I/O completion port.
  244. if (!::PostQueuedCompletionStatus(iocp_.handle,
  245. 0, overlapped_contains_result, op))
  246. {
  247. // Out of resources. Put on completed queue instead.
  248. mutex::scoped_lock lock(dispatch_mutex_);
  249. completed_ops_.push(op);
  250. ::InterlockedExchange(&dispatch_required_, 1);
  251. }
  252. }
  253. }
  254. void win_iocp_io_service::on_completion(win_iocp_operation* op,
  255. DWORD last_error, DWORD bytes_transferred)
  256. {
  257. // Flag that the operation is ready for invocation.
  258. op->ready_ = 1;
  259. // Store results in the OVERLAPPED structure.
  260. op->Internal = reinterpret_cast<ulong_ptr_t>(
  261. &boost::asio::error::get_system_category());
  262. op->Offset = last_error;
  263. op->OffsetHigh = bytes_transferred;
  264. // Enqueue the operation on the I/O completion port.
  265. if (!::PostQueuedCompletionStatus(iocp_.handle,
  266. 0, overlapped_contains_result, op))
  267. {
  268. // Out of resources. Put on completed queue instead.
  269. mutex::scoped_lock lock(dispatch_mutex_);
  270. completed_ops_.push(op);
  271. ::InterlockedExchange(&dispatch_required_, 1);
  272. }
  273. }
  274. void win_iocp_io_service::on_completion(win_iocp_operation* op,
  275. const boost::system::error_code& ec, DWORD bytes_transferred)
  276. {
  277. // Flag that the operation is ready for invocation.
  278. op->ready_ = 1;
  279. // Store results in the OVERLAPPED structure.
  280. op->Internal = reinterpret_cast<ulong_ptr_t>(&ec.category());
  281. op->Offset = ec.value();
  282. op->OffsetHigh = bytes_transferred;
  283. // Enqueue the operation on the I/O completion port.
  284. if (!::PostQueuedCompletionStatus(iocp_.handle,
  285. 0, overlapped_contains_result, op))
  286. {
  287. // Out of resources. Put on completed queue instead.
  288. mutex::scoped_lock lock(dispatch_mutex_);
  289. completed_ops_.push(op);
  290. ::InterlockedExchange(&dispatch_required_, 1);
  291. }
  292. }
  293. size_t win_iocp_io_service::do_one(bool block, boost::system::error_code& ec)
  294. {
  295. for (;;)
  296. {
  297. // Try to acquire responsibility for dispatching timers and completed ops.
  298. if (::InterlockedCompareExchange(&dispatch_required_, 0, 1) == 1)
  299. {
  300. mutex::scoped_lock lock(dispatch_mutex_);
  301. // Dispatch pending timers and operations.
  302. op_queue<win_iocp_operation> ops;
  303. ops.push(completed_ops_);
  304. timer_queues_.get_ready_timers(ops);
  305. post_deferred_completions(ops);
  306. update_timeout();
  307. }
  308. // Get the next operation from the queue.
  309. DWORD bytes_transferred = 0;
  310. dword_ptr_t completion_key = 0;
  311. LPOVERLAPPED overlapped = 0;
  312. ::SetLastError(0);
  313. BOOL ok = ::GetQueuedCompletionStatus(iocp_.handle, &bytes_transferred,
  314. &completion_key, &overlapped, block ? gqcs_timeout : 0);
  315. DWORD last_error = ::GetLastError();
  316. if (overlapped)
  317. {
  318. win_iocp_operation* op = static_cast<win_iocp_operation*>(overlapped);
  319. boost::system::error_code result_ec(last_error,
  320. boost::asio::error::get_system_category());
  321. // We may have been passed the last_error and bytes_transferred in the
  322. // OVERLAPPED structure itself.
  323. if (completion_key == overlapped_contains_result)
  324. {
  325. result_ec = boost::system::error_code(static_cast<int>(op->Offset),
  326. *reinterpret_cast<boost::system::error_category*>(op->Internal));
  327. bytes_transferred = op->OffsetHigh;
  328. }
  329. // Otherwise ensure any result has been saved into the OVERLAPPED
  330. // structure.
  331. else
  332. {
  333. op->Internal = reinterpret_cast<ulong_ptr_t>(&result_ec.category());
  334. op->Offset = result_ec.value();
  335. op->OffsetHigh = bytes_transferred;
  336. }
  337. // Dispatch the operation only if ready. The operation may not be ready
  338. // if the initiating function (e.g. a call to WSARecv) has not yet
  339. // returned. This is because the initiating function still wants access
  340. // to the operation's OVERLAPPED structure.
  341. if (::InterlockedCompareExchange(&op->ready_, 1, 0) == 1)
  342. {
  343. // Ensure the count of outstanding work is decremented on block exit.
  344. work_finished_on_block_exit on_exit = { this };
  345. (void)on_exit;
  346. op->complete(*this, result_ec, bytes_transferred);
  347. ec = boost::system::error_code();
  348. return 1;
  349. }
  350. }
  351. else if (!ok)
  352. {
  353. if (last_error != WAIT_TIMEOUT)
  354. {
  355. ec = boost::system::error_code(last_error,
  356. boost::asio::error::get_system_category());
  357. return 0;
  358. }
  359. // If we're not polling we need to keep going until we get a real handler.
  360. if (block)
  361. continue;
  362. ec = boost::system::error_code();
  363. return 0;
  364. }
  365. else if (completion_key == wake_for_dispatch)
  366. {
  367. // We have been woken up to try to acquire responsibility for dispatching
  368. // timers and completed operations.
  369. }
  370. else
  371. {
  372. // The stopped_ flag is always checked to ensure that any leftover
  373. // interrupts from a previous run invocation are ignored.
  374. if (::InterlockedExchangeAdd(&stopped_, 0) != 0)
  375. {
  376. // Wake up next thread that is blocked on GetQueuedCompletionStatus.
  377. if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, 0))
  378. {
  379. last_error = ::GetLastError();
  380. ec = boost::system::error_code(last_error,
  381. boost::asio::error::get_system_category());
  382. return 0;
  383. }
  384. ec = boost::system::error_code();
  385. return 0;
  386. }
  387. }
  388. }
  389. }
  390. void win_iocp_io_service::do_add_timer_queue(timer_queue_base& queue)
  391. {
  392. mutex::scoped_lock lock(dispatch_mutex_);
  393. timer_queues_.insert(&queue);
  394. if (!waitable_timer_.handle)
  395. {
  396. waitable_timer_.handle = ::CreateWaitableTimer(0, FALSE, 0);
  397. if (waitable_timer_.handle == 0)
  398. {
  399. DWORD last_error = ::GetLastError();
  400. boost::system::error_code ec(last_error,
  401. boost::asio::error::get_system_category());
  402. boost::asio::detail::throw_error(ec, "timer");
  403. }
  404. LARGE_INTEGER timeout;
  405. timeout.QuadPart = -max_timeout_usec;
  406. timeout.QuadPart *= 10;
  407. ::SetWaitableTimer(waitable_timer_.handle,
  408. &timeout, max_timeout_msec, 0, 0, FALSE);
  409. }
  410. if (!timer_thread_.get())
  411. {
  412. timer_thread_function thread_function = { this };
  413. timer_thread_.reset(new thread(thread_function, 65536));
  414. }
  415. }
  416. void win_iocp_io_service::do_remove_timer_queue(timer_queue_base& queue)
  417. {
  418. mutex::scoped_lock lock(dispatch_mutex_);
  419. timer_queues_.erase(&queue);
  420. }
  421. void win_iocp_io_service::update_timeout()
  422. {
  423. if (timer_thread_.get())
  424. {
  425. // There's no point updating the waitable timer if the new timeout period
  426. // exceeds the maximum timeout. In that case, we might as well wait for the
  427. // existing period of the timer to expire.
  428. long timeout_usec = timer_queues_.wait_duration_usec(max_timeout_usec);
  429. if (timeout_usec < max_timeout_usec)
  430. {
  431. LARGE_INTEGER timeout;
  432. timeout.QuadPart = -timeout_usec;
  433. timeout.QuadPart *= 10;
  434. ::SetWaitableTimer(waitable_timer_.handle,
  435. &timeout, max_timeout_msec, 0, 0, FALSE);
  436. }
  437. }
  438. }
  439. } // namespace detail
  440. } // namespace asio
  441. } // namespace boost
  442. #include <boost/asio/detail/pop_options.hpp>
  443. #endif // defined(BOOST_ASIO_HAS_IOCP)
  444. #endif // BOOST_ASIO_DETAIL_IMPL_WIN_IOCP_IO_SERVICE_IPP