04d06b3642752a5db31bd38f52a5519c60bc2140
[m6w6/libmemcached] / libtest / signal.cc
1 /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
2 *
3 * libtest
4 *
5 * Copyright (C) 2011 Data Differential, http://datadifferential.com/
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 3 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22
23 #include <config.h>
24 #include <libtest/common.h>
25
26 #include <csignal>
27
28 #include <libtest/signal.h>
29
30 using namespace libtest;
31
32 #define MAGIC_MEMORY 123569
33
34 bool SignalThread::is_shutdown()
35 {
36 bool ret;
37 pthread_mutex_lock(&shutdown_mutex);
38 ret= bool(__shutdown != SHUTDOWN_RUNNING);
39 pthread_mutex_unlock(&shutdown_mutex);
40
41 return ret;
42 }
43
44 void SignalThread::set_shutdown(shutdown_t arg)
45 {
46 pthread_mutex_lock(&shutdown_mutex);
47 __shutdown= arg;
48 pthread_mutex_unlock(&shutdown_mutex);
49
50 if (arg == SHUTDOWN_GRACEFUL)
51 {
52 if (pthread_kill(thread, SIGUSR2) == 0)
53 {
54 void *retval;
55 pthread_join(thread, &retval);
56 }
57 }
58 }
59
60 shutdown_t SignalThread::get_shutdown()
61 {
62 shutdown_t local;
63 pthread_mutex_lock(&shutdown_mutex);
64 local= __shutdown;
65 pthread_mutex_unlock(&shutdown_mutex);
66
67 return local;
68 }
69
70 void SignalThread::post()
71 {
72 sem_post(&lock);
73 }
74
75 void SignalThread::test()
76 {
77 assert(magic_memory == MAGIC_MEMORY);
78 if (bool(getenv("LIBTEST_IN_GDB")) == false)
79 {
80 assert(sigismember(&set, SIGABRT));
81 assert(sigismember(&set, SIGQUIT));
82 assert(sigismember(&set, SIGINT));
83 }
84 assert(sigismember(&set, SIGUSR2));
85 }
86
87 SignalThread::~SignalThread()
88 {
89 if (is_shutdown() == false)
90 {
91 set_shutdown(SHUTDOWN_GRACEFUL);
92 }
93
94 #if 0
95 if (pthread_equal(thread, pthread_self()) != 0 and (pthread_kill(thread, 0) == ESRCH) == true)
96 {
97 void *retval;
98 pthread_join(thread, &retval);
99 }
100 #endif
101 sem_destroy(&lock);
102
103 int error;
104 if ((error= pthread_sigmask(SIG_UNBLOCK, &set, NULL)) != 0)
105 {
106 Error << "While trying to reset signal mask to original set, pthread_sigmask() died during pthread_sigmask(" << strerror(error) << ")";
107 }
108 }
109
110 extern "C" {
111
112 static void *sig_thread(void *arg)
113 {
114 SignalThread *context= (SignalThread*)arg;
115
116 context->test();
117 context->post();
118
119 while (context->get_shutdown() == SHUTDOWN_RUNNING)
120 {
121 int sig;
122
123 if (context->wait(sig) == -1)
124 {
125 Error << "sigwait() returned errno:" << strerror(errno);
126 continue;
127 }
128
129 switch (sig)
130 {
131 case SIGABRT:
132 case SIGUSR2:
133 case SIGINT:
134 case SIGQUIT:
135 if (context->is_shutdown() == false)
136 {
137 context->set_shutdown(SHUTDOWN_FORCED);
138 }
139 break;
140 case SIGPIPE:
141 {
142 Error << "Ignoring SIGPIPE";
143 }
144 break;
145
146 case 0:
147 Error << "Inside of gdb";
148 break;
149
150 default:
151 Error << "Signal handling thread got unexpected signal " << strsignal(sig);
152 break;
153 }
154 }
155
156 return NULL;
157 }
158
159 }
160
161 SignalThread::SignalThread() :
162 magic_memory(MAGIC_MEMORY),
163 thread(pthread_self())
164 {
165 pthread_mutex_init(&shutdown_mutex, NULL);
166 sigemptyset(&set);
167 if (bool(getenv("LIBTEST_IN_GDB")) == false)
168 {
169 sigaddset(&set, SIGABRT);
170 sigaddset(&set, SIGQUIT);
171 sigaddset(&set, SIGINT);
172 }
173 sigaddset(&set, SIGPIPE);
174
175 sigaddset(&set, SIGUSR2);
176
177 sem_init(&lock, 0, 0);
178
179 sigemptyset(&original_set);
180 pthread_sigmask(SIG_BLOCK, NULL, &original_set);
181 }
182
183
184 bool SignalThread::setup()
185 {
186 set_shutdown(SHUTDOWN_RUNNING);
187
188 if (sigismember(&original_set, SIGQUIT))
189 {
190 Error << strsignal(SIGQUIT) << " has been previously set.";
191 }
192 if (sigismember(&original_set, SIGINT))
193 {
194 Error << strsignal(SIGINT) << " has been previously set.";
195 }
196 if (sigismember(&original_set, SIGUSR2))
197 {
198 Error << strsignal(SIGUSR2) << " has been previously set.";
199 }
200
201 int error;
202 if ((error= pthread_sigmask(SIG_BLOCK, &set, NULL)) != 0)
203 {
204 Error << "pthread_sigmask() died during pthread_sigmask(" << strerror(error) << ")";
205 return false;
206 }
207
208 if ((error= pthread_create(&thread, NULL, &sig_thread, this)) != 0)
209 {
210 Error << "pthread_create() died during pthread_create(" << strerror(error) << ")";
211 return false;
212 }
213
214 sem_wait(&lock);
215
216 return true;
217 }