PageRenderTime 14ms CodeModel.GetById 1ms app.highlight 6ms RepoModel.GetById 1ms app.codeStats 0ms

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

http://hadesmem.googlecode.com/
C++ Header | 518 lines | 414 code | 77 blank | 27 comment | 62 complexity | 5e454f7e54e6aa44e7780ea918b0ce29 MD5 | raw file
  1//
  2// detail/impl/kqueue_reactor.ipp
  3// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  4//
  5// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
  6// Copyright (c) 2005 Stefan Arentz (stefan at soze dot 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
 12#ifndef BOOST_ASIO_DETAIL_IMPL_KQUEUE_REACTOR_IPP
 13#define BOOST_ASIO_DETAIL_IMPL_KQUEUE_REACTOR_IPP
 14
 15#if defined(_MSC_VER) && (_MSC_VER >= 1200)
 16# pragma once
 17#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
 18
 19#include <boost/asio/detail/config.hpp>
 20
 21#if defined(BOOST_ASIO_HAS_KQUEUE)
 22
 23#include <boost/asio/detail/kqueue_reactor.hpp>
 24#include <boost/asio/detail/throw_error.hpp>
 25#include <boost/asio/error.hpp>
 26
 27#include <boost/asio/detail/push_options.hpp>
 28
 29#if defined(__NetBSD__)
 30# define BOOST_ASIO_KQUEUE_EV_SET(ev, ident, filt, flags, fflags, data, udata) \
 31    EV_SET(ev, ident, filt, flags, fflags, \
 32      data, reinterpret_cast<intptr_t>(udata))
 33#else
 34# define BOOST_ASIO_KQUEUE_EV_SET(ev, ident, filt, flags, fflags, data, udata) \
 35    EV_SET(ev, ident, filt, flags, fflags, data, udata)
 36#endif
 37
 38namespace boost {
 39namespace asio {
 40namespace detail {
 41
 42kqueue_reactor::kqueue_reactor(boost::asio::io_service& io_service)
 43  : boost::asio::detail::service_base<kqueue_reactor>(io_service),
 44    io_service_(use_service<io_service_impl>(io_service)),
 45    mutex_(),
 46    kqueue_fd_(do_kqueue_create()),
 47    interrupter_(),
 48    shutdown_(false)
 49{
 50  // The interrupter is put into a permanently readable state. Whenever we want
 51  // to interrupt the blocked kevent call we register a read operation against
 52  // the descriptor.
 53  interrupter_.interrupt();
 54}
 55
 56kqueue_reactor::~kqueue_reactor()
 57{
 58  close(kqueue_fd_);
 59}
 60
 61void kqueue_reactor::shutdown_service()
 62{
 63  mutex::scoped_lock lock(mutex_);
 64  shutdown_ = true;
 65  lock.unlock();
 66
 67  op_queue<operation> ops;
 68
 69  while (descriptor_state* state = registered_descriptors_.first())
 70  {
 71    for (int i = 0; i < max_ops; ++i)
 72      ops.push(state->op_queue_[i]);
 73    state->shutdown_ = true;
 74    registered_descriptors_.free(state);
 75  }
 76
 77  timer_queues_.get_all_timers(ops);
 78
 79  io_service_.abandon_operations(ops);
 80}
 81
 82void kqueue_reactor::fork_service(boost::asio::io_service::fork_event fork_ev)
 83{
 84  if (fork_ev == boost::asio::io_service::fork_child)
 85  {
 86    // The kqueue descriptor is automatically closed in the child.
 87    kqueue_fd_ = -1;
 88    kqueue_fd_ = do_kqueue_create();
 89
 90    interrupter_.recreate();
 91
 92    // Re-register all descriptors with kqueue.
 93    mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_);
 94    for (descriptor_state* state = registered_descriptors_.first();
 95        state != 0; state = state->next_)
 96    {
 97      struct kevent events[2];
 98      int num_events = 0;
 99
100      if (!state->op_queue_[read_op].empty())
101        BOOST_ASIO_KQUEUE_EV_SET(&events[num_events++], state->descriptor_,
102            EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, state);
103      else if (!state->op_queue_[except_op].empty())
104        BOOST_ASIO_KQUEUE_EV_SET(&events[num_events++], state->descriptor_,
105            EVFILT_READ, EV_ADD | EV_CLEAR, EV_OOBAND, 0, state);
106
107      if (!state->op_queue_[write_op].empty())
108        BOOST_ASIO_KQUEUE_EV_SET(&events[num_events++], state->descriptor_,
109            EVFILT_WRITE, EV_ADD | EV_CLEAR, 0, 0, state);
110
111      if (num_events && ::kevent(kqueue_fd_, events, num_events, 0, 0, 0) == -1)
112      {
113        boost::system::error_code error(errno,
114            boost::asio::error::get_system_category());
115        boost::asio::detail::throw_error(error);
116      }
117    }
118  }
119}
120
121void kqueue_reactor::init_task()
122{
123  io_service_.init_task();
124}
125
126int kqueue_reactor::register_descriptor(socket_type descriptor,
127    kqueue_reactor::per_descriptor_data& descriptor_data)
128{
129  mutex::scoped_lock lock(registered_descriptors_mutex_);
130
131  descriptor_data = registered_descriptors_.alloc();
132  descriptor_data->descriptor_ = descriptor;
133  descriptor_data->shutdown_ = false;
134
135  return 0;
136}
137
138int kqueue_reactor::register_internal_descriptor(
139    int op_type, socket_type descriptor,
140    kqueue_reactor::per_descriptor_data& descriptor_data, reactor_op* op)
141{
142  mutex::scoped_lock lock(registered_descriptors_mutex_);
143
144  descriptor_data = registered_descriptors_.alloc();
145  descriptor_data->descriptor_ = descriptor;
146  descriptor_data->shutdown_ = false;
147  descriptor_data->op_queue_[op_type].push(op);
148
149  struct kevent event;
150  switch (op_type)
151  {
152  case read_op:
153    BOOST_ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_READ,
154        EV_ADD | EV_CLEAR, 0, 0, descriptor_data);
155    break;
156  case write_op:
157    BOOST_ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_WRITE,
158        EV_ADD | EV_CLEAR, 0, 0, descriptor_data);
159    break;
160  case except_op:
161    BOOST_ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_READ,
162        EV_ADD | EV_CLEAR, EV_OOBAND, 0, descriptor_data);
163    break;
164  }
165  ::kevent(kqueue_fd_, &event, 1, 0, 0, 0);
166
167  return 0;
168}
169
170void kqueue_reactor::move_descriptor(socket_type,
171    kqueue_reactor::per_descriptor_data& target_descriptor_data,
172    kqueue_reactor::per_descriptor_data& source_descriptor_data)
173{
174  target_descriptor_data = source_descriptor_data;
175  source_descriptor_data = 0;
176}
177
178void kqueue_reactor::start_op(int op_type, socket_type descriptor,
179    kqueue_reactor::per_descriptor_data& descriptor_data,
180    reactor_op* op, bool allow_speculative)
181{
182  if (!descriptor_data)
183  {
184    op->ec_ = boost::asio::error::bad_descriptor;
185    post_immediate_completion(op);
186    return;
187  }
188
189  mutex::scoped_lock descriptor_lock(descriptor_data->mutex_);
190
191  if (descriptor_data->shutdown_)
192  {
193    post_immediate_completion(op);
194    return;
195  }
196
197  bool first = descriptor_data->op_queue_[op_type].empty();
198  if (first)
199  {
200    if (allow_speculative)
201    {
202      if (op_type != read_op || descriptor_data->op_queue_[except_op].empty())
203      {
204        if (op->perform())
205        {
206          descriptor_lock.unlock();
207          io_service_.post_immediate_completion(op);
208          return;
209        }
210      }
211    }
212  }
213
214  descriptor_data->op_queue_[op_type].push(op);
215  io_service_.work_started();
216
217  if (first)
218  {
219    struct kevent event;
220    switch (op_type)
221    {
222    case read_op:
223      BOOST_ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_READ,
224          EV_ADD | EV_CLEAR, 0, 0, descriptor_data);
225      break;
226    case write_op:
227      BOOST_ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_WRITE,
228          EV_ADD | EV_CLEAR, 0, 0, descriptor_data);
229      break;
230    case except_op:
231      if (!descriptor_data->op_queue_[read_op].empty())
232        return; // Already registered for read events.
233      BOOST_ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_READ,
234          EV_ADD | EV_CLEAR, EV_OOBAND, 0, descriptor_data);
235      break;
236    }
237
238    if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1)
239    {
240      op->ec_ = boost::system::error_code(errno,
241          boost::asio::error::get_system_category());
242      descriptor_data->op_queue_[op_type].pop();
243      io_service_.post_deferred_completion(op);
244    }
245  }
246}
247
248void kqueue_reactor::cancel_ops(socket_type,
249    kqueue_reactor::per_descriptor_data& descriptor_data)
250{
251  if (!descriptor_data)
252    return;
253
254  mutex::scoped_lock descriptor_lock(descriptor_data->mutex_);
255
256  op_queue<operation> ops;
257  for (int i = 0; i < max_ops; ++i)
258  {
259    while (reactor_op* op = descriptor_data->op_queue_[i].front())
260    {
261      op->ec_ = boost::asio::error::operation_aborted;
262      descriptor_data->op_queue_[i].pop();
263      ops.push(op);
264    }
265  }
266
267  descriptor_lock.unlock();
268
269  io_service_.post_deferred_completions(ops);
270}
271
272void kqueue_reactor::deregister_descriptor(socket_type descriptor,
273    kqueue_reactor::per_descriptor_data& descriptor_data, bool closing)
274{
275  if (!descriptor_data)
276    return;
277
278  mutex::scoped_lock descriptor_lock(descriptor_data->mutex_);
279  mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_);
280
281  if (!descriptor_data->shutdown_)
282  {
283    if (closing)
284    {
285      // The descriptor will be automatically removed from the kqueue when it
286      // is closed.
287    }
288    else
289    {
290      struct kevent events[2];
291      BOOST_ASIO_KQUEUE_EV_SET(&events[0], descriptor,
292          EVFILT_READ, EV_DELETE, 0, 0, 0);
293      BOOST_ASIO_KQUEUE_EV_SET(&events[1], descriptor,
294          EVFILT_WRITE, EV_DELETE, 0, 0, 0);
295      ::kevent(kqueue_fd_, events, 2, 0, 0, 0);
296    }
297
298    op_queue<operation> ops;
299    for (int i = 0; i < max_ops; ++i)
300    {
301      while (reactor_op* op = descriptor_data->op_queue_[i].front())
302      {
303        op->ec_ = boost::asio::error::operation_aborted;
304        descriptor_data->op_queue_[i].pop();
305        ops.push(op);
306      }
307    }
308
309    descriptor_data->descriptor_ = -1;
310    descriptor_data->shutdown_ = true;
311
312    descriptor_lock.unlock();
313
314    registered_descriptors_.free(descriptor_data);
315    descriptor_data = 0;
316
317    descriptors_lock.unlock();
318
319    io_service_.post_deferred_completions(ops);
320  }
321}
322
323void kqueue_reactor::deregister_internal_descriptor(socket_type descriptor,
324    kqueue_reactor::per_descriptor_data& descriptor_data)
325{
326  if (!descriptor_data)
327    return;
328
329  mutex::scoped_lock descriptor_lock(descriptor_data->mutex_);
330  mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_);
331
332  if (!descriptor_data->shutdown_)
333  {
334    struct kevent events[2];
335    BOOST_ASIO_KQUEUE_EV_SET(&events[0], descriptor,
336        EVFILT_READ, EV_DELETE, 0, 0, 0);
337    BOOST_ASIO_KQUEUE_EV_SET(&events[1], descriptor,
338        EVFILT_WRITE, EV_DELETE, 0, 0, 0);
339    ::kevent(kqueue_fd_, events, 2, 0, 0, 0);
340
341    op_queue<operation> ops;
342    for (int i = 0; i < max_ops; ++i)
343      ops.push(descriptor_data->op_queue_[i]);
344
345    descriptor_data->descriptor_ = -1;
346    descriptor_data->shutdown_ = true;
347
348    descriptor_lock.unlock();
349
350    registered_descriptors_.free(descriptor_data);
351    descriptor_data = 0;
352
353    descriptors_lock.unlock();
354  }
355}
356
357void kqueue_reactor::run(bool block, op_queue<operation>& ops)
358{
359  mutex::scoped_lock lock(mutex_);
360
361  // Determine how long to block while waiting for events.
362  timespec timeout_buf = { 0, 0 };
363  timespec* timeout = block ? get_timeout(timeout_buf) : &timeout_buf;
364
365  lock.unlock();
366
367  // Block on the kqueue descriptor.
368  struct kevent events[128];
369  int num_events = kevent(kqueue_fd_, 0, 0, events, 128, timeout);
370
371  // Dispatch the waiting events.
372  for (int i = 0; i < num_events; ++i)
373  {
374    int descriptor = events[i].ident;
375    void* ptr = reinterpret_cast<void*>(events[i].udata);
376    if (ptr == &interrupter_)
377    {
378      // No need to reset the interrupter since we're leaving the descriptor
379      // in a ready-to-read state and relying on edge-triggered notifications.
380    }
381    else
382    {
383      descriptor_state* descriptor_data = static_cast<descriptor_state*>(ptr);
384      mutex::scoped_lock descriptor_lock(descriptor_data->mutex_);
385
386      // Exception operations must be processed first to ensure that any
387      // out-of-band data is read before normal data.
388#if defined(__NetBSD__)
389      static const unsigned int filter[max_ops] =
390#else
391      static const int filter[max_ops] =
392#endif
393        { EVFILT_READ, EVFILT_WRITE, EVFILT_READ };
394      for (int j = max_ops - 1; j >= 0; --j)
395      {
396        if (events[i].filter == filter[j])
397        {
398          if (j != except_op || events[i].flags & EV_OOBAND)
399          {
400            while (reactor_op* op = descriptor_data->op_queue_[j].front())
401            {
402              if (events[i].flags & EV_ERROR)
403              {
404                op->ec_ = boost::system::error_code(events[i].data,
405                    boost::asio::error::get_system_category());
406                descriptor_data->op_queue_[j].pop();
407                ops.push(op);
408              }
409              if (op->perform())
410              {
411                descriptor_data->op_queue_[j].pop();
412                ops.push(op);
413              }
414              else
415                break;
416            }
417          }
418        }
419      }
420
421      // Renew registration for event notifications.
422      struct kevent event;
423      switch (events[i].filter)
424      {
425      case EVFILT_READ:
426        if (!descriptor_data->op_queue_[read_op].empty())
427          BOOST_ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_READ,
428              EV_ADD | EV_CLEAR, 0, 0, descriptor_data);
429        else if (!descriptor_data->op_queue_[except_op].empty())
430          BOOST_ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_READ,
431              EV_ADD | EV_CLEAR, EV_OOBAND, 0, descriptor_data);
432        else
433          continue;
434        break;
435      case EVFILT_WRITE:
436        if (!descriptor_data->op_queue_[write_op].empty())
437          BOOST_ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_WRITE,
438              EV_ADD | EV_CLEAR, 0, 0, descriptor_data);
439        else
440          continue;
441        break;
442      default:
443        break;
444      }
445      if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1)
446      {
447        boost::system::error_code error(errno,
448            boost::asio::error::get_system_category());
449        for (int j = 0; j < max_ops; ++j)
450        {
451          while (reactor_op* op = descriptor_data->op_queue_[j].front())
452          {
453            op->ec_ = error;
454            descriptor_data->op_queue_[j].pop();
455            ops.push(op);
456          }
457        }
458      }
459    }
460  }
461
462  lock.lock();
463  timer_queues_.get_ready_timers(ops);
464}
465
466void kqueue_reactor::interrupt()
467{
468  struct kevent event;
469  BOOST_ASIO_KQUEUE_EV_SET(&event, interrupter_.read_descriptor(),
470      EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, &interrupter_);
471  ::kevent(kqueue_fd_, &event, 1, 0, 0, 0);
472}
473
474int kqueue_reactor::do_kqueue_create()
475{
476  int fd = ::kqueue();
477  if (fd == -1)
478  {
479    boost::system::error_code ec(errno,
480        boost::asio::error::get_system_category());
481    boost::asio::detail::throw_error(ec, "kqueue");
482  }
483  return fd;
484}
485
486void kqueue_reactor::do_add_timer_queue(timer_queue_base& queue)
487{
488  mutex::scoped_lock lock(mutex_);
489  timer_queues_.insert(&queue);
490}
491
492void kqueue_reactor::do_remove_timer_queue(timer_queue_base& queue)
493{
494  mutex::scoped_lock lock(mutex_);
495  timer_queues_.erase(&queue);
496}
497
498timespec* kqueue_reactor::get_timeout(timespec& ts)
499{
500  // By default we will wait no longer than 5 minutes. This will ensure that
501  // any changes to the system clock are detected after no longer than this.
502  long usec = timer_queues_.wait_duration_usec(5 * 60 * 1000 * 1000);
503  ts.tv_sec = usec / 1000000;
504  ts.tv_nsec = (usec % 1000000) * 1000;
505  return &ts;
506}
507
508} // namespace detail
509} // namespace asio
510} // namespace boost
511
512#undef BOOST_ASIO_KQUEUE_EV_SET
513
514#include <boost/asio/detail/pop_options.hpp>
515
516#endif // defined(BOOST_ASIO_HAS_KQUEUE)
517
518#endif // BOOST_ASIO_DETAIL_IMPL_KQUEUE_REACTOR_IPP