| // |
| // detail/impl/signal_set_service.ipp |
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| // |
| // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) |
| // |
| // Distributed under the Boost Software License, Version 1.0. (See accompanying |
| // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
| // |
| |
| #ifndef BOOST_ASIO_DETAIL_IMPL_SIGNAL_SET_SERVICE_IPP |
| #define BOOST_ASIO_DETAIL_IMPL_SIGNAL_SET_SERVICE_IPP |
| |
| #if defined(_MSC_VER) && (_MSC_VER >= 1200) |
| # pragma once |
| #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) |
| |
| #include <boost/asio/detail/config.hpp> |
| |
| #include <cstring> |
| #include <boost/asio/detail/reactor.hpp> |
| #include <boost/asio/detail/signal_blocker.hpp> |
| #include <boost/asio/detail/signal_set_service.hpp> |
| #include <boost/asio/detail/static_mutex.hpp> |
| |
| #include <boost/asio/detail/push_options.hpp> |
| |
| namespace boost { |
| namespace asio { |
| namespace detail { |
| |
| struct signal_state |
| { |
| // Mutex used for protecting global state. |
| static_mutex mutex_; |
| |
| // The read end of the pipe used for signal notifications. |
| int read_descriptor_; |
| |
| // The write end of the pipe used for signal notifications. |
| int write_descriptor_; |
| |
| // Whether the signal state has been prepared for a fork. |
| bool fork_prepared_; |
| |
| // The head of a linked list of all signal_set_service instances. |
| class signal_set_service* service_list_; |
| |
| // A count of the number of objects that are registered for each signal. |
| std::size_t registration_count_[max_signal_number]; |
| }; |
| |
| signal_state* get_signal_state() |
| { |
| static signal_state state = { |
| BOOST_ASIO_STATIC_MUTEX_INIT, -1, -1, false, 0, { 0 } }; |
| return &state; |
| } |
| |
| void asio_signal_handler(int signal_number) |
| { |
| #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) |
| signal_set_service::deliver_signal(signal_number); |
| #else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) |
| int saved_errno = errno; |
| signal_state* state = get_signal_state(); |
| int result = ::write(state->write_descriptor_, |
| &signal_number, sizeof(signal_number)); |
| (void)result; |
| errno = saved_errno; |
| #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) |
| |
| #if defined(BOOST_ASIO_HAS_SIGNAL) && !defined(BOOST_ASIO_HAS_SIGACTION) |
| signal(signal_number, asio_signal_handler); |
| #endif // defined(BOOST_ASIO_HAS_SIGNAL) && !defined(BOOST_ASIO_HAS_SIGACTION) |
| } |
| |
| #if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) |
| class signal_set_service::pipe_read_op : public reactor_op |
| { |
| public: |
| pipe_read_op() |
| : reactor_op(&pipe_read_op::do_perform, pipe_read_op::do_complete) |
| { |
| } |
| |
| static bool do_perform(reactor_op*) |
| { |
| signal_state* state = get_signal_state(); |
| |
| int fd = state->read_descriptor_; |
| int signal_number = 0; |
| while (::read(fd, &signal_number, sizeof(int)) == sizeof(int)) |
| if (signal_number >= 0 && signal_number < max_signal_number) |
| signal_set_service::deliver_signal(signal_number); |
| |
| return false; |
| } |
| |
| static void do_complete(io_service_impl* /*owner*/, operation* base, |
| boost::system::error_code /*ec*/, std::size_t /*bytes_transferred*/) |
| { |
| pipe_read_op* o(static_cast<pipe_read_op*>(base)); |
| delete o; |
| } |
| }; |
| #endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) |
| |
| signal_set_service::signal_set_service( |
| boost::asio::io_service& io_service) |
| : io_service_(boost::asio::use_service<io_service_impl>(io_service)), |
| #if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) |
| reactor_(boost::asio::use_service<reactor>(io_service)), |
| #endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) |
| next_(0), |
| prev_(0) |
| { |
| get_signal_state()->mutex_.init(); |
| |
| #if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) |
| reactor_.init_task(); |
| #endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) |
| |
| for (int i = 0; i < max_signal_number; ++i) |
| registrations_[i] = 0; |
| |
| add_service(this); |
| } |
| |
| signal_set_service::~signal_set_service() |
| { |
| remove_service(this); |
| } |
| |
| void signal_set_service::shutdown_service() |
| { |
| remove_service(this); |
| |
| op_queue<operation> ops; |
| |
| for (int i = 0; i < max_signal_number; ++i) |
| { |
| registration* reg = registrations_[i]; |
| while (reg) |
| { |
| ops.push(*reg->queue_); |
| reg = reg->next_in_table_; |
| } |
| } |
| |
| io_service_.abandon_operations(ops); |
| } |
| |
| void signal_set_service::fork_service( |
| boost::asio::io_service::fork_event fork_ev) |
| { |
| #if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) |
| signal_state* state = get_signal_state(); |
| static_mutex::scoped_lock lock(state->mutex_); |
| |
| switch (fork_ev) |
| { |
| case boost::asio::io_service::fork_prepare: |
| reactor_.deregister_internal_descriptor( |
| state->read_descriptor_, reactor_data_); |
| state->fork_prepared_ = true; |
| break; |
| case boost::asio::io_service::fork_parent: |
| state->fork_prepared_ = false; |
| reactor_.register_internal_descriptor(reactor::read_op, |
| state->read_descriptor_, reactor_data_, new pipe_read_op); |
| break; |
| case boost::asio::io_service::fork_child: |
| if (state->fork_prepared_) |
| { |
| boost::asio::detail::signal_blocker blocker; |
| close_descriptors(); |
| open_descriptors(); |
| state->fork_prepared_ = false; |
| } |
| reactor_.register_internal_descriptor(reactor::read_op, |
| state->read_descriptor_, reactor_data_, new pipe_read_op); |
| break; |
| default: |
| break; |
| } |
| #else // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) |
| (void)fork_ev; |
| #endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) |
| } |
| |
| void signal_set_service::construct( |
| signal_set_service::implementation_type& impl) |
| { |
| impl.signals_ = 0; |
| } |
| |
| void signal_set_service::destroy( |
| signal_set_service::implementation_type& impl) |
| { |
| boost::system::error_code ignored_ec; |
| clear(impl, ignored_ec); |
| cancel(impl, ignored_ec); |
| } |
| |
| boost::system::error_code signal_set_service::add( |
| signal_set_service::implementation_type& impl, |
| int signal_number, boost::system::error_code& ec) |
| { |
| // Check that the signal number is valid. |
| if (signal_number < 0 || signal_number > max_signal_number) |
| { |
| ec = boost::asio::error::invalid_argument; |
| return ec; |
| } |
| |
| signal_state* state = get_signal_state(); |
| static_mutex::scoped_lock lock(state->mutex_); |
| |
| // Find the appropriate place to insert the registration. |
| registration** insertion_point = &impl.signals_; |
| registration* next = impl.signals_; |
| while (next && next->signal_number_ < signal_number) |
| { |
| insertion_point = &next->next_in_set_; |
| next = next->next_in_set_; |
| } |
| |
| // Only do something if the signal is not already registered. |
| if (next == 0 || next->signal_number_ != signal_number) |
| { |
| registration* new_registration = new registration; |
| |
| #if defined(BOOST_ASIO_HAS_SIGNAL) || defined(BOOST_ASIO_HAS_SIGACTION) |
| // Register for the signal if we're the first. |
| if (state->registration_count_[signal_number] == 0) |
| { |
| # if defined(BOOST_ASIO_HAS_SIGACTION) |
| using namespace std; // For memset. |
| struct sigaction sa; |
| memset(&sa, 0, sizeof(sa)); |
| sa.sa_handler = asio_signal_handler; |
| sigfillset(&sa.sa_mask); |
| if (::sigaction(signal_number, &sa, 0) == -1) |
| # else // defined(BOOST_ASIO_HAS_SIGACTION) |
| if (::signal(signal_number, asio_signal_handler) == SIG_ERR) |
| # endif // defined(BOOST_ASIO_HAS_SIGACTION) |
| { |
| # if defined(BOOST_WINDOWS) || defined(__CYGWIN__) |
| ec = boost::asio::error::invalid_argument; |
| # else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) |
| ec = boost::system::error_code(errno, |
| boost::asio::error::get_system_category()); |
| # endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) |
| delete new_registration; |
| return ec; |
| } |
| } |
| #endif // defined(BOOST_ASIO_HAS_SIGNAL) || defined(BOOST_ASIO_HAS_SIGACTION) |
| |
| // Record the new registration in the set. |
| new_registration->signal_number_ = signal_number; |
| new_registration->queue_ = &impl.queue_; |
| new_registration->next_in_set_ = next; |
| *insertion_point = new_registration; |
| |
| // Insert registration into the registration table. |
| new_registration->next_in_table_ = registrations_[signal_number]; |
| if (registrations_[signal_number]) |
| registrations_[signal_number]->prev_in_table_ = new_registration; |
| registrations_[signal_number] = new_registration; |
| |
| ++state->registration_count_[signal_number]; |
| } |
| |
| ec = boost::system::error_code(); |
| return ec; |
| } |
| |
| boost::system::error_code signal_set_service::remove( |
| signal_set_service::implementation_type& impl, |
| int signal_number, boost::system::error_code& ec) |
| { |
| // Check that the signal number is valid. |
| if (signal_number < 0 || signal_number > max_signal_number) |
| { |
| ec = boost::asio::error::invalid_argument; |
| return ec; |
| } |
| |
| signal_state* state = get_signal_state(); |
| static_mutex::scoped_lock lock(state->mutex_); |
| |
| // Find the signal number in the list of registrations. |
| registration** deletion_point = &impl.signals_; |
| registration* reg = impl.signals_; |
| while (reg && reg->signal_number_ < signal_number) |
| { |
| deletion_point = ®->next_in_set_; |
| reg = reg->next_in_set_; |
| } |
| |
| if (reg != 0 && reg->signal_number_ == signal_number) |
| { |
| #if defined(BOOST_ASIO_HAS_SIGNAL) || defined(BOOST_ASIO_HAS_SIGACTION) |
| // Set signal handler back to the default if we're the last. |
| if (state->registration_count_[signal_number] == 1) |
| { |
| # if defined(BOOST_ASIO_HAS_SIGACTION) |
| using namespace std; // For memset. |
| struct sigaction sa; |
| memset(&sa, 0, sizeof(sa)); |
| sa.sa_handler = SIG_DFL; |
| if (::sigaction(signal_number, &sa, 0) == -1) |
| # else // defined(BOOST_ASIO_HAS_SIGACTION) |
| if (::signal(signal_number, SIG_DFL) == SIG_ERR) |
| # endif // defined(BOOST_ASIO_HAS_SIGACTION) |
| { |
| # if defined(BOOST_WINDOWS) || defined(__CYGWIN__) |
| ec = boost::asio::error::invalid_argument; |
| # else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) |
| ec = boost::system::error_code(errno, |
| boost::asio::error::get_system_category()); |
| # endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) |
| return ec; |
| } |
| } |
| #endif // defined(BOOST_ASIO_HAS_SIGNAL) || defined(BOOST_ASIO_HAS_SIGACTION) |
| |
| // Remove the registration from the set. |
| *deletion_point = reg->next_in_set_; |
| |
| // Remove the registration from the registration table. |
| if (registrations_[signal_number] == reg) |
| registrations_[signal_number] = reg->next_in_table_; |
| if (reg->prev_in_table_) |
| reg->prev_in_table_->next_in_table_ = reg->next_in_table_; |
| if (reg->next_in_table_) |
| reg->next_in_table_->prev_in_table_ = reg->prev_in_table_; |
| |
| --state->registration_count_[signal_number]; |
| |
| delete reg; |
| } |
| |
| ec = boost::system::error_code(); |
| return ec; |
| } |
| |
| boost::system::error_code signal_set_service::clear( |
| signal_set_service::implementation_type& impl, |
| boost::system::error_code& ec) |
| { |
| signal_state* state = get_signal_state(); |
| static_mutex::scoped_lock lock(state->mutex_); |
| |
| while (registration* reg = impl.signals_) |
| { |
| #if defined(BOOST_ASIO_HAS_SIGNAL) || defined(BOOST_ASIO_HAS_SIGACTION) |
| // Set signal handler back to the default if we're the last. |
| if (state->registration_count_[reg->signal_number_] == 1) |
| { |
| # if defined(BOOST_ASIO_HAS_SIGACTION) |
| using namespace std; // For memset. |
| struct sigaction sa; |
| memset(&sa, 0, sizeof(sa)); |
| sa.sa_handler = SIG_DFL; |
| if (::sigaction(reg->signal_number_, &sa, 0) == -1) |
| # else // defined(BOOST_ASIO_HAS_SIGACTION) |
| if (::signal(reg->signal_number_, SIG_DFL) == SIG_ERR) |
| # endif // defined(BOOST_ASIO_HAS_SIGACTION) |
| { |
| # if defined(BOOST_WINDOWS) || defined(__CYGWIN__) |
| ec = boost::asio::error::invalid_argument; |
| # else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) |
| ec = boost::system::error_code(errno, |
| boost::asio::error::get_system_category()); |
| # endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) |
| return ec; |
| } |
| } |
| #endif // defined(BOOST_ASIO_HAS_SIGNAL) || defined(BOOST_ASIO_HAS_SIGACTION) |
| |
| // Remove the registration from the registration table. |
| if (registrations_[reg->signal_number_] == reg) |
| registrations_[reg->signal_number_] = reg->next_in_table_; |
| if (reg->prev_in_table_) |
| reg->prev_in_table_->next_in_table_ = reg->next_in_table_; |
| if (reg->next_in_table_) |
| reg->next_in_table_->prev_in_table_ = reg->prev_in_table_; |
| |
| --state->registration_count_[reg->signal_number_]; |
| |
| impl.signals_ = reg->next_in_set_; |
| delete reg; |
| } |
| |
| ec = boost::system::error_code(); |
| return ec; |
| } |
| |
| boost::system::error_code signal_set_service::cancel( |
| signal_set_service::implementation_type& impl, |
| boost::system::error_code& ec) |
| { |
| BOOST_ASIO_HANDLER_OPERATION(("signal_set", &impl, "cancel")); |
| |
| op_queue<operation> ops; |
| { |
| signal_state* state = get_signal_state(); |
| static_mutex::scoped_lock lock(state->mutex_); |
| |
| while (signal_op* op = impl.queue_.front()) |
| { |
| op->ec_ = boost::asio::error::operation_aborted; |
| impl.queue_.pop(); |
| ops.push(op); |
| } |
| } |
| |
| io_service_.post_deferred_completions(ops); |
| |
| ec = boost::system::error_code(); |
| return ec; |
| } |
| |
| void signal_set_service::deliver_signal(int signal_number) |
| { |
| signal_state* state = get_signal_state(); |
| static_mutex::scoped_lock lock(state->mutex_); |
| |
| signal_set_service* service = state->service_list_; |
| while (service) |
| { |
| op_queue<operation> ops; |
| |
| registration* reg = service->registrations_[signal_number]; |
| while (reg) |
| { |
| if (reg->queue_->empty()) |
| { |
| ++reg->undelivered_; |
| } |
| else |
| { |
| while (signal_op* op = reg->queue_->front()) |
| { |
| op->signal_number_ = signal_number; |
| reg->queue_->pop(); |
| ops.push(op); |
| } |
| } |
| |
| reg = reg->next_in_table_; |
| } |
| |
| service->io_service_.post_deferred_completions(ops); |
| |
| service = service->next_; |
| } |
| } |
| |
| void signal_set_service::add_service(signal_set_service* service) |
| { |
| signal_state* state = get_signal_state(); |
| static_mutex::scoped_lock lock(state->mutex_); |
| |
| #if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) |
| // If this is the first service to be created, open a new pipe. |
| if (state->service_list_ == 0) |
| open_descriptors(); |
| #endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) |
| |
| // Insert service into linked list of all services. |
| service->next_ = state->service_list_; |
| service->prev_ = 0; |
| if (state->service_list_) |
| state->service_list_->prev_ = service; |
| state->service_list_ = service; |
| |
| #if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) |
| // Register for pipe readiness notifications. |
| service->reactor_.register_internal_descriptor(reactor::read_op, |
| state->read_descriptor_, service->reactor_data_, new pipe_read_op); |
| #endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) |
| } |
| |
| void signal_set_service::remove_service(signal_set_service* service) |
| { |
| signal_state* state = get_signal_state(); |
| static_mutex::scoped_lock lock(state->mutex_); |
| |
| if (service->next_ || service->prev_ || state->service_list_ == service) |
| { |
| #if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) |
| // Disable the pipe readiness notifications. |
| service->reactor_.deregister_descriptor( |
| state->read_descriptor_, service->reactor_data_, false); |
| #endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) |
| |
| // Remove service from linked list of all services. |
| if (state->service_list_ == service) |
| state->service_list_ = service->next_; |
| if (service->prev_) |
| service->prev_->next_ = service->next_; |
| if (service->next_) |
| service->next_->prev_= service->prev_; |
| service->next_ = 0; |
| service->prev_ = 0; |
| |
| #if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) |
| // If this is the last service to be removed, close the pipe. |
| if (state->service_list_ == 0) |
| close_descriptors(); |
| #endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) |
| } |
| } |
| |
| void signal_set_service::open_descriptors() |
| { |
| #if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) |
| signal_state* state = get_signal_state(); |
| |
| int pipe_fds[2]; |
| if (::pipe(pipe_fds) == 0) |
| { |
| state->read_descriptor_ = pipe_fds[0]; |
| ::fcntl(state->read_descriptor_, F_SETFL, O_NONBLOCK); |
| |
| state->write_descriptor_ = pipe_fds[1]; |
| ::fcntl(state->write_descriptor_, F_SETFL, O_NONBLOCK); |
| |
| #if defined(FD_CLOEXEC) |
| ::fcntl(state->read_descriptor_, F_SETFD, FD_CLOEXEC); |
| ::fcntl(state->write_descriptor_, F_SETFD, FD_CLOEXEC); |
| #endif // defined(FD_CLOEXEC) |
| } |
| else |
| { |
| boost::system::error_code ec(errno, |
| boost::asio::error::get_system_category()); |
| boost::asio::detail::throw_error(ec, "signal_set_service pipe"); |
| } |
| #endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) |
| } |
| |
| void signal_set_service::close_descriptors() |
| { |
| #if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) |
| signal_state* state = get_signal_state(); |
| |
| if (state->read_descriptor_ != -1) |
| ::close(state->read_descriptor_); |
| state->read_descriptor_ = -1; |
| |
| if (state->write_descriptor_ != -1) |
| ::close(state->write_descriptor_); |
| state->write_descriptor_ = -1; |
| #endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) |
| } |
| |
| void signal_set_service::start_wait_op( |
| signal_set_service::implementation_type& impl, signal_op* op) |
| { |
| io_service_.work_started(); |
| |
| signal_state* state = get_signal_state(); |
| static_mutex::scoped_lock lock(state->mutex_); |
| |
| registration* reg = impl.signals_; |
| while (reg) |
| { |
| if (reg->undelivered_ > 0) |
| { |
| --reg->undelivered_; |
| io_service_.post_deferred_completion(op); |
| return; |
| } |
| |
| reg = reg->next_in_set_; |
| } |
| |
| impl.queue_.push(op); |
| } |
| |
| } // namespace detail |
| } // namespace asio |
| } // namespace boost |
| |
| #include <boost/asio/detail/pop_options.hpp> |
| |
| #endif // BOOST_ASIO_DETAIL_IMPL_SIGNAL_SET_SERVICE_IPP |