825 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			825 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C++
		
	
	
	
| //
 | |
| // detail/impl/signal_set_service.ipp
 | |
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | |
| //
 | |
| // Copyright (c) 2003-2023 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 ASIO_DETAIL_IMPL_SIGNAL_SET_SERVICE_IPP
 | |
| #define ASIO_DETAIL_IMPL_SIGNAL_SET_SERVICE_IPP
 | |
| 
 | |
| #if defined(_MSC_VER) && (_MSC_VER >= 1200)
 | |
| # pragma once
 | |
| #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
 | |
| 
 | |
| #include "asio/detail/config.hpp"
 | |
| 
 | |
| #include <cstring>
 | |
| #include <stdexcept>
 | |
| #include "asio/detail/signal_blocker.hpp"
 | |
| #include "asio/detail/signal_set_service.hpp"
 | |
| #include "asio/detail/static_mutex.hpp"
 | |
| #include "asio/detail/throw_exception.hpp"
 | |
| 
 | |
| #if defined(ASIO_HAS_IO_URING_AS_DEFAULT)
 | |
| # include "asio/detail/io_uring_service.hpp"
 | |
| #else // defined(ASIO_HAS_IO_URING_AS_DEFAULT)
 | |
| # include "asio/detail/reactor.hpp"
 | |
| #endif // defined(ASIO_HAS_IO_URING_AS_DEFAULT)
 | |
| 
 | |
| #include "asio/detail/push_options.hpp"
 | |
| 
 | |
| 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];
 | |
| 
 | |
|   // The flags used for each registered signal.
 | |
|   signal_set_base::flags_t flags_[max_signal_number];
 | |
| };
 | |
| 
 | |
| signal_state* get_signal_state()
 | |
| {
 | |
|   static signal_state state = {
 | |
|     ASIO_STATIC_MUTEX_INIT, -1, -1, false, 0,
 | |
|     { 0 }, { signal_set_base::flags_t() } };
 | |
|   return &state;
 | |
| }
 | |
| 
 | |
| void asio_signal_handler(int signal_number)
 | |
| {
 | |
| #if defined(ASIO_WINDOWS) \
 | |
|   || defined(ASIO_WINDOWS_RUNTIME) \
 | |
|   || defined(__CYGWIN__)
 | |
|   signal_set_service::deliver_signal(signal_number);
 | |
| #else // defined(ASIO_WINDOWS)
 | |
|       //   || defined(ASIO_WINDOWS_RUNTIME)
 | |
|       //   || defined(__CYGWIN__)
 | |
|   int saved_errno = errno;
 | |
|   signal_state* state = get_signal_state();
 | |
|   signed_size_type result = ::write(state->write_descriptor_,
 | |
|       &signal_number, sizeof(signal_number));
 | |
|   (void)result;
 | |
|   errno = saved_errno;
 | |
| #endif // defined(ASIO_WINDOWS)
 | |
|        //   || defined(ASIO_WINDOWS_RUNTIME)
 | |
|        //   || defined(__CYGWIN__)
 | |
| 
 | |
| #if defined(ASIO_HAS_SIGNAL) && !defined(ASIO_HAS_SIGACTION)
 | |
|   ::signal(signal_number, asio_signal_handler);
 | |
| #endif // defined(ASIO_HAS_SIGNAL) && !defined(ASIO_HAS_SIGACTION)
 | |
| }
 | |
| 
 | |
| #if !defined(ASIO_WINDOWS) \
 | |
|   && !defined(ASIO_WINDOWS_RUNTIME) \
 | |
|   && !defined(__CYGWIN__)
 | |
| class signal_set_service::pipe_read_op :
 | |
| # if defined(ASIO_HAS_IO_URING_AS_DEFAULT)
 | |
|   public io_uring_operation
 | |
| # else // defined(ASIO_HAS_IO_URING_AS_DEFAULT)
 | |
|   public reactor_op
 | |
| # endif // defined(ASIO_HAS_IO_URING_AS_DEFAULT)
 | |
