New tests.
[mono.git] / support / signal.c
1 /*
2  * <signal.h> wrapper functions.
3  *
4  * Authors:
5  *   Jonathan Pryor (jonpryor@vt.edu)
6  *   Jonathan Pryor (jpryor@novell.com)
7  *   Tim Jenks (tim.jenks@realtimeworlds.com)
8  *
9  * Copyright (C) 2004-2005 Jonathan Pryor
10  * Copyright (C) 2008 Novell, Inc.
11  */
12
13 #include <signal.h>
14
15 #include "map.h"
16 #include "mph.h"
17
18 #ifndef HOST_WIN32
19 #include <sys/time.h>
20 #include <sys/types.h>
21 #include <poll.h>
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <pthread.h>
26 #include <mono/io-layer/atomic.h>
27 #include <mono/metadata/appdomain.h>
28 #endif
29
30 G_BEGIN_DECLS
31
32 typedef void (*mph_sighandler_t)(int);
33 typedef struct Mono_Unix_UnixSignal_SignalInfo signal_info;
34
35 static int count_handlers (int signum);
36
37 void*
38 Mono_Posix_Stdlib_SIG_DFL (void)
39 {
40         return SIG_DFL;
41 }
42
43 void*
44 Mono_Posix_Stdlib_SIG_ERR (void)
45 {
46         return SIG_ERR;
47 }
48
49 void*
50 Mono_Posix_Stdlib_SIG_IGN (void)
51 {
52         return SIG_IGN;
53 }
54
55 void
56 Mono_Posix_Stdlib_InvokeSignalHandler (int signum, void *handler)
57 {
58         mph_sighandler_t _h = (mph_sighandler_t) handler;
59         _h (signum);
60 }
61
62 int Mono_Posix_SIGRTMIN (void)
63 {
64 #ifdef SIGRTMIN
65         return SIGRTMIN;
66 #else /* def SIGRTMIN */
67         return -1;
68 #endif /* ndef SIGRTMIN */
69 }
70
71 int Mono_Posix_SIGRTMAX (void)
72 {
73 #ifdef SIGRTMAX
74         return SIGRTMAX;
75 #else /* def SIGRTMAX */
76         return -1;
77 #endif /* ndef SIGRTMAX */
78 }
79
80 int Mono_Posix_FromRealTimeSignum (int offset, int *r)
81 {
82         if (NULL == r) {
83                 errno = EINVAL;
84                 return -1;
85         }
86         *r = 0;
87 #if defined (SIGRTMIN) && defined (SIGRTMAX)
88         if ((offset < 0) || (SIGRTMIN > SIGRTMAX - offset)) {
89                 errno = EINVAL;
90                 return -1;
91         }
92         *r = SIGRTMIN+offset;
93         return 0;
94 #else /* defined (SIGRTMIN) && defined (SIGRTMAX) */
95 # ifdef ENOSYS
96         errno = ENOSYS;
97 # endif /* ENOSYS */
98         return -1;
99 #endif /* defined (SIGRTMIN) && defined (SIGRTMAX) */
100 }
101
102 #ifndef HOST_WIN32
103
104 #ifdef WAPI_ATOMIC_ASM
105         #define mph_int_get(p)     InterlockedExchangeAdd ((p), 0)
106         #define mph_int_inc(p)     InterlockedIncrement ((p))
107         #define mph_int_dec_test(p)     (InterlockedDecrement ((p)) == 0)
108         #define mph_int_set(p,o,n) InterlockedExchange ((p), (n))
109 #elif GLIB_CHECK_VERSION(2,4,0)
110         #define mph_int_get(p) g_atomic_int_get ((p))
111         #define mph_int_inc(p) do {g_atomic_int_inc ((p));} while (0)
112         #define mph_int_dec_test(p) g_atomic_int_dec_and_test ((p))
113         #define mph_int_set(p,o,n) do {                                 \
114                 while (!g_atomic_int_compare_and_exchange ((p), (o), (n))) {} \
115         } while (0)
116 #else
117         #define mph_int_get(p) (*(p))
118         #define mph_int_inc(p) do { (*(p))++; } while (0)
119         #define mph_int_dec_test(p) (--(*(p)) == 0)
120         #define mph_int_set(p,o,n) do { *(p) = n; } while (0)
121 #endif
122
123 #if HAVE_PSIGNAL
124 int
125 Mono_Posix_Syscall_psignal (int sig, const char* s)
126 {
127         errno = 0;
128         psignal (sig, s);
129         return errno == 0 ? 0 : -1;
130 }
131 #endif  /* def HAVE_PSIGNAL */
132
133 #define NUM_SIGNALS 64
134 static signal_info signals[NUM_SIGNALS];
135
136 static int acquire_mutex (pthread_mutex_t *mutex)
137 {
138         int mr;
139         while ((mr = pthread_mutex_lock (mutex)) == EAGAIN) {
140                 /* try to acquire again */
141         }
142         if ((mr != 0) && (mr != EDEADLK))  {
143                 errno = mr;
144                 return -1;
145         }
146         return 0;
147 }
148
149 static void release_mutex (pthread_mutex_t *mutex)
150 {
151         int mr;
152         while ((mr = pthread_mutex_unlock (mutex)) == EAGAIN) {
153                 /* try to release mutex again */
154         }
155 }
156
157 static inline int
158 keep_trying (int r)
159 {
160         return r == -1 && errno == EINTR;
161 }
162
163 static void
164 default_handler (int signum)
165 {
166         int i;
167         for (i = 0; i < NUM_SIGNALS; ++i) {
168                 int fd;
169                 signal_info* h = &signals [i];
170                 if (mph_int_get (&h->signum) != signum)
171                         continue;
172                 mph_int_inc (&h->count);
173                 fd = mph_int_get (&h->write_fd);
174                 if (fd > 0) {
175                         int j,pipecounter;
176                         char c = signum;
177                         pipecounter = mph_int_get (&h->pipecnt);
178                         for (j = 0; j < pipecounter; ++j) {
179                                 int r;
180                                 do { r = write (fd, &c, 1); } while (keep_trying (r));
181                                 fsync (fd); /* force */
182                         }
183                 }
184         }
185 }
186
187 static pthread_mutex_t signals_mutex = PTHREAD_MUTEX_INITIALIZER;
188
189 void*
190 Mono_Unix_UnixSignal_install (int sig)
191 {
192         int i;
193         signal_info* h = NULL; 
194         int have_handler = 0;
195         void* handler = NULL;
196
197         if (acquire_mutex (&signals_mutex) == -1)
198                 return NULL;
199
200 #if defined (SIGRTMIN) && defined (SIGRTMAX)
201         /*The runtime uses some rt signals for itself so it's important to not override them.*/
202         if (sig >= SIGRTMIN && sig <= SIGRTMAX && count_handlers (sig) == 0) {
203                 struct sigaction sinfo;
204                 sigaction (sig, NULL, &sinfo);
205                 if (sinfo.sa_handler != SIG_DFL || (void*)sinfo.sa_sigaction != (void*)SIG_DFL) {
206                         pthread_mutex_unlock (&signals_mutex);
207                         errno = EADDRINUSE;
208                         return NULL;
209                 }
210         }
211 #endif /*defined (SIGRTMIN) && defined (SIGRTMAX)*/
212
213         for (i = 0; i < NUM_SIGNALS; ++i) {
214                 if (h == NULL && signals [i].signum == 0) {
215                         h = &signals [i];
216                         h->handler = signal (sig, default_handler);
217                         if (h->handler == SIG_ERR) {
218                                 h->handler = NULL;
219                                 h = NULL;
220                                 break;
221                         }
222                         else {
223                                 h->have_handler = 1;
224                         }
225                 }
226                 if (!have_handler && signals [i].signum == sig &&
227                                 signals [i].handler != default_handler) {
228                         have_handler = 1;
229                         handler = signals [i].handler;
230                 }
231                 if (h && have_handler)
232                         break;
233         }
234
235         if (h && have_handler) {
236                 h->have_handler = 1;
237                 h->handler      = handler;
238         }
239
240         if (h) {
241                 mph_int_set (&h->count, h->count, 0);
242                 mph_int_set (&h->signum, h->signum, sig);
243                 mph_int_set (&h->pipecnt, h->pipecnt, 0);
244         }
245
246         release_mutex (&signals_mutex);
247
248         return h;
249 }
250
251 static int
252 count_handlers (int signum)
253 {
254         int i;
255         int count = 0;
256         for (i = 0; i < NUM_SIGNALS; ++i) {
257                 if (signals [i].signum == signum)
258                         ++count;
259         }
260         return count;
261 }
262
263 int
264 Mono_Unix_UnixSignal_uninstall (void* info)
265 {
266         signal_info* h;
267         int r = -1;
268
269         if (acquire_mutex (&signals_mutex) == -1)
270                 return -1;
271
272         h = info;
273
274         if (h == NULL || h < signals || h > &signals [NUM_SIGNALS])
275                 errno = EINVAL;
276         else {
277                 /* last UnixSignal -- we can unregister */
278                 if (h->have_handler && count_handlers (h->signum) == 1) {
279                         mph_sighandler_t p = signal (h->signum, h->handler);
280                         if (p != SIG_ERR)
281                                 r = 0;
282                         h->handler      = NULL;
283                         h->have_handler = 0;
284                 }
285                 h->signum = 0;
286         }
287
288         release_mutex (&signals_mutex);
289
290         return r;
291 }
292
293 static int
294 setup_pipes (signal_info** signals, int count, struct pollfd *fd_structs, int *currfd)
295 {
296         int i;
297         int r = 0;
298         for (i = 0; i < count; ++i) {
299                 signal_info* h;
300                 int filedes[2];
301
302                 h = signals [i];
303
304                 if (mph_int_get (&h->pipecnt) == 0) {
305                         if ((r = pipe (filedes)) != 0) {
306                                 break;
307                         }
308                         h->read_fd  = filedes [0];
309                         h->write_fd = filedes [1];
310                 }
311                 mph_int_inc (&h->pipecnt);
312                 fd_structs[*currfd].fd = h->read_fd;
313                 fd_structs[*currfd].events = POLLIN;
314                 ++(*currfd);
315         }
316         return r;
317 }
318
319 static void
320 teardown_pipes (signal_info** signals, int count)
321 {
322         int i;
323         for (i = 0; i < count; ++i) {
324                 signal_info* h = signals [i];
325
326                 if (mph_int_dec_test (&h->pipecnt)) {
327                         if (h->read_fd != 0)
328                                 close (h->read_fd);
329                         if (h->write_fd != 0)
330                                 close (h->write_fd);
331                         h->read_fd  = 0;
332                         h->write_fd = 0;
333                 }
334         }
335 }
336
337 static int
338 wait_for_any (signal_info** signals, int count, int *currfd, struct pollfd* fd_structs, int timeout, Mono_Posix_RuntimeIsShuttingDown shutting_down)
339 {
340         int r, idx;
341         do {
342                 struct timeval tv;
343                 struct timeval *ptv = NULL;
344                 if (timeout != -1) {
345                         tv.tv_sec  = timeout / 1000;
346                         tv.tv_usec = (timeout % 1000)*1000;
347                         ptv = &tv;
348                 }
349                 r = poll (fd_structs, count, timeout);
350         } while (keep_trying (r) && !shutting_down ());
351
352         idx = -1;
353         if (r == 0)
354                 idx = timeout;
355         else if (r > 0) {
356                 int i;
357                 for (i = 0; i < count; ++i) {
358                         signal_info* h = signals [i];
359                         if (fd_structs[i].revents & POLLIN) {
360                                 int r;
361                                 char c;
362                                 do {
363                                         r = read (h->read_fd, &c, 1);
364                                 } while (keep_trying (r) && !shutting_down ());
365                                 if (idx == -1)
366                                         idx = i;
367                         }
368                 }
369         }
370
371         return idx;
372 }
373
374 /*
375  * returns: -1 on error:
376  *          timeout on timeout
377  *          index into _signals array of signal that was generated on success
378  */
379 int
380 Mono_Unix_UnixSignal_WaitAny (void** _signals, int count, int timeout /* milliseconds */, Mono_Posix_RuntimeIsShuttingDown shutting_down)
381 {
382         int r;
383         int currfd = 0;
384         struct pollfd fd_structs[NUM_SIGNALS];
385
386         signal_info** signals = (signal_info**) _signals;
387
388         if (count > NUM_SIGNALS)
389                 return -1;
390
391         if (acquire_mutex (&signals_mutex) == -1)
392                 return -1;
393
394         r = setup_pipes (signals, count, &fd_structs[0], &currfd);
395
396         release_mutex (&signals_mutex);
397
398         if (r == 0) {
399                 r = wait_for_any (signals, count, &currfd, &fd_structs[0], timeout, shutting_down);
400         }
401
402         if (acquire_mutex (&signals_mutex) == -1)
403                 return -1;
404
405         teardown_pipes (signals, count);
406
407         release_mutex (&signals_mutex);
408
409         return r;
410 }
411
412 #endif /* ndef HOST_WIN32 */
413
414
415 G_END_DECLS
416
417 /*
418  * vim: noexpandtab
419  */