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