Apache Mesos
signals.hpp
Go to the documentation of this file.
1 // Licensed under the Apache License, Version 2.0 (the "License");
2 // you may not use this file except in compliance with the License.
3 // You may obtain a copy of the License at
4 //
5 // http://www.apache.org/licenses/LICENSE-2.0
6 //
7 // Unless required by applicable law or agreed to in writing, software
8 // distributed under the License is distributed on an "AS IS" BASIS,
9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 // See the License for the specific language governing permissions and
11 // limitations under the License.
12 
13 #ifndef __STOUT_OS_POSIX_SIGNALS_HPP__
14 #define __STOUT_OS_POSIX_SIGNALS_HPP__
15 
16 #include <errno.h>
17 #include <pthread.h>
18 #include <signal.h>
19 #include <string.h>
20 #include <unistd.h>
21 
22 
23 namespace os {
24 
25 namespace signals {
26 
27 // Installs the given signal handler.
28 inline int install(int signal, void(*handler)(int))
29 {
30  struct sigaction action;
31  memset(&action, 0, sizeof(action));
32  sigemptyset(&action.sa_mask);
33  action.sa_handler = handler;
34  return sigaction(signal, &action, nullptr);
35 }
36 
37 
38 // Resets the signal handler to the default handler of the signal.
39 inline int reset(int signal)
40 {
41  struct sigaction action;
42  memset(&action, 0, sizeof(action));
43  sigemptyset(&action.sa_mask);
44  action.sa_handler = SIG_DFL;
45  return sigaction(signal, &action, nullptr);
46 }
47 
48 
49 // Returns true iff the signal is pending.
50 inline bool pending(int signal)
51 {
52  sigset_t set;
53  sigemptyset(&set);
54  sigpending(&set);
55  return sigismember(&set, signal);
56 }
57 
58 
59 // Returns true if the signal has been blocked, or false if the
60 // signal was already blocked.
61 inline bool block(int signal)
62 {
63  sigset_t set;
64  sigemptyset(&set);
65  sigaddset(&set, signal);
66 
67  sigset_t oldset;
68  sigemptyset(&oldset);
69 
70  // We ignore errors here as the only documented one is
71  // EINVAL due to a bad value of the SIG_* argument.
72  pthread_sigmask(SIG_BLOCK, &set, &oldset);
73 
74  return !sigismember(&oldset, signal);
75 }
76 
77 
78 // Returns true if the signal has been unblocked, or false if the
79 // signal was not previously blocked.
80 inline bool unblock(int signal)
81 {
82  sigset_t set;
83  sigemptyset(&set);
84  sigaddset(&set, signal);
85 
86  sigset_t oldset;
87  sigemptyset(&oldset);
88 
89  pthread_sigmask(SIG_UNBLOCK, &set, &oldset);
90 
91  return sigismember(&oldset, signal);
92 }
93 
94 namespace internal {
95 
96 // Suppresses a signal on the current thread for the lifetime of
97 // the Suppressor. The signal *must* be synchronous and delivered
98 // per-thread. The suppression occurs only on the thread of
99 // execution of the Suppressor.
101 {
102  Suppressor(int _signal)
103  : signal(_signal), pending(false), unblock(false)
104  {
105  // Check to see if the signal is already reported as pending.
106  // If pending, it means the thread already blocks the signal!
107  // Therefore, any new instances of the signal will also be
108  // blocked and merged with the pending one since there is no
109  // queuing for signals.
110  pending = signals::pending(signal);
111 
112  if (!pending) {
113  // Block the signal for this thread only. If already blocked,
114  // there's no need to unblock it.
115  unblock = signals::block(signal);
116  }
117  }
118 
120  {
121  // We want to preserve errno when the Suppressor drops out of
122  // scope. Otherwise, one needs to potentially store errno when
123  // using the suppress() macro.
124  int _errno = errno;
125 
126  // If the signal has become pending after we blocked it, we
127  // need to clear it before unblocking it.
128  if (!pending && signals::pending(signal)) {
129  // It is possible that in between having observed the pending
130  // signal with sigpending() and clearing it with sigwait(),
131  // the signal was delivered to another thread before we were
132  // able to clear it here. This can happen if the signal was
133  // generated for the whole process (e.g. a kill was issued).
134  // See 2.4.1 here:
135  // http://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html
136  // To handle the above scenario, one can either:
137  // 1. Use sigtimedwait() with a timeout of 0, to ensure we
138  // don't block forever. However, this only works on Linux
139  // and we may still swallow the signal intended for the
140  // process.
141  // 2. After seeing the pending signal, signal ourselves with
142  // pthread_kill prior to calling sigwait(). This can still
143  // swallow the signal intended for the process.
144  // We chose to use the latter technique as it works on all
145  // POSIX systems and is less likely to swallow process signals,
146  // provided the thread signal and process signal are not merged.
147 
148  // Delivering on this thread an extra time will require an extra sigwait
149  // call on FreeBSD, so we skip it.
150 #ifndef __FreeBSD__
151  pthread_kill(pthread_self(), signal);
152 #endif
153 
154  sigset_t mask;
155  sigemptyset(&mask);
156  sigaddset(&mask, signal);
157 
158  int result;
159  do {
160  int _ignored;
161  result = sigwait(&mask, &_ignored);
162  } while (result == -1 && errno == EINTR);
163  }
164 
165  // Unblock the signal (only if we were the ones to block it).
166  if (unblock) {
167  signals::unblock(signal);
168  }
169 
170  // Restore errno.
171  errno = _errno;
172  }
173 
174  // Needed for the suppress() macro.
175  operator bool() { return true; }
176 private:
177  const int signal;
178  bool pending; // Whether the signal is already pending.
179  bool unblock; // Whether to unblock the signal on destruction.
180 };
181 
182 } // namespace internal {
183 
184 } // namespace signals {
185 
186 } // namespace os {
187 
188 #endif // __STOUT_OS_POSIX_SIGNALS_HPP__
bool pending(int signal)
Definition: signals.hpp:50
int install(int signal, void(*handler)(int))
Definition: signals.hpp:28
~Suppressor()
Definition: signals.hpp:119
Suppressor(int _signal)
Definition: signals.hpp:102
Definition: signals.hpp:100
bool block(int signal)
Definition: signals.hpp:61
int reset(int signal)
Definition: signals.hpp:39
bool unblock(int signal)
Definition: signals.hpp:80