You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and dots ('.'), can be up to 35 characters long. Letters must be lowercase.
593 lines
17 KiB
593 lines
17 KiB
// |
|
// 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, |
|
const 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
|
|
|