2 // detail/impl/signal_set_service.ipp
3 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 // Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
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)
11 #ifndef BOOST_ASIO_DETAIL_IMPL_SIGNAL_SET_SERVICE_IPP
12 #define BOOST_ASIO_DETAIL_IMPL_SIGNAL_SET_SERVICE_IPP
14 #if defined(_MSC_VER) && (_MSC_VER >= 1200)
16 #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
18 #include <boost/asio/detail/config.hpp>
22 #include <boost/asio/detail/reactor.hpp>
23 #include <boost/asio/detail/signal_blocker.hpp>
24 #include <boost/asio/detail/signal_set_service.hpp>
25 #include <boost/asio/detail/static_mutex.hpp>
26 #include <boost/asio/detail/throw_exception.hpp>
28 #include <boost/asio/detail/push_options.hpp>
36 // Mutex used for protecting global state.
39 // The read end of the pipe used for signal notifications.
42 // The write end of the pipe used for signal notifications.
43 int write_descriptor_;
45 // Whether the signal state has been prepared for a fork.
48 // The head of a linked list of all signal_set_service instances.
49 class signal_set_service* service_list_;
51 // A count of the number of objects that are registered for each signal.
52 std::size_t registration_count_[max_signal_number];
55 signal_state* get_signal_state()
57 static signal_state state = {
58 BOOST_ASIO_STATIC_MUTEX_INIT, -1, -1, false, 0, { 0 } };
62 void boost_asio_signal_handler(int signal_number)
64 #if defined(BOOST_ASIO_WINDOWS) \
65 || defined(BOOST_ASIO_WINDOWS_RUNTIME) \
66 || defined(__CYGWIN__)
67 signal_set_service::deliver_signal(signal_number);
68 #else // defined(BOOST_ASIO_WINDOWS)
69 // || defined(BOOST_ASIO_WINDOWS_RUNTIME)
70 // || defined(__CYGWIN__)
71 int saved_errno = errno;
72 signal_state* state = get_signal_state();
73 signed_size_type result = ::write(state->write_descriptor_,
74 &signal_number, sizeof(signal_number));
77 #endif // defined(BOOST_ASIO_WINDOWS)
78 // || defined(BOOST_ASIO_WINDOWS_RUNTIME)
79 // || defined(__CYGWIN__)
81 #if defined(BOOST_ASIO_HAS_SIGNAL) && !defined(BOOST_ASIO_HAS_SIGACTION)
82 ::signal(signal_number, boost_asio_signal_handler);
83 #endif // defined(BOOST_ASIO_HAS_SIGNAL) && !defined(BOOST_ASIO_HAS_SIGACTION)
86 #if !defined(BOOST_ASIO_WINDOWS) \
87 && !defined(BOOST_ASIO_WINDOWS_RUNTIME) \
88 && !defined(__CYGWIN__)
89 class signal_set_service::pipe_read_op : public reactor_op
93 : reactor_op(&pipe_read_op::do_perform, pipe_read_op::do_complete)
97 static status do_perform(reactor_op*)
99 signal_state* state = get_signal_state();
101 int fd = state->read_descriptor_;
102 int signal_number = 0;
103 while (::read(fd, &signal_number, sizeof(int)) == sizeof(int))
104 if (signal_number >= 0 && signal_number < max_signal_number)
105 signal_set_service::deliver_signal(signal_number);
110 static void do_complete(void* /*owner*/, operation* base,
111 const boost::system::error_code& /*ec*/,
112 std::size_t /*bytes_transferred*/)
114 pipe_read_op* o(static_cast<pipe_read_op*>(base));
118 #endif // !defined(BOOST_ASIO_WINDOWS)
119 // && !defined(BOOST_ASIO_WINDOWS_RUNTIME)
120 // && !defined(__CYGWIN__)
122 signal_set_service::signal_set_service(
123 boost::asio::io_context& io_context)
124 : service_base<signal_set_service>(io_context),
125 io_context_(boost::asio::use_service<io_context_impl>(io_context)),
126 #if !defined(BOOST_ASIO_WINDOWS) \
127 && !defined(BOOST_ASIO_WINDOWS_RUNTIME) \
128 && !defined(__CYGWIN__)
129 reactor_(boost::asio::use_service<reactor>(io_context)),
130 #endif // !defined(BOOST_ASIO_WINDOWS)
131 // && !defined(BOOST_ASIO_WINDOWS_RUNTIME)
132 // && !defined(__CYGWIN__)
136 get_signal_state()->mutex_.init();
138 #if !defined(BOOST_ASIO_WINDOWS) \
139 && !defined(BOOST_ASIO_WINDOWS_RUNTIME) \
140 && !defined(__CYGWIN__)
141 reactor_.init_task();
142 #endif // !defined(BOOST_ASIO_WINDOWS)
143 // && !defined(BOOST_ASIO_WINDOWS_RUNTIME)
144 // && !defined(__CYGWIN__)
146 for (int i = 0; i < max_signal_number; ++i)
147 registrations_[i] = 0;
152 signal_set_service::~signal_set_service()
154 remove_service(this);
157 void signal_set_service::shutdown()
159 remove_service(this);
161 op_queue<operation> ops;
163 for (int i = 0; i < max_signal_number; ++i)
165 registration* reg = registrations_[i];
168 ops.push(*reg->queue_);
169 reg = reg->next_in_table_;
173 io_context_.abandon_operations(ops);
176 void signal_set_service::notify_fork(
177 boost::asio::io_context::fork_event fork_ev)
179 #if !defined(BOOST_ASIO_WINDOWS) \
180 && !defined(BOOST_ASIO_WINDOWS_RUNTIME) \
181 && !defined(__CYGWIN__)
182 signal_state* state = get_signal_state();
183 static_mutex::scoped_lock lock(state->mutex_);
187 case boost::asio::io_context::fork_prepare:
189 int read_descriptor = state->read_descriptor_;
190 state->fork_prepared_ = true;
192 reactor_.deregister_internal_descriptor(read_descriptor, reactor_data_);
193 reactor_.cleanup_descriptor_data(reactor_data_);
196 case boost::asio::io_context::fork_parent:
197 if (state->fork_prepared_)
199 int read_descriptor = state->read_descriptor_;
200 state->fork_prepared_ = false;
202 reactor_.register_internal_descriptor(reactor::read_op,
203 read_descriptor, reactor_data_, new pipe_read_op);
206 case boost::asio::io_context::fork_child:
207 if (state->fork_prepared_)
209 boost::asio::detail::signal_blocker blocker;
212 int read_descriptor = state->read_descriptor_;
213 state->fork_prepared_ = false;
215 reactor_.register_internal_descriptor(reactor::read_op,
216 read_descriptor, reactor_data_, new pipe_read_op);
222 #else // !defined(BOOST_ASIO_WINDOWS)
223 // && !defined(BOOST_ASIO_WINDOWS_RUNTIME)
224 // && !defined(__CYGWIN__)
226 #endif // !defined(BOOST_ASIO_WINDOWS)
227 // && !defined(BOOST_ASIO_WINDOWS_RUNTIME)
228 // && !defined(__CYGWIN__)
231 void signal_set_service::construct(
232 signal_set_service::implementation_type& impl)
237 void signal_set_service::destroy(
238 signal_set_service::implementation_type& impl)
240 boost::system::error_code ignored_ec;
241 clear(impl, ignored_ec);
242 cancel(impl, ignored_ec);
245 boost::system::error_code signal_set_service::add(
246 signal_set_service::implementation_type& impl,
247 int signal_number, boost::system::error_code& ec)
249 // Check that the signal number is valid.
250 if (signal_number < 0 || signal_number >= max_signal_number)
252 ec = boost::asio::error::invalid_argument;
256 signal_state* state = get_signal_state();
257 static_mutex::scoped_lock lock(state->mutex_);
259 // Find the appropriate place to insert the registration.
260 registration** insertion_point = &impl.signals_;
261 registration* next = impl.signals_;
262 while (next && next->signal_number_ < signal_number)
264 insertion_point = &next->next_in_set_;
265 next = next->next_in_set_;
268 // Only do something if the signal is not already registered.
269 if (next == 0 || next->signal_number_ != signal_number)
271 registration* new_registration = new registration;
273 #if defined(BOOST_ASIO_HAS_SIGNAL) || defined(BOOST_ASIO_HAS_SIGACTION)
274 // Register for the signal if we're the first.
275 if (state->registration_count_[signal_number] == 0)
277 # if defined(BOOST_ASIO_HAS_SIGACTION)
278 using namespace std; // For memset.
280 memset(&sa, 0, sizeof(sa));
281 sa.sa_handler = boost_asio_signal_handler;
282 sigfillset(&sa.sa_mask);
283 if (::sigaction(signal_number, &sa, 0) == -1)
284 # else // defined(BOOST_ASIO_HAS_SIGACTION)
285 if (::signal(signal_number, boost_asio_signal_handler) == SIG_ERR)
286 # endif // defined(BOOST_ASIO_HAS_SIGACTION)
288 # if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__)
289 ec = boost::asio::error::invalid_argument;
290 # else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__)
291 ec = boost::system::error_code(errno,
292 boost::asio::error::get_system_category());
293 # endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__)
294 delete new_registration;
298 #endif // defined(BOOST_ASIO_HAS_SIGNAL) || defined(BOOST_ASIO_HAS_SIGACTION)
300 // Record the new registration in the set.
301 new_registration->signal_number_ = signal_number;
302 new_registration->queue_ = &impl.queue_;
303 new_registration->next_in_set_ = next;
304 *insertion_point = new_registration;
306 // Insert registration into the registration table.
307 new_registration->next_in_table_ = registrations_[signal_number];
308 if (registrations_[signal_number])
309 registrations_[signal_number]->prev_in_table_ = new_registration;
310 registrations_[signal_number] = new_registration;
312 ++state->registration_count_[signal_number];
315 ec = boost::system::error_code();
319 boost::system::error_code signal_set_service::remove(
320 signal_set_service::implementation_type& impl,
321 int signal_number, boost::system::error_code& ec)
323 // Check that the signal number is valid.
324 if (signal_number < 0 || signal_number >= max_signal_number)
326 ec = boost::asio::error::invalid_argument;
330 signal_state* state = get_signal_state();
331 static_mutex::scoped_lock lock(state->mutex_);
333 // Find the signal number in the list of registrations.
334 registration** deletion_point = &impl.signals_;
335 registration* reg = impl.signals_;
336 while (reg && reg->signal_number_ < signal_number)
338 deletion_point = ®->next_in_set_;
339 reg = reg->next_in_set_;
342 if (reg != 0 && reg->signal_number_ == signal_number)
344 #if defined(BOOST_ASIO_HAS_SIGNAL) || defined(BOOST_ASIO_HAS_SIGACTION)
345 // Set signal handler back to the default if we're the last.
346 if (state->registration_count_[signal_number] == 1)
348 # if defined(BOOST_ASIO_HAS_SIGACTION)
349 using namespace std; // For memset.
351 memset(&sa, 0, sizeof(sa));
352 sa.sa_handler = SIG_DFL;
353 if (::sigaction(signal_number, &sa, 0) == -1)
354 # else // defined(BOOST_ASIO_HAS_SIGACTION)
355 if (::signal(signal_number, SIG_DFL) == SIG_ERR)
356 # endif // defined(BOOST_ASIO_HAS_SIGACTION)
358 # if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__)
359 ec = boost::asio::error::invalid_argument;
360 # else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__)
361 ec = boost::system::error_code(errno,
362 boost::asio::error::get_system_category());
363 # endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__)
367 #endif // defined(BOOST_ASIO_HAS_SIGNAL) || defined(BOOST_ASIO_HAS_SIGACTION)
369 // Remove the registration from the set.
370 *deletion_point = reg->next_in_set_;
372 // Remove the registration from the registration table.
373 if (registrations_[signal_number] == reg)
374 registrations_[signal_number] = reg->next_in_table_;
375 if (reg->prev_in_table_)
376 reg->prev_in_table_->next_in_table_ = reg->next_in_table_;
377 if (reg->next_in_table_)
378 reg->next_in_table_->prev_in_table_ = reg->prev_in_table_;
380 --state->registration_count_[signal_number];
385 ec = boost::system::error_code();
389 boost::system::error_code signal_set_service::clear(
390 signal_set_service::implementation_type& impl,
391 boost::system::error_code& ec)
393 signal_state* state = get_signal_state();
394 static_mutex::scoped_lock lock(state->mutex_);
396 while (registration* reg = impl.signals_)
398 #if defined(BOOST_ASIO_HAS_SIGNAL) || defined(BOOST_ASIO_HAS_SIGACTION)
399 // Set signal handler back to the default if we're the last.
400 if (state->registration_count_[reg->signal_number_] == 1)
402 # if defined(BOOST_ASIO_HAS_SIGACTION)
403 using namespace std; // For memset.
405 memset(&sa, 0, sizeof(sa));
406 sa.sa_handler = SIG_DFL;
407 if (::sigaction(reg->signal_number_, &sa, 0) == -1)
408 # else // defined(BOOST_ASIO_HAS_SIGACTION)
409 if (::signal(reg->signal_number_, SIG_DFL) == SIG_ERR)
410 # endif // defined(BOOST_ASIO_HAS_SIGACTION)
412 # if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__)
413 ec = boost::asio::error::invalid_argument;
414 # else // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__)
415 ec = boost::system::error_code(errno,
416 boost::asio::error::get_system_category());
417 # endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__)
421 #endif // defined(BOOST_ASIO_HAS_SIGNAL) || defined(BOOST_ASIO_HAS_SIGACTION)
423 // Remove the registration from the registration table.
424 if (registrations_[reg->signal_number_] == reg)
425 registrations_[reg->signal_number_] = reg->next_in_table_;
426 if (reg->prev_in_table_)
427 reg->prev_in_table_->next_in_table_ = reg->next_in_table_;
428 if (reg->next_in_table_)
429 reg->next_in_table_->prev_in_table_ = reg->prev_in_table_;
431 --state->registration_count_[reg->signal_number_];
433 impl.signals_ = reg->next_in_set_;
437 ec = boost::system::error_code();
441 boost::system::error_code signal_set_service::cancel(
442 signal_set_service::implementation_type& impl,
443 boost::system::error_code& ec)
445 BOOST_ASIO_HANDLER_OPERATION((io_context_.context(),
446 "signal_set", &impl, 0, "cancel"));
448 op_queue<operation> ops;
450 signal_state* state = get_signal_state();
451 static_mutex::scoped_lock lock(state->mutex_);
453 while (signal_op* op = impl.queue_.front())
455 op->ec_ = boost::asio::error::operation_aborted;
461 io_context_.post_deferred_completions(ops);
463 ec = boost::system::error_code();
467 void signal_set_service::deliver_signal(int signal_number)
469 signal_state* state = get_signal_state();
470 static_mutex::scoped_lock lock(state->mutex_);
472 signal_set_service* service = state->service_list_;
475 op_queue<operation> ops;
477 registration* reg = service->registrations_[signal_number];
480 if (reg->queue_->empty())
486 while (signal_op* op = reg->queue_->front())
488 op->signal_number_ = signal_number;
494 reg = reg->next_in_table_;
497 service->io_context_.post_deferred_completions(ops);
499 service = service->next_;
503 void signal_set_service::add_service(signal_set_service* service)
505 signal_state* state = get_signal_state();
506 static_mutex::scoped_lock lock(state->mutex_);
508 #if !defined(BOOST_ASIO_WINDOWS) && !defined(__CYGWIN__)
509 // If this is the first service to be created, open a new pipe.
510 if (state->service_list_ == 0)
512 #endif // !defined(BOOST_ASIO_WINDOWS) && !defined(__CYGWIN__)
514 // If an io_context object is thread-unsafe then it must be the only
515 // io_context used to create signal_set objects.
516 if (state->service_list_ != 0)
518 if (!BOOST_ASIO_CONCURRENCY_HINT_IS_LOCKING(SCHEDULER,
519 service->io_context_.concurrency_hint())
520 || !BOOST_ASIO_CONCURRENCY_HINT_IS_LOCKING(SCHEDULER,
521 state->service_list_->io_context_.concurrency_hint()))
524 "Thread-unsafe io_context objects require "
525 "exclusive access to signal handling.");
526 boost::asio::detail::throw_exception(ex);
530 // Insert service into linked list of all services.
531 service->next_ = state->service_list_;
533 if (state->service_list_)
534 state->service_list_->prev_ = service;
535 state->service_list_ = service;
537 #if !defined(BOOST_ASIO_WINDOWS) \
538 && !defined(BOOST_ASIO_WINDOWS_RUNTIME) \
539 && !defined(__CYGWIN__)
540 // Register for pipe readiness notifications.
541 int read_descriptor = state->read_descriptor_;
543 service->reactor_.register_internal_descriptor(reactor::read_op,
544 read_descriptor, service->reactor_data_, new pipe_read_op);
545 #endif // !defined(BOOST_ASIO_WINDOWS)
546 // && !defined(BOOST_ASIO_WINDOWS_RUNTIME)
547 // && !defined(__CYGWIN__)
550 void signal_set_service::remove_service(signal_set_service* service)
552 signal_state* state = get_signal_state();
553 static_mutex::scoped_lock lock(state->mutex_);
555 if (service->next_ || service->prev_ || state->service_list_ == service)
557 #if !defined(BOOST_ASIO_WINDOWS) \
558 && !defined(BOOST_ASIO_WINDOWS_RUNTIME) \
559 && !defined(__CYGWIN__)
560 // Disable the pipe readiness notifications.
561 int read_descriptor = state->read_descriptor_;
563 service->reactor_.deregister_internal_descriptor(
564 read_descriptor, service->reactor_data_);
565 service->reactor_.cleanup_descriptor_data(service->reactor_data_);
567 #endif // !defined(BOOST_ASIO_WINDOWS)
568 // && !defined(BOOST_ASIO_WINDOWS_RUNTIME)
569 // && !defined(__CYGWIN__)
571 // Remove service from linked list of all services.
572 if (state->service_list_ == service)
573 state->service_list_ = service->next_;
575 service->prev_->next_ = service->next_;
577 service->next_->prev_= service->prev_;
581 #if !defined(BOOST_ASIO_WINDOWS) && !defined(__CYGWIN__)
582 // If this is the last service to be removed, close the pipe.
583 if (state->service_list_ == 0)
585 #endif // !defined(BOOST_ASIO_WINDOWS) && !defined(__CYGWIN__)
589 void signal_set_service::open_descriptors()
591 #if !defined(BOOST_ASIO_WINDOWS) \
592 && !defined(BOOST_ASIO_WINDOWS_RUNTIME) \
593 && !defined(__CYGWIN__)
594 signal_state* state = get_signal_state();
597 if (::pipe(pipe_fds) == 0)
599 state->read_descriptor_ = pipe_fds[0];
600 ::fcntl(state->read_descriptor_, F_SETFL, O_NONBLOCK);
602 state->write_descriptor_ = pipe_fds[1];
603 ::fcntl(state->write_descriptor_, F_SETFL, O_NONBLOCK);
605 #if defined(FD_CLOEXEC)
606 ::fcntl(state->read_descriptor_, F_SETFD, FD_CLOEXEC);
607 ::fcntl(state->write_descriptor_, F_SETFD, FD_CLOEXEC);
608 #endif // defined(FD_CLOEXEC)
612 boost::system::error_code ec(errno,
613 boost::asio::error::get_system_category());
614 boost::asio::detail::throw_error(ec, "signal_set_service pipe");
616 #endif // !defined(BOOST_ASIO_WINDOWS)
617 // && !defined(BOOST_ASIO_WINDOWS_RUNTIME)
618 // && !defined(__CYGWIN__)
621 void signal_set_service::close_descriptors()
623 #if !defined(BOOST_ASIO_WINDOWS) \
624 && !defined(BOOST_ASIO_WINDOWS_RUNTIME) \
625 && !defined(__CYGWIN__)
626 signal_state* state = get_signal_state();
628 if (state->read_descriptor_ != -1)
629 ::close(state->read_descriptor_);
630 state->read_descriptor_ = -1;
632 if (state->write_descriptor_ != -1)
633 ::close(state->write_descriptor_);
634 state->write_descriptor_ = -1;
635 #endif // !defined(BOOST_ASIO_WINDOWS)
636 // && !defined(BOOST_ASIO_WINDOWS_RUNTIME)
637 // && !defined(__CYGWIN__)
640 void signal_set_service::start_wait_op(
641 signal_set_service::implementation_type& impl, signal_op* op)
643 io_context_.work_started();
645 signal_state* state = get_signal_state();
646 static_mutex::scoped_lock lock(state->mutex_);
648 registration* reg = impl.signals_;
651 if (reg->undelivered_ > 0)
654 op->signal_number_ = reg->signal_number_;
655 io_context_.post_deferred_completion(op);
659 reg = reg->next_in_set_;
662 impl.queue_.push(op);
665 } // namespace detail
669 #include <boost/asio/detail/pop_options.hpp>
671 #endif // BOOST_ASIO_DETAIL_IMPL_SIGNAL_SET_SERVICE_IPP