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