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