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