PageRenderTime 22ms CodeModel.GetById 10ms app.highlight 5ms RepoModel.GetById 1ms app.codeStats 0ms

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