/home/travis/build/MoarVM/MoarVM/src/io/signals.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "moar.h" |
2 | | |
3 | | #include <signal.h> |
4 | | #ifdef _WIN32 |
5 | | /* Add signals used by libuv but not defined in the Windows signal.h */ |
6 | | #define SIGHUP 1 |
7 | | #define SIGKILL 9 |
8 | | #define SIGBREAK 21 |
9 | | #define SIGWINCH 28 |
10 | | #endif |
11 | | |
12 | | /* Info we convey about a signal handler. */ |
13 | | typedef struct { |
14 | | int signum; |
15 | | uv_signal_t handle; |
16 | | MVMThreadContext *tc; |
17 | | int work_idx; |
18 | | } SignalInfo; |
19 | | |
20 | | /* Signal callback; dispatches schedulee to the queue. */ |
21 | 0 | static void signal_cb(uv_signal_t *handle, int sig_num) { |
22 | 0 | SignalInfo *si = (SignalInfo *)handle->data; |
23 | 0 | MVMThreadContext *tc = si->tc; |
24 | 0 | MVMObject *arr = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTArray); |
25 | 0 | MVMAsyncTask *t = MVM_io_eventloop_get_active_work(tc, si->work_idx); |
26 | 0 | MVM_repr_push_o(tc, arr, t->body.schedulee); |
27 | 0 | MVMROOT2(tc, t, arr, { |
28 | 0 | MVMObject *sig_num_boxed = MVM_repr_box_int(tc, |
29 | 0 | tc->instance->boot_types.BOOTInt, sig_num); |
30 | 0 | MVM_repr_push_o(tc, arr, sig_num_boxed); |
31 | 0 | }); |
32 | 0 | MVM_repr_push_o(tc, t->body.queue, arr); |
33 | 0 | } |
34 | | |
35 | | /* Sets the signal handler up on the event loop. */ |
36 | 0 | static void setup(MVMThreadContext *tc, uv_loop_t *loop, MVMObject *async_task, void *data) { |
37 | 0 | SignalInfo *si = (SignalInfo *)data; |
38 | 0 | uv_signal_init(loop, &si->handle); |
39 | 0 | si->work_idx = MVM_io_eventloop_add_active_work(tc, async_task); |
40 | 0 | si->tc = tc; |
41 | 0 | si->handle.data = si; |
42 | 0 | uv_signal_start(&si->handle, signal_cb, si->signum); |
43 | 0 | } |
44 | | |
45 | 0 | static void cancel(MVMThreadContext *tc, uv_loop_t *loop, MVMObject *async_task, void *data) { |
46 | 0 | SignalInfo *si = (SignalInfo *)data; |
47 | 0 | if (si->work_idx >= 0) { |
48 | 0 | if (!uv_is_closing((uv_handle_t *)&(si->handle))) |
49 | 0 | uv_signal_stop(&si->handle); |
50 | 0 | MVM_io_eventloop_remove_active_work(tc, &(si->work_idx)); |
51 | 0 | } |
52 | 0 | } |
53 | | |
54 | | /* Frees data associated with a timer async task. */ |
55 | 0 | static void gc_free(MVMThreadContext *tc, MVMObject *t, void *data) { |
56 | 0 | if (data) |
57 | 0 | MVM_free(data); |
58 | 0 | } |
59 | | |
60 | | /* Operations table for async timer task. */ |
61 | | static const MVMAsyncTaskOps op_table = { |
62 | | setup, |
63 | | NULL, |
64 | | cancel, |
65 | | NULL, |
66 | | gc_free |
67 | | }; |
68 | | |
69 | 0 | #define NUM_SIG_WANTED 35 |
70 | | #define PROCESS_SIGS(X) \ |
71 | | X( MVM_SIGHUP ) \ |
72 | | X( MVM_SIGINT ) \ |
73 | | X( MVM_SIGQUIT ) \ |
74 | | X( MVM_SIGILL ) \ |
75 | | X( MVM_SIGTRAP ) \ |
76 | | X( MVM_SIGABRT ) \ |
77 | | X( MVM_SIGEMT ) \ |
78 | | X( MVM_SIGFPE ) \ |
79 | | X( MVM_SIGKILL ) \ |
80 | | X( MVM_SIGBUS ) \ |
81 | | X( MVM_SIGSEGV ) \ |
82 | | X( MVM_SIGSYS ) \ |
83 | | X( MVM_SIGPIPE ) \ |
84 | | X( MVM_SIGALRM ) \ |
85 | | X( MVM_SIGTERM ) \ |
86 | | X( MVM_SIGURG ) \ |
87 | | X( MVM_SIGSTOP ) \ |
88 | | X( MVM_SIGTSTP ) \ |
89 | | X( MVM_SIGCONT ) \ |
90 | | X( MVM_SIGCHLD ) \ |
91 | | X( MVM_SIGTTIN ) \ |
92 | | X( MVM_SIGTTOU ) \ |
93 | | X( MVM_SIGIO ) \ |
94 | | X( MVM_SIGXCPU ) \ |
95 | | X( MVM_SIGXFSZ ) \ |
96 | | X( MVM_SIGVTALRM ) \ |
97 | | X( MVM_SIGPROF ) \ |
98 | | X( MVM_SIGWINCH ) \ |
99 | | X( MVM_SIGINFO ) \ |
100 | | X( MVM_SIGUSR1 ) \ |
101 | | X( MVM_SIGUSR2 ) \ |
102 | | X( MVM_SIGTHR ) \ |
103 | | X( MVM_SIGSTKFLT ) \ |
104 | | X( MVM_SIGPWR ) \ |
105 | | X( MVM_SIGBREAK ) |
106 | | |
107 | | #define GEN_ENUMS(v) v, |
108 | | #define GEN_STRING(v) #v, |
109 | | |
110 | | static enum { |
111 | | PROCESS_SIGS(GEN_ENUMS) |
112 | | } MVM_sig_names; |
113 | | |
114 | | static char const * const SIG_WANTED[NUM_SIG_WANTED] = { |
115 | | PROCESS_SIGS(GEN_STRING) |
116 | | }; |
117 | | |
118 | 0 | static void populate_sig_values(MVMint8 *sig_vals) { |
119 | 0 | MVMint8 i; |
120 | 0 | for (i = 0; i < NUM_SIG_WANTED; i++) { sig_vals[i] = 0; } |
121 | 0 |
|
122 | 0 | #ifdef SIGHUP |
123 | 0 | sig_vals[MVM_SIGHUP] = SIGHUP; |
124 | 0 | #endif |
125 | 0 | #ifdef SIGINT |
126 | 0 | sig_vals[MVM_SIGINT] = SIGINT; |
127 | 0 | #endif |
128 | 0 | #ifdef SIGQUIT |
129 | 0 | sig_vals[MVM_SIGQUIT] = SIGQUIT; |
130 | 0 | #endif |
131 | 0 | #ifdef SIGILL |
132 | 0 | sig_vals[MVM_SIGILL] = SIGILL; |
133 | 0 | #endif |
134 | 0 | #ifdef SIGTRAP |
135 | 0 | sig_vals[MVM_SIGTRAP] = SIGTRAP; |
136 | 0 | #endif |
137 | 0 | #ifdef SIGABRT |
138 | 0 | sig_vals[MVM_SIGABRT] = SIGABRT; |
139 | 0 | #endif |
140 | 0 | #ifdef SIGEMT |
141 | | sig_vals[MVM_SIGEMT] = SIGEMT; |
142 | | #endif |
143 | 0 | #ifdef SIGFPE |
144 | 0 | sig_vals[MVM_SIGFPE] = SIGFPE; |
145 | 0 | #endif |
146 | 0 | #ifdef SIGKILL |
147 | 0 | sig_vals[MVM_SIGKILL] = SIGKILL; |
148 | 0 | #endif |
149 | 0 | #ifdef SIGBUS |
150 | 0 | sig_vals[MVM_SIGBUS] = SIGBUS; |
151 | 0 | #endif |
152 | 0 | #ifdef SIGSEGV |
153 | 0 | sig_vals[MVM_SIGSEGV] = SIGSEGV; |
154 | 0 | #endif |
155 | 0 | #ifdef SIGSYS |
156 | 0 | sig_vals[MVM_SIGSYS] = SIGSYS; |
157 | 0 | #endif |
158 | 0 | #ifdef SIGPIPE |
159 | 0 | sig_vals[MVM_SIGPIPE] = SIGPIPE; |
160 | 0 | #endif |
161 | 0 | #ifdef SIGALRM |
162 | 0 | sig_vals[MVM_SIGALRM] = SIGALRM; |
163 | 0 | #endif |
164 | 0 | #ifdef SIGTERM |
165 | 0 | sig_vals[MVM_SIGTERM] = SIGTERM; |
166 | 0 | #endif |
167 | 0 | #ifdef SIGURG |
168 | 0 | sig_vals[MVM_SIGURG] = SIGURG; |
169 | 0 | #endif |
170 | 0 | #ifdef SIGSTOP |
171 | 0 | sig_vals[MVM_SIGSTOP] = SIGSTOP; /* hammer time */ |
172 | 0 | #endif |
173 | 0 | #ifdef SIGTSTP |
174 | 0 | sig_vals[MVM_SIGTSTP] = SIGTSTP; |
175 | 0 | #endif |
176 | 0 | #ifdef SIGCONT |
177 | 0 | sig_vals[MVM_SIGCONT] = SIGCONT; |
178 | 0 | #endif |
179 | 0 | #ifdef SIGCHLD |
180 | 0 | sig_vals[MVM_SIGCHLD] = SIGCHLD; |
181 | 0 | #endif |
182 | 0 | #ifdef SIGTTIN |
183 | 0 | sig_vals[MVM_SIGTTIN] = SIGTTIN; |
184 | 0 | #endif |
185 | 0 | #ifdef SIGTTOU |
186 | 0 | sig_vals[MVM_SIGTTOU] = SIGTTOU; |
187 | 0 | #endif |
188 | 0 | #ifdef SIGIO |
189 | 0 | sig_vals[MVM_SIGIO] = SIGIO; |
190 | 0 | #endif |
191 | 0 | #ifdef SIGXCPU |
192 | 0 | sig_vals[MVM_SIGXCPU] = SIGXCPU; |
193 | 0 | #endif |
194 | 0 | #ifdef SIGXFSZ |
195 | 0 | sig_vals[MVM_SIGXFSZ] = SIGXFSZ; |
196 | 0 | #endif |
197 | 0 | #ifdef SIGVTALRM |
198 | 0 | sig_vals[MVM_SIGVTALRM] = SIGVTALRM; |
199 | 0 | #endif |
200 | 0 | #ifdef SIGPROF |
201 | 0 | sig_vals[MVM_SIGPROF] = SIGPROF; |
202 | 0 | #endif |
203 | 0 | #ifdef SIGWINCH |
204 | 0 | sig_vals[MVM_SIGWINCH] = SIGWINCH; |
205 | 0 | #endif |
206 | 0 | #ifdef SIGINFO |
207 | | sig_vals[MVM_SIGINFO] = SIGINFO; |
208 | | #endif |
209 | 0 | #ifdef SIGUSR1 |
210 | 0 | sig_vals[MVM_SIGUSR1] = SIGUSR1; |
211 | 0 | #endif |
212 | 0 | #ifdef SIGUSR2 |
213 | 0 | sig_vals[MVM_SIGUSR2] = SIGUSR2; |
214 | 0 | #endif |
215 | 0 | #ifdef SIGTHR |
216 | | sig_vals[MVM_SIGTHR] = SIGTHR; |
217 | | #endif |
218 | 0 | #ifdef SIGSTKFLT |
219 | 0 | sig_vals[MVM_SIGSTKFLT] = SIGSTKFLT; |
220 | 0 | #endif |
221 | 0 | #ifdef SIGPWR |
222 | 0 | sig_vals[MVM_SIGPWR] = SIGPWR; |
223 | 0 | #endif |
224 | 0 | #ifdef SIGBREAK |
225 | | sig_vals[MVM_SIGBREAK] = SIGBREAK; |
226 | | #endif |
227 | 0 | } |
228 | | |
229 | 0 | #define SIG_SHIFT(s) (1 << ((s) - 1)) |
230 | | |
231 | 0 | static void populate_instance_valid_sigs(MVMThreadContext *tc, MVMint8 *sig_vals) { |
232 | 0 | MVMuint64 valid_sigs = 0; |
233 | 0 | MVMint8 i; |
234 | 0 |
|
235 | 0 | if ( tc->instance->valid_sigs ) return; |
236 | 0 |
|
237 | 0 | for (i = 0; i < NUM_SIG_WANTED; i++) { |
238 | 0 | if (sig_vals[i]) { |
239 | 0 | valid_sigs |= SIG_SHIFT(sig_vals[i]); |
240 | 0 | } |
241 | 0 | } |
242 | 0 | tc->instance->valid_sigs = valid_sigs; |
243 | 0 | } |
244 | | |
245 | 0 | MVMObject * MVM_io_get_signals(MVMThreadContext *tc) { |
246 | 0 | MVMInstance * const instance = tc->instance; |
247 | 0 | MVMHLLConfig * hll = MVM_hll_current(tc); |
248 | 0 | MVMObject * sig_arr; |
249 | 0 |
|
250 | 0 | MVMint8 sig_wanted_vals[NUM_SIG_WANTED]; |
251 | 0 | populate_sig_values(sig_wanted_vals); |
252 | 0 |
|
253 | 0 | if (instance->sig_arr) { |
254 | 0 | return instance->sig_arr; |
255 | 0 | } |
256 | 0 |
|
257 | 0 | sig_arr = MVM_repr_alloc_init(tc, hll->slurpy_array_type); |
258 | 0 | MVMROOT(tc, sig_arr, { |
259 | 0 | MVMint8 i; |
260 | 0 | for (i = 0; i < NUM_SIG_WANTED; i++) { |
261 | 0 | MVMObject *key = NULL; |
262 | 0 | MVMString *full_key = NULL; |
263 | 0 | MVMObject *val = NULL; |
264 | 0 |
|
265 | 0 | MVMROOT3(tc, key, full_key, val, { |
266 | 0 | full_key = MVM_string_utf8_c8_decode( |
267 | 0 | tc, instance->VMString, SIG_WANTED[i], strlen(SIG_WANTED[i]) |
268 | 0 | ); |
269 | 0 |
|
270 | 0 | key = MVM_repr_box_str(tc, hll->str_box_type, |
271 | 0 | MVM_string_substring(tc, full_key, 4, -1) |
272 | 0 | ); |
273 | 0 | val = MVM_repr_box_int(tc, hll->int_box_type, sig_wanted_vals[i]); |
274 | 0 |
|
275 | 0 | MVM_repr_push_o(tc, sig_arr, key); |
276 | 0 | MVM_repr_push_o(tc, sig_arr, val); |
277 | 0 | }); |
278 | 0 | } |
279 | 0 |
|
280 | 0 | populate_instance_valid_sigs(tc, sig_wanted_vals); |
281 | 0 | instance->sig_arr = sig_arr; |
282 | 0 | }); |
283 | 0 |
|
284 | 0 | return sig_arr; |
285 | 0 | } |
286 | | |
287 | | /* Register a new signal handler. */ |
288 | | MVMObject * MVM_io_signal_handle(MVMThreadContext *tc, MVMObject *queue, |
289 | | MVMObject *schedulee, MVMint64 signal, |
290 | 0 | MVMObject *async_type) { |
291 | 0 | MVMAsyncTask *task; |
292 | 0 | SignalInfo *signal_info; |
293 | 0 | MVMInstance * const instance = tc->instance; |
294 | 0 |
|
295 | 0 | if ( !instance->valid_sigs ) { |
296 | 0 | MVMint8 sig_wanted_vals[NUM_SIG_WANTED]; |
297 | 0 | populate_sig_values(sig_wanted_vals); |
298 | 0 | populate_instance_valid_sigs(tc, sig_wanted_vals); |
299 | 0 | } |
300 | 0 | if ( signal <= 0 || !(instance->valid_sigs & SIG_SHIFT(signal) ) ) { |
301 | 0 | MVM_exception_throw_adhoc(tc, "Unsupported signal handler %d", |
302 | 0 | (int)signal); |
303 | 0 | } |
304 | 0 |
|
305 | 0 | /* Validate REPRs. */ |
306 | 0 | if (REPR(queue)->ID != MVM_REPR_ID_ConcBlockingQueue) |
307 | 0 | MVM_exception_throw_adhoc(tc, |
308 | 0 | "signal target queue must have ConcBlockingQueue REPR"); |
309 | 0 | if (REPR(async_type)->ID != MVM_REPR_ID_MVMAsyncTask) |
310 | 0 | MVM_exception_throw_adhoc(tc, |
311 | 0 | "signal result type must have REPR AsyncTask"); |
312 | 0 |
|
313 | 0 | /* Create async task handle. */ |
314 | 0 | MVMROOT2(tc, queue, schedulee, { |
315 | 0 | task = (MVMAsyncTask *)MVM_repr_alloc_init(tc, async_type); |
316 | 0 | }); |
317 | 0 | MVM_ASSIGN_REF(tc, &(task->common.header), task->body.queue, queue); |
318 | 0 | MVM_ASSIGN_REF(tc, &(task->common.header), task->body.schedulee, schedulee); |
319 | 0 | task->body.ops = &op_table; |
320 | 0 | signal_info = MVM_malloc(sizeof(SignalInfo)); |
321 | 0 | signal_info->signum = signal; |
322 | 0 | task->body.data = signal_info; |
323 | 0 |
|
324 | 0 | /* Hand the task off to the event loop. */ |
325 | 0 | MVMROOT(tc, task, { |
326 | 0 | MVM_io_eventloop_queue_work(tc, (MVMObject *)task); |
327 | 0 | }); |
328 | 0 |
|
329 | 0 | return (MVMObject *)task; |
330 | 0 | } |