| {
 | |
| public:
 | |
| # if defined(ASIO_HAS_IO_URING_AS_DEFAULT)
 | |
|   pipe_read_op()
 | |
|     : io_uring_operation(asio::error_code(), &pipe_read_op::do_prepare,
 | |
|         &pipe_read_op::do_perform, pipe_read_op::do_complete)
 | |
|   {
 | |
|   }
 | |
| 
 | |
|   static void do_prepare(io_uring_operation*, ::io_uring_sqe* sqe)
 | |
|   {
 | |
|     signal_state* state = get_signal_state();
 | |
| 
 | |
|     int fd = state->read_descriptor_;
 | |
|     ::io_uring_prep_poll_add(sqe, fd, POLLIN);
 | |
|   }
 | |
| 
 | |
|   static bool do_perform(io_uring_operation*, bool)
 | |
|   {
 | |
|     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;
 | |
|   }
 | |
| # else // defined(ASIO_HAS_IO_URING_AS_DEFAULT)
 | |
|   pipe_read_op()
 | |
|     : reactor_op(asio::error_code(),
 | |
|         &pipe_read_op::do_perform, pipe_read_op::do_complete)
 | |
|   {
 | |
|   }
 | |
| 
 | |
|   static status 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 not_done;
 | |
|   }
 | |
| # endif // defined(ASIO_HAS_IO_URING_AS_DEFAULT)
 | |
| 
 | |
|   static void do_complete(void* /*owner*/, operation* base,
 | |
|       const asio::error_code& /*ec*/,
 | |
|       std::size_t /*bytes_transferred*/)
 | |
|   {
 | |
|     pipe_read_op* o(static_cast<pipe_read_op*>(base));
 | |
|     delete o;
 | |
|   }
 | |
| };
 | |
| #endif // !defined(ASIO_WINDOWS)
 | |
|        //   && !defined(ASIO_WINDOWS_RUNTIME)
 | |
|        //   && !defined(__CYGWIN__)
 | |
| 
 | |
| signal_set_service::signal_set_service(execution_context& context)
 | |
|   : execution_context_service_base<signal_set_service>(context),
 | |
|     scheduler_(asio::use_service<scheduler_impl>(context)),
 | |
| #if !defined(ASIO_WINDOWS) \
 | |
|   && !defined(ASIO_WINDOWS_RUNTIME) \
 | |
|   && !defined(__CYGWIN__)
 | |
| # if defined(ASIO_HAS_IO_URING_AS_DEFAULT)
 | |
|     io_uring_service_(asio::use_service<io_uring_service>(context)),
 | |
| # else // defined(ASIO_HAS_IO_URING_AS_DEFAULT)
 | |
|     reactor_(asio::use_service<reactor>(context)),
 | |
| # endif // defined(ASIO_HAS_IO_URING_AS_DEFAULT)
 | |
| #endif // !defined(ASIO_WINDOWS)
 | |
|        //   && !defined(ASIO_WINDOWS_RUNTIME)
 | |
|        //   && !defined(__CYGWIN__)
 | |
|     next_(0),
 | |
|     prev_(0)
 | |
