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