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

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

http://hadesmem.googlecode.com/
C++ Header | 592 lines | 467 code | 91 blank | 34 comment | 110 complexity | 0aae66e6182200174514a7560dd6dee0 MD5 | raw file
  1//
  2// detail/impl/signal_set_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_SIGNAL_SET_SERVICE_IPP
 12#define BOOST_ASIO_DETAIL_IMPL_SIGNAL_SET_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#include <cstring>
 21#include <boost/asio/detail/reactor.hpp>
 22#include <boost/asio/detail/signal_blocker.hpp>
 23#include <boost/asio/detail/signal_set_service.hpp>
 24#include <boost/asio/detail/static_mutex.hpp>
 25
 26#include <boost/asio/detail/push_options.hpp>
 27
 28namespace boost {
 29namespace asio {
 30namespace detail {
 31
 32struct signal_state
 33{
 34  // Mutex used for protecting global state.
 35  static_mutex mutex_;
 36
 37  // The read end of the pipe used for signal notifications.
 38  int read_descriptor_;
 39
 40  // The write end of the pipe used for signal notifications.
 41  int write_descriptor_;
 42
 43  // Whether the signal state has been prepared for a fork.
 44  bool fork_prepared_;
 45
 46  // The head of a linked list of all signal_set_service instances.
 47  class signal_set_service* service_list_;
 48
 49  // A count of the number of objects that are registered for each signal.
 50  std::size_t registration_count_[max_signal_number];
 51};
 52
 53signal_state* get_signal_state()
 54{
 55  static signal_state state = {
 56    BOOST_ASIO_STATIC_MUTEX_INIT, -1, -1, false, 0, { 0 } };
 57  return &state;
 58}
 59
 60void asio_signal_handler(int signal_number)
 61{
 62#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
 63  signal_set_service::deliver_signal(signal_number);
 64#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
 65  int saved_errno = errno;
 66  signal_state* state = get_signal_state();
 67  int result = ::write(state->write_descriptor_,
 68      &signal_number, sizeof(signal_number));
 69  (void)result;
 70  errno = saved_errno;
 71#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
 72
 73#if defined(BOOST_ASIO_HAS_SIGNAL) && !defined(BOOST_ASIO_HAS_SIGACTION)
 74  signal(signal_number, asio_signal_handler);
 75#endif // defined(BOOST_ASIO_HAS_SIGNAL) && !defined(BOOST_ASIO_HAS_SIGACTION)
 76}
 77
 78#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
 79class signal_set_service::pipe_read_op : public reactor_op
 80{
 81public:
 82  pipe_read_op()
 83    : reactor_op(&pipe_read_op::do_perform, pipe_read_op::do_complete)
 84  {
 85  }
 86
 87  static bool do_perform(reactor_op*)
 88  {
 89    signal_state* state = get_signal_state();
 90
 91    int fd = state->read_descriptor_;
 92    int signal_number = 0;
 93    while (::read(fd, &signal_number, sizeof(int)) == sizeof(int))
 94      if (signal_number >= 0 && signal_number < max_signal_number)
 95        signal_set_service::deliver_signal(signal_number);
 96
 97    return false;
 98  }
 99
100  static void do_complete(io_service_impl* /*owner*/, operation* base,
101      boost::system::error_code /*ec*/, std::size_t /*bytes_transferred*/)
102  {
103    pipe_read_op* o(static_cast<pipe_read_op*>(base));
104    delete o;
105  }
106};
107#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
108
109signal_set_service::signal_set_service(
110    boost::asio::io_service& io_service)
111  : io_service_(boost::asio::use_service<io_service_impl>(io_service)),
112#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
113    reactor_(boost::asio::use_service<reactor>(io_service)),
114#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
115    next_(0),
116    prev_(0)
117{
118  get_signal_state()->mutex_.init();
119
120#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
121  reactor_.init_task();
122#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
123
124  for (int i = 0; i < max_signal_number; ++i)
125    registrations_[i] = 0;
126
127  add_service(this);
128}
129
130signal_set_service::~signal_set_service()
131{
132  remove_service(this);
133}
134
135void signal_set_service::shutdown_service()
136{
137  remove_service(this);
138
139  op_queue<operation> ops;
140
141  for (int i = 0; i < max_signal_number; ++i)
142  {
143    registration* reg = registrations_[i];
144    while (reg)
145    {
146      ops.push(*reg->queue_);
147      reg = reg->next_in_table_;
148    }
149  }
150
151  io_service_.abandon_operations(ops);
152}
153
154void signal_set_service::fork_service(
155    boost::asio::io_service::fork_event fork_ev)
156{
157#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
158  signal_state* state = get_signal_state();
159  static_mutex::scoped_lock lock(state->mutex_);
160
161  switch (fork_ev)
162  {
163  case boost::asio::io_service::fork_prepare:
164    reactor_.deregister_internal_descriptor(
165        state->read_descriptor_, reactor_data_);
166    state->fork_prepared_ = true;
167    break;
168  case boost::asio::io_service::fork_parent:
169    state->fork_prepared_ = false;
170    reactor_.register_internal_descriptor(reactor::read_op,
171        state->read_descriptor_, reactor_data_, new pipe_read_op);
172    break;
173  case boost::asio::io_service::fork_child:
174    if (state->fork_prepared_)
175    {
176      boost::asio::detail::signal_blocker blocker;
177      close_descriptors();
178      open_descriptors();
179      state->fork_prepared_ = false;
180    }
181    reactor_.register_internal_descriptor(reactor::read_op,
182        state->read_descriptor_, reactor_data_, new pipe_read_op);
183    break;
184  default:
185    break;
186  }
187#else // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
188  (void)fork_ev;
189#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
190}
191
192void signal_set_service::construct(
193    signal_set_service::implementation_type& impl)
194{
195  impl.signals_ = 0;
196}
197
198void signal_set_service::destroy(
199    signal_set_service::implementation_type& impl)
200{
201  boost::system::error_code ignored_ec;
202  clear(impl, ignored_ec);
203  cancel(impl, ignored_ec);
204}
205
206boost::system::error_code signal_set_service::add(
207    signal_set_service::implementation_type& impl,
208    int signal_number, boost::system::error_code& ec)
209{
210  // Check that the signal number is valid.
211  if (signal_number < 0 || signal_number > max_signal_number)
212  {
213    ec = boost::asio::error::invalid_argument;
214    return ec;
215  }
216
217  signal_state* state = get_signal_state();
218  static_mutex::scoped_lock lock(state->mutex_);
219
220  // Find the appropriate place to insert the registration.
221  registration** insertion_point = &impl.signals_;
222  registration* next = impl.signals_;
223  while (next && next->signal_number_ < signal_number)
224  {
225    insertion_point = &next->next_in_set_;
226    next = next->next_in_set_;
227  }
228
229  // Only do something if the signal is not already registered.
230  if (next == 0 || next->signal_number_ != signal_number)
231  {
232    registration* new_registration = new registration;
233
234#if defined(BOOST_ASIO_HAS_SIGNAL) || defined(BOOST_ASIO_HAS_SIGACTION)
235    // Register for the signal if we're the first.
236    if (state->registration_count_[signal_number] == 0)
237    {
238# if defined(BOOST_ASIO_HAS_SIGACTION)
239      using namespace std; // For memset.
240      struct sigaction sa;
241      memset(&sa, 0, sizeof(sa));
242      sa.sa_handler = asio_signal_handler;
243      sigfillset(&sa.sa_mask);
244      if (::sigaction(signal_number, &sa, 0) == -1)
245# else // defined(BOOST_ASIO_HAS_SIGACTION)
246      if (::signal(signal_number, asio_signal_handler) == SIG_ERR)
247# endif // defined(BOOST_ASIO_HAS_SIGACTION)
248      {
249# if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
250        ec = boost::asio::error::invalid_argument;
251# else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
252        ec = boost::system::error_code(errno,
253            boost::asio::error::get_system_category());
254# endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
255        delete new_registration;
256        return ec;
257      }
258    }
259#endif // defined(BOOST_ASIO_HAS_SIGNAL) || defined(BOOST_ASIO_HAS_SIGACTION)
260
261    // Record the new registration in the set.
262    new_registration->signal_number_ = signal_number;
263    new_registration->queue_ = &impl.queue_;
264    new_registration->next_in_set_ = next;
265    *insertion_point = new_registration;
266
267    // Insert registration into the registration table.
268    new_registration->next_in_table_ = registrations_[signal_number];
269    if (registrations_[signal_number])
270      registrations_[signal_number]->prev_in_table_ = new_registration;
271    registrations_[signal_number] = new_registration;
272
273    ++state->registration_count_[signal_number];
274  }
275
276  ec = boost::system::error_code();
277  return ec;
278}
279
280boost::system::error_code signal_set_service::remove(
281    signal_set_service::implementation_type& impl,
282    int signal_number, boost::system::error_code& ec)
283{
284  // Check that the signal number is valid.
285  if (signal_number < 0 || signal_number > max_signal_number)
286  {
287    ec = boost::asio::error::invalid_argument;
288    return ec;
289  }
290
291  signal_state* state = get_signal_state();
292  static_mutex::scoped_lock lock(state->mutex_);
293
294  // Find the signal number in the list of registrations.
295  registration** deletion_point = &impl.signals_;
296  registration* reg = impl.signals_;
297  while (reg && reg->signal_number_ < signal_number)
298  {
299    deletion_point = &reg->next_in_set_;
300    reg = reg->next_in_set_;
301  }
302
303  if (reg != 0 && reg->signal_number_ == signal_number)
304  {
305#if defined(BOOST_ASIO_HAS_SIGNAL) || defined(BOOST_ASIO_HAS_SIGACTION)
306    // Set signal handler back to the default if we're the last.
307    if (state->registration_count_[signal_number] == 1)
308    {
309# if defined(BOOST_ASIO_HAS_SIGACTION)
310      using namespace std; // For memset.
311      struct sigaction sa;
312      memset(&sa, 0, sizeof(sa));
313      sa.sa_handler = SIG_DFL;
314      if (::sigaction(signal_number, &sa, 0) == -1)
315# else // defined(BOOST_ASIO_HAS_SIGACTION)
316      if (::signal(signal_number, SIG_DFL) == SIG_ERR)
317# endif // defined(BOOST_ASIO_HAS_SIGACTION)
318      {
319# if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
320        ec = boost::asio::error::invalid_argument;
321# else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
322        ec = boost::system::error_code(errno,
323            boost::asio::error::get_system_category());
324# endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
325        return ec;
326      }
327    }
328#endif // defined(BOOST_ASIO_HAS_SIGNAL) || defined(BOOST_ASIO_HAS_SIGACTION)
329
330    // Remove the registration from the set.
331    *deletion_point = reg->next_in_set_;
332
333    // Remove the registration from the registration table.
334    if (registrations_[signal_number] == reg)
335      registrations_[signal_number] = reg->next_in_table_;
336    if (reg->prev_in_table_)
337      reg->prev_in_table_->next_in_table_ = reg->next_in_table_;
338    if (reg->next_in_table_)
339      reg->next_in_table_->prev_in_table_ = reg->prev_in_table_;
340
341    --state->registration_count_[signal_number];
342
343    delete reg;
344  }
345
346  ec = boost::system::error_code();
347  return ec;
348}
349
350boost::system::error_code signal_set_service::clear(
351    signal_set_service::implementation_type& impl,
352    boost::system::error_code& ec)
353{
354  signal_state* state = get_signal_state();
355  static_mutex::scoped_lock lock(state->mutex_);
356
357  while (registration* reg = impl.signals_)
358  {
359#if defined(BOOST_ASIO_HAS_SIGNAL) || defined(BOOST_ASIO_HAS_SIGACTION)
360    // Set signal handler back to the default if we're the last.
361    if (state->registration_count_[reg->signal_number_] == 1)
362    {
363# if defined(BOOST_ASIO_HAS_SIGACTION)
364      using namespace std; // For memset.
365      struct sigaction sa;
366      memset(&sa, 0, sizeof(sa));
367      sa.sa_handler = SIG_DFL;
368      if (::sigaction(reg->signal_number_, &sa, 0) == -1)
369# else // defined(BOOST_ASIO_HAS_SIGACTION)
370      if (::signal(reg->signal_number_, SIG_DFL) == SIG_ERR)
371# endif // defined(BOOST_ASIO_HAS_SIGACTION)
372      {
373# if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
374        ec = boost::asio::error::invalid_argument;
375# else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
376        ec = boost::system::error_code(errno,
377            boost::asio::error::get_system_category());
378# endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
379        return ec;
380      }
381    }
382#endif // defined(BOOST_ASIO_HAS_SIGNAL) || defined(BOOST_ASIO_HAS_SIGACTION)
383
384    // Remove the registration from the registration table.
385    if (registrations_[reg->signal_number_] == reg)
386      registrations_[reg->signal_number_] = reg->next_in_table_;
387    if (reg->prev_in_table_)
388      reg->prev_in_table_->next_in_table_ = reg->next_in_table_;
389    if (reg->next_in_table_)
390      reg->next_in_table_->prev_in_table_ = reg->prev_in_table_;
391
392    --state->registration_count_[reg->signal_number_];
393
394    impl.signals_ = reg->next_in_set_;
395    delete reg;
396  }
397
398  ec = boost::system::error_code();
399  return ec;
400}
401
402boost::system::error_code signal_set_service::cancel(
403    signal_set_service::implementation_type& impl,
404    boost::system::error_code& ec)
405{
406  BOOST_ASIO_HANDLER_OPERATION(("signal_set", &impl, "cancel"));
407
408  op_queue<operation> ops;
409  {
410    signal_state* state = get_signal_state();
411    static_mutex::scoped_lock lock(state->mutex_);
412
413    while (signal_op* op = impl.queue_.front())
414    {
415      op->ec_ = boost::asio::error::operation_aborted;
416      impl.queue_.pop();
417      ops.push(op);
418    }
419  }
420
421  io_service_.post_deferred_completions(ops);
422
423  ec = boost::system::error_code();
424  return ec;
425}
426
427void signal_set_service::deliver_signal(int signal_number)
428{
429  signal_state* state = get_signal_state();
430  static_mutex::scoped_lock lock(state->mutex_);
431
432  signal_set_service* service = state->service_list_;
433  while (service)
434  {
435    op_queue<operation> ops;
436
437    registration* reg = service->registrations_[signal_number];
438    while (reg)
439    {
440      if (reg->queue_->empty())
441      {
442        ++reg->undelivered_;
443      }
444      else
445      {
446        while (signal_op* op = reg->queue_->front())
447        {
448          op->signal_number_ = signal_number;
449          reg->queue_->pop();
450          ops.push(op);
451        }
452      }
453
454      reg = reg->next_in_table_;
455    }
456
457    service->io_service_.post_deferred_completions(ops);
458
459    service = service->next_;
460  }
461}
462
463void signal_set_service::add_service(signal_set_service* service)
464{
465  signal_state* state = get_signal_state();
466  static_mutex::scoped_lock lock(state->mutex_);
467
468#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
469  // If this is the first service to be created, open a new pipe.
470  if (state->service_list_ == 0)
471    open_descriptors();
472#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
473
474  // Insert service into linked list of all services.
475  service->next_ = state->service_list_;
476  service->prev_ = 0;
477  if (state->service_list_)
478    state->service_list_->prev_ = service;
479  state->service_list_ = service;
480
481#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
482  // Register for pipe readiness notifications.
483  service->reactor_.register_internal_descriptor(reactor::read_op,
484      state->read_descriptor_, service->reactor_data_, new pipe_read_op);
485#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
486}
487
488void signal_set_service::remove_service(signal_set_service* service)
489{
490  signal_state* state = get_signal_state();
491  static_mutex::scoped_lock lock(state->mutex_);
492
493  if (service->next_ || service->prev_ || state->service_list_ == service)
494  {
495#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
496    // Disable the pipe readiness notifications.
497    service->reactor_.deregister_descriptor(
498        state->read_descriptor_, service->reactor_data_, false);
499#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
500
501    // Remove service from linked list of all services.
502    if (state->service_list_ == service)
503      state->service_list_ = service->next_;
504    if (service->prev_)
505      service->prev_->next_ = service->next_;
506    if (service->next_)
507      service->next_->prev_= service->prev_;
508    service->next_ = 0;
509    service->prev_ = 0;
510
511#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
512    // If this is the last service to be removed, close the pipe.
513    if (state->service_list_ == 0)
514      close_descriptors();
515#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
516  }
517}
518
519void signal_set_service::open_descriptors()
520{
521#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
522  signal_state* state = get_signal_state();
523
524  int pipe_fds[2];
525  if (::pipe(pipe_fds) == 0)
526  {
527    state->read_descriptor_ = pipe_fds[0];
528    ::fcntl(state->read_descriptor_, F_SETFL, O_NONBLOCK);
529
530    state->write_descriptor_ = pipe_fds[1];
531    ::fcntl(state->write_descriptor_, F_SETFL, O_NONBLOCK);
532
533#if defined(FD_CLOEXEC)
534    ::fcntl(state->read_descriptor_, F_SETFD, FD_CLOEXEC);
535    ::fcntl(state->write_descriptor_, F_SETFD, FD_CLOEXEC);
536#endif // defined(FD_CLOEXEC)
537  }
538  else
539  {
540    boost::system::error_code ec(errno,
541        boost::asio::error::get_system_category());
542    boost::asio::detail::throw_error(ec, "signal_set_service pipe");
543  }
544#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
545}
546
547void signal_set_service::close_descriptors()
548{
549#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
550  signal_state* state = get_signal_state();
551
552  if (state->read_descriptor_ != -1)
553    ::close(state->read_descriptor_);
554  state->read_descriptor_ = -1;
555
556  if (state->write_descriptor_ != -1)
557    ::close(state->write_descriptor_);
558  state->write_descriptor_ = -1;
559#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
560}
561
562void signal_set_service::start_wait_op(
563    signal_set_service::implementation_type& impl, signal_op* op)
564{
565  io_service_.work_started();
566
567  signal_state* state = get_signal_state();
568  static_mutex::scoped_lock lock(state->mutex_);
569
570  registration* reg = impl.signals_;
571  while (reg)
572  {
573    if (reg->undelivered_ > 0)
574    {
575      --reg->undelivered_;
576      io_service_.post_deferred_completion(op);
577      return;
578    }
579
580    reg = reg->next_in_set_;
581  }
582
583  impl.queue_.push(op);
584}
585
586} // namespace detail
587} // namespace asio
588} // namespace boost
589
590#include <boost/asio/detail/pop_options.hpp>
591
592#endif // BOOST_ASIO_DETAIL_IMPL_SIGNAL_SET_SERVICE_IPP