| {
 | |
|   get_signal_state()->mutex_.init();
 | |
| 
 | |
| #if !defined(ASIO_WINDOWS) \
 | |
|   && !defined(ASIO_WINDOWS_RUNTIME) \
 | |
|   && !defined(__CYGWIN__)
 | |
| # if defined(ASIO_HAS_IO_URING_AS_DEFAULT)
 | |
|   io_uring_service_.init_task();
 | |
| # else // defined(ASIO_HAS_IO_URING_AS_DEFAULT)
 | |
|   reactor_.init_task();
 | |
| # endif // defined(ASIO_HAS_IO_URING_AS_DEFAULT)
 | |
| #endif // !defined(ASIO_WINDOWS)
 | |
|        //   && !defined(ASIO_WINDOWS_RUNTIME)
 | |
|        //   && !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()
 | |
| {
 | |
|   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_;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   scheduler_.abandon_operations(ops);
 | |
| }
 | |
| 
 | |
| void signal_set_service::notify_fork(execution_context::fork_event fork_ev)
 | |
| {
 | |
| #if !defined(ASIO_WINDOWS) \
 | |
|   && !defined(ASIO_WINDOWS_RUNTIME) \
 | |
|   && !defined(__CYGWIN__)
 | |
|   signal_state* state = get_signal_state();
 | |
|   static_mutex::scoped_lock lock(state->mutex_);
 | |
| 
 | |
|   switch (fork_ev)
 | |
|   {
 | |
|   case execution_context::fork_prepare:
 | |
|     {
 | |
|       int read_descriptor = state->read_descriptor_;
 | |
|       state->fork_prepared_ = true;
 | |
|       lock.unlock();
 | |
| # if defined(ASIO_HAS_IO_URING_AS_DEFAULT)
 | |
|       (void)read_descriptor;
 | |
|       io_uring_service_.deregister_io_object(io_object_data_);
 | |
|       io_uring_service_.cleanup_io_object(io_object_data_);
 | |
| # else // defined(ASIO_HAS_IO_URING_AS_DEFAULT)
 | |
|       reactor_.deregister_internal_descriptor(read_descriptor, reactor_data_);
 | |
|       reactor_.cleanup_descriptor_data(reactor_data_);
 | |
| # endif // defined(ASIO_HAS_IO_URING_AS_DEFAULT)
 | |
|     }
 | |
|     break;
 | |
|   case execution_context::fork_parent:
 | |
|     if (state->fork_prepared_)
 | |
|     {
 | |
|       int read_descriptor = state->read_descriptor_;
 | |
|       state->fork_prepared_ = false;
 | |
|       lock.unlock();
 | |
| # if defined(ASIO_HAS_IO_URING_AS_DEFAULT)
 | |
|       (void)read_descriptor;
 | |
|       io_uring_service_.register_internal_io_object(io_object_data_,
 | |
|           io_uring_service::read_op, new pipe_read_op);
 | |
| # else // defined(ASIO_HAS_IO_URING_AS_DEFAULT)
 | |
|       reactor_.register_internal_descriptor(reactor::read_op,
 | |
|           read_descriptor, reactor_data_, new pipe_read_op);
 | |
| # endif // defined(ASIO_HAS_IO_URING_AS_DEFAULT)
 | |
|     }
 | |
|     break;
 | |
|   case execution_context::fork_child:
 | |
|     if (state->fork_prepared_)
 | |
|     {
 | |
|       asio::detail::signal_blocker blocker;
 | |
|       close_descriptors();
 | |
|       open_descriptors();
 | |
|       int read_descriptor = state->read_descriptor_;
 | |
|       state->fork_prepared_ = false;
 | |
|       lock.unlock();
 | |
| # if defined(ASIO_HAS_IO_URING_AS_DEFAULT)
 | |
|       (void)read_descriptor;
 | |
|       io_uring_service_.register_internal_io_object(io_object_data_,
 | |
|           io_uring_service::read_op, new pipe_read_op);
 | |
| # else // defined(ASIO_HAS_IO_URING_AS_DEFAULT)
 | |
|       reactor_.register_internal_descriptor(reactor::read_op,
 | |
|           read_descriptor, reactor_data_, new pipe_read_op);
 | |
| # endif // defined(ASIO_HAS_IO_URING_AS_DEFAULT)
 | |
|     }
 | |
|     break;
 | |
|   default:
 | |
|     break;
 | |
|   }
 | |
| #else // !defined(ASIO_WINDOWS)
 | |
|       //   && !defined(ASIO_WINDOWS_RUNTIME)
 | |
|       //   && !defined(__CYGWIN__)
 | |
|   (void)fork_ev;
 | |
| #endif // !defined(ASIO_WINDOWS)
 | |
|        //   && !defined(ASIO_WINDOWS_RUNTIME)
 | |
|        //   && !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)
 | |
| {
 | |
|   asio::error_code ignored_ec;
 | |
|   clear(impl, ignored_ec);
 | |
|   cancel(impl, ignored_ec);
 | |
| }
 | |
| 
 | |
| asio::error_code signal_set_service::add(
 | |
|     signal_set_service::implementation_type& impl, int signal_number,
 | |
|     signal_set_base::flags_t f, asio::error_code& ec)
 | |
| {
 | |
|   // Check that the signal number is valid.
 | |
|   if (signal_number < 0 || signal_number >= max_signal_number)
 | |
|   {
 | |
|     ec = asio::error::invalid_argument;
 | |
|     return ec;
 | |
|   }
 | |
| 
 | |
|   // Check that the specified flags are supported.
 | |
| #if !defined(ASIO_HAS_SIGACTION)
 | |
|   if (f != signal_set_base::flags::dont_care)
 | |
|   {
 | |
|     ec = asio::error::operation_not_supported;
 | |
|     return ec;
 | |
|   }
 | |
| #endif // !defined(ASIO_HAS_SIGACTION)
 | |
| 
 | |
|   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(ASIO_HAS_SIGNAL) || defined(ASIO_HAS_SIGACTION)
 | |
|     // Register for the signal if we're the first.
 | |
|     if (state->registration_count_[signal_number] == 0)
 | |
|     {
 | |
| # if defined(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 (f != signal_set_base::flags::dont_care)
 | |
|         sa.sa_flags = static_cast<int>(f);
 | |
|       if (::sigaction(signal_number, &sa, 0) == -1)
 | |
| # else // defined(ASIO_HAS_SIGACTION)
 | |
|       if (::signal(signal_number, asio_signal_handler) == SIG_ERR)
 | |
| # endif // defined(ASIO_HAS_SIGACTION)
 | |
|       {
 | |
| # if defined(ASIO_WINDOWS) || defined(__CYGWIN__)
 | |
|         ec = asio::error::invalid_argument;
 | |
| # else // defined(ASIO_WINDOWS) || defined(__CYGWIN__)
 | |
|         ec = asio::error_code(errno,
 | |
|             asio::error::get_system_category());
 | |
| # endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__)
 | |
|         delete new_registration;
 | |
|         return ec;
 | |
|       }
 | |
| # if defined(ASIO_HAS_SIGACTION)
 | |
|       state->flags_[signal_number] = f;
 | |
| # endif // defined(ASIO_HAS_SIGACTION)
 | |
|     }
 | |
| # if defined(ASIO_HAS_SIGACTION)
 | |
|     // Otherwise check to see if the flags have changed.
 | |
|     else if (f != signal_set_base::flags::dont_care)
 | |
|     {
 | |
|       if (f != state->flags_[signal_number])
 | |
|       {
 | |
|         using namespace std; // For memset.
 | |
|         if (state->flags_[signal_number] != signal_set_base::flags::dont_care)
 | |
|         {
 | |
|           ec = asio::error::invalid_argument;
 | |
|           return ec;
 | |
|         }
 | |
|         struct sigaction sa;
 | |
|         memset(&sa, 0, sizeof(sa));
 | |
|         sa.sa_handler = asio_signal_handler;
 | |
|         sigfillset(&sa.sa_mask);
 | |
|         sa.sa_flags = static_cast<int>(f);
 | |
|         if (::sigaction(signal_number, &sa, 0) == -1)
 | |
|         {
 | |
|           ec = asio::error_code(errno,
 | |
|               asio::error::get_system_category());
 | |
|           return ec;
 | |
|         }
 | |
|         state->flags_[signal_number] = f;
 | |
|       }
 | |
|     }
 | |
| # endif // defined(ASIO_HAS_SIGACTION)
 | |
| #endif // defined(ASIO_HAS_SIGNAL) || defined(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 = asio::error_code();
 | |
|   return ec;
 | |
| }
 | |
| 
 | |
| asio::error_code signal_set_service::remove(
 | |
|     signal_set_service::implementation_type& impl,
 | |
|     int signal_number, asio::error_code& ec)
 | |
| {
 | |
|   // Check that the signal number is valid.
 | |
|   if (signal_number < 0 || signal_number >= max_signal_number)
 | |
|   {
 | |
|     ec = 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(ASIO_HAS_SIGNAL) || defined(ASIO_HAS_SIGACTION)
 | |
|     // Set signal handler back to the default if we're the last.
 | |
|     if (state->registration_count_[signal_number] == 1)
 | |
|     {
 | |
| # if defined(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(ASIO_HAS_SIGACTION)
 | |
|       if (::signal(signal_number, SIG_DFL) == SIG_ERR)
 | |
| # endif // defined(ASIO_HAS_SIGACTION)
 | |
|       {
 | |
| # if defined(ASIO_WINDOWS) || defined(__CYGWIN__)
 | |
|         ec = asio::error::invalid_argument;
 | |
| # else // defined(ASIO_WINDOWS) || defined(__CYGWIN__)
 | |
|         ec = asio::error_code(errno,
 | |
|             asio::error::get_system_category());
 | |
| # endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__)
 | |
|         return ec;
 | |
|       }
 | |
| # if defined(ASIO_HAS_SIGACTION)
 | |
|       state->flags_[signal_number] = signal_set_base::flags_t();
 | |
| # endif // defined(ASIO_HAS_SIGACTION)
 | |
|     }
 | |
| #endif // defined(ASIO_HAS_SIGNAL) || defined(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 = asio::error_code();
 | |
|   return ec;
 | |
| }
 | |
| 
 | |
| asio::error_code signal_set_service::clear(
 | |
|     signal_set_service::implementation_type& impl,
 | |
|     asio::error_code& ec)
 | |
| {
 | |
|   signal_state* state = get_signal_state();
 | |
|   static_mutex::scoped_lock lock(state->mutex_);
 | |
| 
 | |
|   while (registration* reg = impl.signals_)
 | |
|   {
 | |
| #if defined(ASIO_HAS_SIGNAL) || defined(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(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(ASIO_HAS_SIGACTION)
 | |
|       if (::signal(reg->signal_number_, SIG_DFL) == SIG_ERR)
 | |
| # endif // defined(ASIO_HAS_SIGACTION)
 | |
|       {
 | |
| # if defined(ASIO_WINDOWS) || defined(__CYGWIN__)
 | |
|         ec = asio::error::invalid_argument;
 | |
| # else // defined(ASIO_WINDOWS) || defined(__CYGWIN__)
 | |
|         ec = asio::error_code(errno,
 | |
|             asio::error::get_system_category());
 | |
| # endif // defined(ASIO_WINDOWS) || defined(__CYGWIN__)
 | |
|         return ec;
 | |
|       }
 | |
| # if defined(ASIO_HAS_SIGACTION)
 | |
|       state->flags_[reg->signal_number_] = signal_set_base::flags_t();
 | |
| # endif // defined(ASIO_HAS_SIGACTION)
 | |
|     }
 | |
| #endif // defined(ASIO_HAS_SIGNAL) || defined(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 = asio::error_code();
 | |
|   return ec;
 | |
| }
 | |
| 
 | |
| asio::error_code signal_set_service::cancel(
 | |
|     signal_set_service::implementation_type& impl,
 | |
|     asio::error_code& ec)
 | |
| {
 | |
|   ASIO_HANDLER_OPERATION((scheduler_.context(),
 | |
|         "signal_set", &impl, 0, "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_ = asio::error::operation_aborted;
 | |
|       impl.queue_.pop();
 | |
|       ops.push(op);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   scheduler_.post_deferred_completions(ops);
 | |
| 
 | |
|   ec = asio::error_code();
 | |
|   return ec;
 | |
| }
 | |
| 
 | |
| void signal_set_service::cancel_ops_by_key(
 | |
|     signal_set_service::implementation_type& impl, void* cancellation_key)
 | |
| {
 | |
|   op_queue<operation> ops;
 | |
|   {
 | |
|     op_queue<signal_op> other_ops;
 | |
|     signal_state* state = get_signal_state();
 | |
|     static_mutex::scoped_lock lock(state->mutex_);
 | |
| 
 | |
|     while (signal_op* op = impl.queue_.front())
 | |
|     {
 | |
|       impl.queue_.pop();
 | |
|       if (op->cancellation_key_ == cancellation_key)
 | |
|       {
 | |
|         op->ec_ = asio::error::operation_aborted;
 | |
|         ops.push(op);
 | |
|       }
 | |
|       else
 | |
|         other_ops.push(op);
 | |
|     }
 | |
| 
 | |
|     impl.queue_.push(other_ops);
 | |
|   }
 | |
| 
 | |
|   scheduler_.post_deferred_completions(ops);
 | |
| }
 | |
| 
 | |
| 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->scheduler_.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(ASIO_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(ASIO_WINDOWS) && !defined(__CYGWIN__)
 | |
| 
 | |
|   // If a scheduler_ object is thread-unsafe then it must be the only
 | |
|   // scheduler used to create signal_set objects.
 | |
|   if (state->service_list_ != 0)
 | |
|   {
 | |
|     if (!ASIO_CONCURRENCY_HINT_IS_LOCKING(SCHEDULER,
 | |
|           service->scheduler_.concurrency_hint())
 | |
|         || !ASIO_CONCURRENCY_HINT_IS_LOCKING(SCHEDULER,
 | |
|           state->service_list_->scheduler_.concurrency_hint()))
 | |
|     {
 | |
|       std::logic_error ex(
 | |
|           "Thread-unsafe execution context objects require "
 | |
|           "exclusive access to signal handling.");
 | |
|       asio::detail::throw_exception(ex);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // 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(ASIO_WINDOWS) \
 | |
|   && !defined(ASIO_WINDOWS_RUNTIME) \
 | |
|   && !defined(__CYGWIN__)
 | |
|   // Register for pipe readiness notifications.
 | |
|   int read_descriptor = state->read_descriptor_;
 | |
|   lock.unlock();
 | |
| # if defined(ASIO_HAS_IO_URING_AS_DEFAULT)
 | |
|   (void)read_descriptor;
 | |
|   service->io_uring_service_.register_internal_io_object(
 | |
|       service->io_object_data_, io_uring_service::read_op, new pipe_read_op);
 | |
| # else // defined(ASIO_HAS_IO_URING_AS_DEFAULT)
 | |
|   service->reactor_.register_internal_descriptor(reactor::read_op,
 | |
|       read_descriptor, service->reactor_data_, new pipe_read_op);
 | |
| # endif // defined(ASIO_HAS_IO_URING_AS_DEFAULT)
 | |
| #endif // !defined(ASIO_WINDOWS)
 | |
|        //   && !defined(ASIO_WINDOWS_RUNTIME)
 | |
|        //   && !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(ASIO_WINDOWS) \
 | |
|   && !defined(ASIO_WINDOWS_RUNTIME) \
 | |
|   && !defined(__CYGWIN__)
 | |
|     // Disable the pipe readiness notifications.
 | |
|     int read_descriptor = state->read_descriptor_;
 | |
|     lock.unlock();
 | |
| # if defined(ASIO_HAS_IO_URING_AS_DEFAULT)
 | |
|     (void)read_descriptor;
 | |
|     service->io_uring_service_.deregister_io_object(service->io_object_data_);
 | |
|     service->io_uring_service_.cleanup_io_object(service->io_object_data_);
 | |
|     lock.lock();
 | |
| # else // defined(ASIO_HAS_IO_URING_AS_DEFAULT)
 | |
|     service->reactor_.deregister_internal_descriptor(
 | |
|         read_descriptor, service->reactor_data_);
 | |
|     service->reactor_.cleanup_descriptor_data(service->reactor_data_);
 | |
|     lock.lock();
 | |
| # endif // defined(ASIO_HAS_IO_URING_AS_DEFAULT)
 | |
| #endif // !defined(ASIO_WINDOWS)
 | |
|        //   && !defined(ASIO_WINDOWS_RUNTIME)
 | |
|        //   && !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(ASIO_WINDOWS) && !defined(__CYGWIN__)
 | |
|     // If this is the last service to be removed, close the pipe.
 | |
|     if (state->service_list_ == 0)
 | |
|       close_descriptors();
 | |
| #endif // !defined(ASIO_WINDOWS) && !defined(__CYGWIN__)
 | |
|   }
 | |
| }
 | |
| 
 | |
| void signal_set_service::open_descriptors()
 | |
| {
 | |
| #if !defined(ASIO_WINDOWS) \
 | |
|   && !defined(ASIO_WINDOWS_RUNTIME) \
 | |
|   && !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
 | |
|   {
 | |
|     asio::error_code ec(errno,
 | |
|         asio::error::get_system_category());
 | |
|     asio::detail::throw_error(ec, "signal_set_service pipe");
 | |
|   }
 | |
| #endif // !defined(ASIO_WINDOWS)
 | |
|        //   && !defined(ASIO_WINDOWS_RUNTIME)
 | |
|        //   && !defined(__CYGWIN__)
 | |
| }
 | |
| 
 | |
| void signal_set_service::close_descriptors()
 | |
| {
 | |
| #if !defined(ASIO_WINDOWS) \
 | |
|   && !defined(ASIO_WINDOWS_RUNTIME) \
 | |
|   && !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(ASIO_WINDOWS)
 | |
|        //   && !defined(ASIO_WINDOWS_RUNTIME)
 | |
|        //   && !defined(__CYGWIN__)
 | |
| }
 | |
| 
 | |
| void signal_set_service::start_wait_op(
 | |
|     signal_set_service::implementation_type& impl, signal_op* op)
 | |
| {
 | |
|   scheduler_.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_;
 | |
|       op->signal_number_ = reg->signal_number_;
 | |
|       scheduler_.post_deferred_completion(op);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     reg = reg->next_in_set_;
 | |
|   }
 | |
| 
 | |
|   impl.queue_.push(op);
 | |
| }
 | |
| 
 | |
| } // namespace detail
 | |
| } // namespace asio
 | |
| 
 | |
| #include "asio/detail/pop_options.hpp"
 | |
| 
 | |
| #endif // ASIO_DETAIL_IMPL_SIGNAL_SET_SERVICE_IPP
 |