win_iocp_io_service.hpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  1. //
  2. // win_iocp_io_service.hpp
  3. // ~~~~~~~~~~~~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2003-2010 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 ASIO_DETAIL_WIN_IOCP_IO_SERVICE_HPP
  11. #define ASIO_DETAIL_WIN_IOCP_IO_SERVICE_HPP
  12. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  13. # pragma once
  14. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  15. #include "asio/detail/push_options.hpp"
  16. #include "asio/detail/win_iocp_io_service_fwd.hpp"
  17. #if defined(ASIO_HAS_IOCP)
  18. #include "asio/detail/push_options.hpp"
  19. #include <boost/limits.hpp>
  20. #include <boost/throw_exception.hpp>
  21. #include "asio/detail/pop_options.hpp"
  22. #include "asio/io_service.hpp"
  23. #include "asio/system_error.hpp"
  24. #include "asio/detail/call_stack.hpp"
  25. #include "asio/detail/completion_handler.hpp"
  26. #include "asio/detail/fenced_block.hpp"
  27. #include "asio/detail/handler_alloc_helpers.hpp"
  28. #include "asio/detail/handler_invoke_helpers.hpp"
  29. #include "asio/detail/mutex.hpp"
  30. #include "asio/detail/op_queue.hpp"
  31. #include "asio/detail/service_base.hpp"
  32. #include "asio/detail/socket_types.hpp"
  33. #include "asio/detail/timer_op.hpp"
  34. #include "asio/detail/timer_queue_base.hpp"
  35. #include "asio/detail/timer_queue_fwd.hpp"
  36. #include "asio/detail/timer_queue_set.hpp"
  37. #include "asio/detail/win_iocp_operation.hpp"
  38. namespace asio {
  39. namespace detail {
  40. class timer_op;
  41. class win_iocp_io_service
  42. : public asio::detail::service_base<win_iocp_io_service>
  43. {
  44. public:
  45. typedef win_iocp_operation operation;
  46. // Constructor.
  47. win_iocp_io_service(asio::io_service& io_service)
  48. : asio::detail::service_base<win_iocp_io_service>(io_service),
  49. iocp_(),
  50. outstanding_work_(0),
  51. stopped_(0),
  52. shutdown_(0),
  53. timer_thread_(0),
  54. timer_interrupt_issued_(false)
  55. {
  56. }
  57. void init(size_t concurrency_hint)
  58. {
  59. iocp_.handle = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0,
  60. static_cast<DWORD>((std::min<size_t>)(concurrency_hint, DWORD(~0))));
  61. if (!iocp_.handle)
  62. {
  63. DWORD last_error = ::GetLastError();
  64. asio::system_error e(
  65. asio::error_code(last_error,
  66. asio::error::get_system_category()),
  67. "iocp");
  68. boost::throw_exception(e);
  69. }
  70. }
  71. // Destroy all user-defined handler objects owned by the service.
  72. void shutdown_service()
  73. {
  74. ::InterlockedExchange(&shutdown_, 1);
  75. while (::InterlockedExchangeAdd(&outstanding_work_, 0) > 0)
  76. {
  77. op_queue<operation> ops;
  78. timer_queues_.get_all_timers(ops);
  79. ops.push(completed_ops_);
  80. if (!ops.empty())
  81. {
  82. while (operation* op = ops.front())
  83. {
  84. ops.pop();
  85. ::InterlockedDecrement(&outstanding_work_);
  86. op->destroy();
  87. }
  88. }
  89. else
  90. {
  91. DWORD bytes_transferred = 0;
  92. dword_ptr_t completion_key = 0;
  93. LPOVERLAPPED overlapped = 0;
  94. ::GetQueuedCompletionStatus(iocp_.handle, &bytes_transferred,
  95. &completion_key, &overlapped, max_timeout);
  96. if (overlapped)
  97. {
  98. ::InterlockedDecrement(&outstanding_work_);
  99. static_cast<operation*>(overlapped)->destroy();
  100. }
  101. }
  102. }
  103. }
  104. // Initialise the task. Nothing to do here.
  105. void init_task()
  106. {
  107. }
  108. // Register a handle with the IO completion port.
  109. asio::error_code register_handle(
  110. HANDLE handle, asio::error_code& ec)
  111. {
  112. if (::CreateIoCompletionPort(handle, iocp_.handle, 0, 0) == 0)
  113. {
  114. DWORD last_error = ::GetLastError();
  115. ec = asio::error_code(last_error,
  116. asio::error::get_system_category());
  117. }
  118. else
  119. {
  120. ec = asio::error_code();
  121. }
  122. return ec;
  123. }
  124. // Run the event loop until stopped or no more work.
  125. size_t run(asio::error_code& ec)
  126. {
  127. if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0)
  128. {
  129. stop();
  130. ec = asio::error_code();
  131. return 0;
  132. }
  133. call_stack<win_iocp_io_service>::context ctx(this);
  134. size_t n = 0;
  135. while (do_one(true, ec))
  136. if (n != (std::numeric_limits<size_t>::max)())
  137. ++n;
  138. return n;
  139. }
  140. // Run until stopped or one operation is performed.
  141. size_t run_one(asio::error_code& ec)
  142. {
  143. if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0)
  144. {
  145. stop();
  146. ec = asio::error_code();
  147. return 0;
  148. }
  149. call_stack<win_iocp_io_service>::context ctx(this);
  150. return do_one(true, ec);
  151. }
  152. // Poll for operations without blocking.
  153. size_t poll(asio::error_code& ec)
  154. {
  155. if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0)
  156. {
  157. stop();
  158. ec = asio::error_code();
  159. return 0;
  160. }
  161. call_stack<win_iocp_io_service>::context ctx(this);
  162. size_t n = 0;
  163. while (do_one(false, ec))
  164. if (n != (std::numeric_limits<size_t>::max)())
  165. ++n;
  166. return n;
  167. }
  168. // Poll for one operation without blocking.
  169. size_t poll_one(asio::error_code& ec)
  170. {
  171. if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0)
  172. {
  173. stop();
  174. ec = asio::error_code();
  175. return 0;
  176. }
  177. call_stack<win_iocp_io_service>::context ctx(this);
  178. return do_one(false, ec);
  179. }
  180. // Stop the event processing loop.
  181. void stop()
  182. {
  183. if (::InterlockedExchange(&stopped_, 1) == 0)
  184. {
  185. if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, 0))
  186. {
  187. DWORD last_error = ::GetLastError();
  188. asio::system_error e(
  189. asio::error_code(last_error,
  190. asio::error::get_system_category()),
  191. "pqcs");
  192. boost::throw_exception(e);
  193. }
  194. }
  195. }
  196. // Reset in preparation for a subsequent run invocation.
  197. void reset()
  198. {
  199. ::InterlockedExchange(&stopped_, 0);
  200. }
  201. // Notify that some work has started.
  202. void work_started()
  203. {
  204. ::InterlockedIncrement(&outstanding_work_);
  205. }
  206. // Notify that some work has finished.
  207. void work_finished()
  208. {
  209. if (::InterlockedDecrement(&outstanding_work_) == 0)
  210. stop();
  211. }
  212. // Request invocation of the given handler.
  213. template <typename Handler>
  214. void dispatch(Handler handler)
  215. {
  216. if (call_stack<win_iocp_io_service>::contains(this))
  217. {
  218. asio::detail::fenced_block b;
  219. asio_handler_invoke_helpers::invoke(handler, handler);
  220. }
  221. else
  222. post(handler);
  223. }
  224. // Request invocation of the given handler and return immediately.
  225. template <typename Handler>
  226. void post(Handler handler)
  227. {
  228. // Allocate and construct an operation to wrap the handler.
  229. typedef completion_handler<Handler> value_type;
  230. typedef handler_alloc_traits<Handler, value_type> alloc_traits;
  231. raw_handler_ptr<alloc_traits> raw_ptr(handler);
  232. handler_ptr<alloc_traits> ptr(raw_ptr, handler);
  233. post_immediate_completion(ptr.get());
  234. ptr.release();
  235. }
  236. // Request invocation of the given operation and return immediately. Assumes
  237. // that work_started() has not yet been called for the operation.
  238. void post_immediate_completion(operation* op)
  239. {
  240. work_started();
  241. post_deferred_completion(op);
  242. }
  243. // Request invocation of the given operation and return immediately. Assumes
  244. // that work_started() was previously called for the operation.
  245. void post_deferred_completion(operation* op)
  246. {
  247. // Flag the operation as ready.
  248. op->ready_ = 1;
  249. // Enqueue the operation on the I/O completion port.
  250. if (!::PostQueuedCompletionStatus(iocp_.handle,
  251. 0, overlapped_contains_result, op))
  252. {
  253. // Out of resources. Put on completed queue instead.
  254. asio::detail::mutex::scoped_lock lock(timer_mutex_);
  255. completed_ops_.push(op);
  256. }
  257. }
  258. // Request invocation of the given operation and return immediately. Assumes
  259. // that work_started() was previously called for the operations.
  260. void post_deferred_completions(op_queue<operation>& ops)
  261. {
  262. while (operation* op = ops.front())
  263. {
  264. ops.pop();
  265. // Flag the operation as ready.
  266. op->ready_ = 1;
  267. // Enqueue the operation on the I/O completion port.
  268. if (!::PostQueuedCompletionStatus(iocp_.handle,
  269. 0, overlapped_contains_result, op))
  270. {
  271. // Out of resources. Put on completed queue instead.
  272. asio::detail::mutex::scoped_lock lock(timer_mutex_);
  273. completed_ops_.push(op);
  274. completed_ops_.push(ops);
  275. }
  276. }
  277. }
  278. // Called after starting an overlapped I/O operation that did not complete
  279. // immediately. The caller must have already called work_started() prior to
  280. // starting the operation.
  281. void on_pending(operation* op)
  282. {
  283. if (::InterlockedCompareExchange(&op->ready_, 1, 0) == 1)
  284. {
  285. // Enqueue the operation on the I/O completion port.
  286. if (!::PostQueuedCompletionStatus(iocp_.handle,
  287. 0, overlapped_contains_result, op))
  288. {
  289. // Out of resources. Put on completed queue instead.
  290. asio::detail::mutex::scoped_lock lock(timer_mutex_);
  291. completed_ops_.push(op);
  292. }
  293. }
  294. }
  295. // Called after starting an overlapped I/O operation that completed
  296. // immediately. The caller must have already called work_started() prior to
  297. // starting the operation.
  298. void on_completion(operation* op,
  299. DWORD last_error = 0, DWORD bytes_transferred = 0)
  300. {
  301. // Flag that the operation is ready for invocation.
  302. op->ready_ = 1;
  303. // Store results in the OVERLAPPED structure.
  304. op->Internal = asio::error::get_system_category();
  305. op->Offset = last_error;
  306. op->OffsetHigh = bytes_transferred;
  307. // Enqueue the operation on the I/O completion port.
  308. if (!::PostQueuedCompletionStatus(iocp_.handle,
  309. 0, overlapped_contains_result, op))
  310. {
  311. // Out of resources. Put on completed queue instead.
  312. asio::detail::mutex::scoped_lock lock(timer_mutex_);
  313. completed_ops_.push(op);
  314. }
  315. }
  316. // Called after starting an overlapped I/O operation that completed
  317. // immediately. The caller must have already called work_started() prior to
  318. // starting the operation.
  319. void on_completion(operation* op,
  320. const asio::error_code& ec, DWORD bytes_transferred = 0)
  321. {
  322. // Flag that the operation is ready for invocation.
  323. op->ready_ = 1;
  324. // Store results in the OVERLAPPED structure.
  325. op->Internal = ec.category();
  326. op->Offset = ec.value();
  327. op->OffsetHigh = bytes_transferred;
  328. // Enqueue the operation on the I/O completion port.
  329. if (!::PostQueuedCompletionStatus(iocp_.handle,
  330. 0, overlapped_contains_result, op))
  331. {
  332. // Out of resources. Put on completed queue instead.
  333. asio::detail::mutex::scoped_lock lock(timer_mutex_);
  334. completed_ops_.push(op);
  335. }
  336. }
  337. // Add a new timer queue to the service.
  338. template <typename Time_Traits>
  339. void add_timer_queue(timer_queue<Time_Traits>& timer_queue)
  340. {
  341. asio::detail::mutex::scoped_lock lock(timer_mutex_);
  342. timer_queues_.insert(&timer_queue);
  343. }
  344. // Remove a timer queue from the service.
  345. template <typename Time_Traits>
  346. void remove_timer_queue(timer_queue<Time_Traits>& timer_queue)
  347. {
  348. asio::detail::mutex::scoped_lock lock(timer_mutex_);
  349. timer_queues_.erase(&timer_queue);
  350. }
  351. // Schedule a new operation in the given timer queue to expire at the
  352. // specified absolute time.
  353. template <typename Time_Traits>
  354. void schedule_timer(timer_queue<Time_Traits>& timer_queue,
  355. const typename Time_Traits::time_type& time, timer_op* op, void* token)
  356. {
  357. // If the service has been shut down we silently discard the timer.
  358. if (::InterlockedExchangeAdd(&shutdown_, 0) != 0)
  359. return;
  360. asio::detail::mutex::scoped_lock lock(timer_mutex_);
  361. bool interrupt = timer_queue.enqueue_timer(time, op, token);
  362. work_started();
  363. if (interrupt && !timer_interrupt_issued_)
  364. {
  365. timer_interrupt_issued_ = true;
  366. lock.unlock();
  367. ::PostQueuedCompletionStatus(iocp_.handle,
  368. 0, steal_timer_dispatching, 0);
  369. }
  370. }
  371. // Cancel the timer associated with the given token. Returns the number of
  372. // handlers that have been posted or dispatched.
  373. template <typename Time_Traits>
  374. std::size_t cancel_timer(timer_queue<Time_Traits>& timer_queue, void* token)
  375. {
  376. // If the service has been shut down we silently ignore the cancellation.
  377. if (::InterlockedExchangeAdd(&shutdown_, 0) != 0)
  378. return 0;
  379. asio::detail::mutex::scoped_lock lock(timer_mutex_);
  380. op_queue<operation> ops;
  381. std::size_t n = timer_queue.cancel_timer(token, ops);
  382. post_deferred_completions(ops);
  383. if (n > 0 && !timer_interrupt_issued_)
  384. {
  385. timer_interrupt_issued_ = true;
  386. lock.unlock();
  387. ::PostQueuedCompletionStatus(iocp_.handle,
  388. 0, steal_timer_dispatching, 0);
  389. }
  390. return n;
  391. }
  392. private:
  393. #if defined(WINVER) && (WINVER < 0x0500)
  394. typedef DWORD dword_ptr_t;
  395. typedef ULONG ulong_ptr_t;
  396. #else // defined(WINVER) && (WINVER < 0x0500)
  397. typedef DWORD_PTR dword_ptr_t;
  398. typedef ULONG_PTR ulong_ptr_t;
  399. #endif // defined(WINVER) && (WINVER < 0x0500)
  400. // Dequeues at most one operation from the I/O completion port, and then
  401. // executes it. Returns the number of operations that were dequeued (i.e.
  402. // either 0 or 1).
  403. size_t do_one(bool block, asio::error_code& ec)
  404. {
  405. long this_thread_id = static_cast<long>(::GetCurrentThreadId());
  406. for (;;)
  407. {
  408. // Try to acquire responsibility for dispatching timers.
  409. bool dispatching_timers = (::InterlockedCompareExchange(
  410. &timer_thread_, this_thread_id, 0) == 0);
  411. // Calculate timeout for GetQueuedCompletionStatus call.
  412. DWORD timeout = max_timeout;
  413. if (dispatching_timers)
  414. {
  415. asio::detail::mutex::scoped_lock lock(timer_mutex_);
  416. timer_interrupt_issued_ = false;
  417. timeout = get_timeout();
  418. }
  419. // Get the next operation from the queue.
  420. DWORD bytes_transferred = 0;
  421. dword_ptr_t completion_key = 0;
  422. LPOVERLAPPED overlapped = 0;
  423. ::SetLastError(0);
  424. BOOL ok = ::GetQueuedCompletionStatus(iocp_.handle, &bytes_transferred,
  425. &completion_key, &overlapped, block ? timeout : 0);
  426. DWORD last_error = ::GetLastError();
  427. // Dispatch any pending timers.
  428. if (dispatching_timers)
  429. {
  430. asio::detail::mutex::scoped_lock lock(timer_mutex_);
  431. op_queue<operation> ops;
  432. ops.push(completed_ops_);
  433. timer_queues_.get_ready_timers(ops);
  434. post_deferred_completions(ops);
  435. }
  436. if (!ok && overlapped == 0)
  437. {
  438. if (block && last_error == WAIT_TIMEOUT)
  439. {
  440. // Relinquish responsibility for dispatching timers.
  441. if (dispatching_timers)
  442. {
  443. ::InterlockedCompareExchange(&timer_thread_, 0, this_thread_id);
  444. }
  445. continue;
  446. }
  447. // Transfer responsibility for dispatching timers to another thread.
  448. if (dispatching_timers && ::InterlockedCompareExchange(
  449. &timer_thread_, 0, this_thread_id) == this_thread_id)
  450. {
  451. ::PostQueuedCompletionStatus(iocp_.handle,
  452. 0, transfer_timer_dispatching, 0);
  453. }
  454. ec = asio::error_code();
  455. return 0;
  456. }
  457. else if (overlapped)
  458. {
  459. operation* op = static_cast<operation*>(overlapped);
  460. asio::error_code result_ec(last_error,
  461. asio::error::get_system_category());
  462. // Transfer responsibility for dispatching timers to another thread.
  463. if (dispatching_timers && ::InterlockedCompareExchange(
  464. &timer_thread_, 0, this_thread_id) == this_thread_id)
  465. {
  466. ::PostQueuedCompletionStatus(iocp_.handle,
  467. 0, transfer_timer_dispatching, 0);
  468. }
  469. // We may have been passed the last_error and bytes_transferred in the
  470. // OVERLAPPED structure itself.
  471. if (completion_key == overlapped_contains_result)
  472. {
  473. result_ec = asio::error_code(static_cast<int>(op->Offset),
  474. static_cast<asio::error_category>(op->Internal));
  475. bytes_transferred = op->OffsetHigh;
  476. }
  477. // Otherwise ensure any result has been saved into the OVERLAPPED
  478. // structure.
  479. else
  480. {
  481. op->Internal = result_ec.category();
  482. op->Offset = result_ec.value();
  483. op->OffsetHigh = bytes_transferred;
  484. }
  485. // Dispatch the operation only if ready. The operation may not be ready
  486. // if the initiating function (e.g. a call to WSARecv) has not yet
  487. // returned. This is because the initiating function still wants access
  488. // to the operation's OVERLAPPED structure.
  489. if (::InterlockedCompareExchange(&op->ready_, 1, 0) == 1)
  490. {
  491. // Ensure the count of outstanding work is decremented on block exit.
  492. work_finished_on_block_exit on_exit = { this };
  493. (void)on_exit;
  494. op->complete(*this, result_ec, bytes_transferred);
  495. ec = asio::error_code();
  496. return 1;
  497. }
  498. }
  499. else if (completion_key == transfer_timer_dispatching)
  500. {
  501. // Woken up to try to acquire responsibility for dispatching timers.
  502. ::InterlockedCompareExchange(&timer_thread_, 0, this_thread_id);
  503. }
  504. else if (completion_key == steal_timer_dispatching)
  505. {
  506. // Woken up to steal responsibility for dispatching timers.
  507. ::InterlockedExchange(&timer_thread_, 0);
  508. }
  509. else
  510. {
  511. // Relinquish responsibility for dispatching timers. If the io_service
  512. // is not being stopped then the thread will get an opportunity to
  513. // reacquire timer responsibility on the next loop iteration.
  514. if (dispatching_timers)
  515. {
  516. ::InterlockedCompareExchange(&timer_thread_, 0, this_thread_id);
  517. }
  518. // The stopped_ flag is always checked to ensure that any leftover
  519. // interrupts from a previous run invocation are ignored.
  520. if (::InterlockedExchangeAdd(&stopped_, 0) != 0)
  521. {
  522. // Wake up next thread that is blocked on GetQueuedCompletionStatus.
  523. if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, 0))
  524. {
  525. last_error = ::GetLastError();
  526. ec = asio::error_code(last_error,
  527. asio::error::get_system_category());
  528. return 0;
  529. }
  530. ec = asio::error_code();
  531. return 0;
  532. }
  533. }
  534. }
  535. }
  536. // Get the timeout value for the GetQueuedCompletionStatus call. The timeout
  537. // value is returned as a number of milliseconds. We will wait no longer than
  538. // 1000 milliseconds.
  539. DWORD get_timeout()
  540. {
  541. return timer_queues_.wait_duration_msec(max_timeout);
  542. }
  543. // Helper class to call work_finished() on block exit.
  544. struct work_finished_on_block_exit
  545. {
  546. ~work_finished_on_block_exit()
  547. {
  548. io_service_->work_finished();
  549. }
  550. win_iocp_io_service* io_service_;
  551. };
  552. // The IO completion port used for queueing operations.
  553. struct iocp_holder
  554. {
  555. HANDLE handle;
  556. iocp_holder() : handle(0) {}
  557. ~iocp_holder() { if (handle) ::CloseHandle(handle); }
  558. } iocp_;
  559. // The count of unfinished work.
  560. long outstanding_work_;
  561. // Flag to indicate whether the event loop has been stopped.
  562. long stopped_;
  563. // Flag to indicate whether the service has been shut down.
  564. long shutdown_;
  565. enum
  566. {
  567. // Maximum GetQueuedCompletionStatus timeout, in milliseconds.
  568. max_timeout = 500,
  569. // Completion key value to indicate that responsibility for dispatching
  570. // timers is being cooperatively transferred from one thread to another.
  571. transfer_timer_dispatching = 1,
  572. // Completion key value to indicate that responsibility for dispatching
  573. // timers should be stolen from another thread.
  574. steal_timer_dispatching = 2,
  575. // Completion key value to indicate that an operation has posted with the
  576. // original last_error and bytes_transferred values stored in the fields of
  577. // the OVERLAPPED structure.
  578. overlapped_contains_result = 3
  579. };
  580. // The thread that's currently in charge of dispatching timers.
  581. long timer_thread_;
  582. // Mutex for protecting access to the timer queues.
  583. mutex timer_mutex_;
  584. // Whether a thread has been interrupted to process a new timeout.
  585. bool timer_interrupt_issued_;
  586. // The timer queues.
  587. timer_queue_set timer_queues_;
  588. // The operations that are ready to dispatch.
  589. op_queue<operation> completed_ops_;
  590. };
  591. } // namespace detail
  592. } // namespace asio
  593. #endif // defined(ASIO_HAS_IOCP)
  594. #include "asio/detail/pop_options.hpp"
  595. #endif // ASIO_DETAIL_WIN_IOCP_IO_SERVICE_HPP