42e6d78da39234032bb3736aa6df3ccfe22c5ad0
[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 <libtest/common.h>
24
25 #include <csignal>
26
27 #include <libtest/signal.h>
28
29 using namespace libtest;
30
31 #define MAGIC_MEMORY 123569
32
33 bool SignalThread::is_shutdown()
34 {
35 bool ret;
36 pthread_mutex_lock(&shutdown_mutex);
37 ret= bool(__shutdown != SHUTDOWN_RUNNING);
38 pthread_mutex_unlock(&shutdown_mutex);
39
40 return ret;
41 }
42
43 void SignalThread::set_shutdown(shutdown_t arg)
44 {
45 pthread_mutex_lock(&shutdown_mutex);
46 __shutdown= arg;
47 pthread_mutex_unlock(&shutdown_mutex);
48
49 if (arg == SHUTDOWN_GRACEFUL)
50 {
51 if (pthread_kill(thread, SIGUSR2) == 0)
52 {
53 void *retval;
54 pthread_join(thread, &retval);
55 }
56 }
57 }
58
59 shutdown_t SignalThread::get_shutdown()
60 {
61 shutdown_t local;
62 pthread_mutex_lock(&shutdown_mutex);
63 local= __shutdown;
64 pthread_mutex_unlock(&shutdown_mutex);
65
66 return local;
67 }
68
69 void SignalThread::post()
70 {
71 sem_post(&lock);
72 }
73
74 void SignalThread::test()
75 {
76 assert(magic_memory == MAGIC_MEMORY);
77 if (not getenv("LIBTEST_IN_GDB"))
78 {
79 assert(sigismember(&set, SIGABRT));
80 assert(sigismember(&set, SIGQUIT));
81 assert(sigismember(&set, SIGINT));
82 }
83 assert(sigismember(&set, SIGUSR2));
84 }
85
86 SignalThread::~SignalThread()
87 {
88 if (not is_shutdown())
89 {
90 set_shutdown(SHUTDOWN_GRACEFUL);
91 }
92
93 #if 0
94 if (pthread_equal(thread, pthread_self()) != 0 and (pthread_kill(thread, 0) == ESRCH) == true)
95 {
96 void *retval;
97 pthread_join(thread, &retval);
98 }
99 #endif
100 sem_destroy(&lock);
101
102 int error;
103 if ((error= pthread_sigmask(SIG_UNBLOCK, &set, NULL)) != 0)
104 {
105 Error << "While trying to reset signal mask to original set, pthread_sigmask() died during pthread_sigmask(" << strerror(error) << ")";
106 }
107 }
108
109 extern "C" {
110
111 static void *sig_thread(void *arg)
112 {
113 SignalThread *context= (SignalThread*)arg;
114
115 context->test();
116 context->post();
117
118 while (context->get_shutdown() == SHUTDOWN_RUNNING)
119 {
120 int sig;
121
122 if (context->wait(sig) == -1)
123 {
124 Error << "sigwait() returned errno:" << strerror(errno);
125 continue;
126 }
127
128 switch (sig)
129 {
130 case SIGABRT:
131 case SIGUSR2:
132 case SIGINT:
133 case SIGQUIT:
134 if (context->is_shutdown() == false)
135 {
136 context->set_shutdown(SHUTDOWN_FORCED);
137 }
138 break;
139 case SIGPIPE:
140 {
141 Error << "Ignoring SIGPIPE";
142 }
143 break;
144
145 default:
146 Error << "Signal handling thread got unexpected signal " << strsignal(sig);
147 break;
148 }
149 }
150
151 return NULL;
152 }
153
154 }
155
156 SignalThread::SignalThread() :
157 magic_memory(MAGIC_MEMORY),
158 thread(pthread_self())
159 {
160 pthread_mutex_init(&shutdown_mutex, NULL);
161 sigemptyset(&set);
162 if (not getenv("LIBTEST_IN_GDB"))
163 {
164 sigaddset(&set, SIGABRT);
165 sigaddset(&set, SIGQUIT);
166 sigaddset(&set, SIGINT);
167 }
168 sigaddset(&set, SIGPIPE);
169
170 sigaddset(&set, SIGUSR2);
171
172 sem_init(&lock, 0, 0);
173
174 sigemptyset(&original_set);
175 pthread_sigmask(SIG_BLOCK, NULL, &original_set);
176 }
177
178
179 bool SignalThread::setup()
180 {
181 set_shutdown(SHUTDOWN_RUNNING);
182
183 if (sigismember(&original_set, SIGQUIT))
184 {
185 Error << strsignal(SIGQUIT) << " has been previously set.";
186 }
187 if (sigismember(&original_set, SIGINT))
188 {
189 Error << strsignal(SIGINT) << " has been previously set.";
190 }
191 if (sigismember(&original_set, SIGUSR2))
192 {
193 Error << strsignal(SIGUSR2) << " has been previously set.";
194 }
195
196 int error;
197 if ((error= pthread_sigmask(SIG_BLOCK, &set, NULL)) != 0)
198 {
199 Error << "pthread_sigmask() died during pthread_sigmask(" << strerror(error) << ")";
200 return false;
201 }
202
203 if ((error= pthread_create(&thread, NULL, &sig_thread, this)) != 0)
204 {
205 Error << "pthread_create() died during pthread_create(" << strerror(error) << ")";
206 return false;
207 }
208
209 sem_wait(&lock);
210
211 return true;
212 }