* Makefile.am: On non-Windows platforms, MonoPosixHelper now links
[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 int
124 Mono_Posix_Syscall_psignal (int sig, const char* s)
125 {
126         errno = 0;
127         psignal (sig, s);
128         return errno == 0 ? 0 : -1;
129 }
130
131 #define NUM_SIGNALS 64
132 static signal_info signals[NUM_SIGNALS];
133
134 static int acquire_mutex (pthread_mutex_t *mutex)
135 {
136         int mr;
137         while ((mr = pthread_mutex_lock (mutex)) == EAGAIN) {
138                 /* try to acquire again */
139         }
140         if ((mr != 0) && (mr != EDEADLK))  {
141                 errno = mr;
142                 return -1;
143         }
144         return 0;
145 }
146
147 static void release_mutex (pthread_mutex_t *mutex)
148 {
149         int mr;
150         while ((mr = pthread_mutex_unlock (mutex)) == EAGAIN) {
151                 /* try to release mutex again */
152         }
153 }
154
155 static inline int
156 keep_trying (int r)
157 {
158         return r == -1 && errno == EINTR && !mono_runtime_is_shutting_down();
159 }
160
161 static void
162 default_handler (int signum)
163 {
164         int i;
165         for (i = 0; i < NUM_SIGNALS; ++i) {
166                 int fd;
167                 signal_info* h = &signals [i];
168                 if (mph_int_get (&h->signum) != signum)
169                         continue;
170                 mph_int_inc (&h->count);
171                 fd = mph_int_get (&h->write_fd);
172                 if (fd > 0) {
173                         int j,pipecounter;
174                         char c = signum;
175                         pipecounter = mph_int_get (&h->pipecnt);
176                         for (j = 0; j < pipecounter; ++j) {
177                                 int r;
178                                 do { r = write (fd, &c, 1); } while (keep_trying (r));
179                                 fsync (fd); /* force */
180                         }
181                 }
182         }
183 }
184
185 static pthread_mutex_t signals_mutex = PTHREAD_MUTEX_INITIALIZER;
186
187 void*
188 Mono_Unix_UnixSignal_install (int sig)
189 {
190         int i;
191         signal_info* h = NULL; 
192         int have_handler = 0;
193         void* handler = NULL;
194
195         if (acquire_mutex (&signals_mutex) == -1)
196                 return NULL;
197
198 #if defined (SIGRTMIN) && defined (SIGRTMAX)
199         /*The runtime uses some rt signals for itself so it's important to not override them.*/
200         if (sig >= SIGRTMIN && sig <= SIGRTMAX && count_handlers (sig) == 0) {
201                 struct sigaction sinfo;
202                 sigaction (sig, NULL, &sinfo);
203                 if (sinfo.sa_handler != SIG_DFL || (void*)sinfo.sa_sigaction != (void*)SIG_DFL) {
204                         pthread_mutex_unlock (&signals_mutex);
205                         errno = EADDRINUSE;
206                         return NULL;
207                 }
208         }
209 #endif /*defined (SIGRTMIN) && defined (SIGRTMAX)*/
210
211         for (i = 0; i < NUM_SIGNALS; ++i) {
212                 if (h == NULL && signals [i].signum == 0) {
213                         h = &signals [i];
214                         h->handler = signal (sig, default_handler);
215                         if (h->handler == SIG_ERR) {
216                                 h->handler = NULL;
217                                 h = NULL;
218                                 break;
219                         }
220                         else {
221                                 h->have_handler = 1;
222                         }
223                 }
224                 if (!have_handler && signals [i].signum == sig &&
225                                 signals [i].handler != default_handler) {
226                         have_handler = 1;
227                         handler = signals [i].handler;
228                 }
229                 if (h && have_handler)
230                         break;
231         }
232
233         if (h && have_handler) {
234                 h->have_handler = 1;
235                 h->handler      = handler;
236         }
237
238         if (h) {
239                 mph_int_set (&h->count, h->count, 0);
240                 mph_int_set (&h->signum, h->signum, sig);
241                 mph_int_set (&h->pipecnt, h->pipecnt, 0);
242         }
243
244         release_mutex (&signals_mutex);
245
246         return h;
247 }
248
249 static int
250 count_handlers (int signum)
251 {
252         int i;
253         int count = 0;
254         for (i = 0; i < NUM_SIGNALS; ++i) {
255                 if (signals [i].signum == signum)
256                         ++count;
257         }
258         return count;
259 }
260
261 int
262 Mono_Unix_UnixSignal_uninstall (void* info)
263 {
264         signal_info* h;
265         int r = -1;
266
267         if (acquire_mutex (&signals_mutex) == -1)
268                 return -1;
269
270         h = info;
271
272         if (h == NULL || h < signals || h > &signals [NUM_SIGNALS])
273                 errno = EINVAL;
274         else {
275                 /* last UnixSignal -- we can unregister */
276                 if (h->have_handler && count_handlers (h->signum) == 1) {
277                         mph_sighandler_t p = signal (h->signum, h->handler);
278                         if (p != SIG_ERR)
279                                 r = 0;
280                         h->handler      = NULL;
281                         h->have_handler = 0;
282                 }
283                 h->signum = 0;
284         }
285
286         release_mutex (&signals_mutex);
287
288         return r;
289 }
290
291 static int
292 setup_pipes (signal_info** signals, int count, struct pollfd *fd_structs, int *currfd)
293 {
294         int i;
295         int r = 0;
296         for (i = 0; i < count; ++i) {
297                 signal_info* h;
298                 int filedes[2];
299
300                 h = signals [i];
301
302                 if (mph_int_get (&h->pipecnt) == 0) {
303                         if ((r = pipe (filedes)) != 0) {
304                                 break;
305                         }
306                         h->read_fd  = filedes [0];
307                         h->write_fd = filedes [1];
308                 }
309                 mph_int_inc (&h->pipecnt);
310                 fd_structs[*currfd].fd = h->read_fd;
311                 fd_structs[*currfd].events = POLLIN;
312                 ++(*currfd);
313         }
314         return r;
315 }
316
317 static void
318 teardown_pipes (signal_info** signals, int count)
319 {
320         int i;
321         for (i = 0; i < count; ++i) {
322                 signal_info* h = signals [i];
323
324                 if (mph_int_dec_test (&h->pipecnt)) {
325                         if (h->read_fd != 0)
326                                 close (h->read_fd);
327                         if (h->write_fd != 0)
328                                 close (h->write_fd);
329                         h->read_fd  = 0;
330                         h->write_fd = 0;
331                 }
332         }
333 }
334
335 static int
336 wait_for_any (signal_info** signals, int count, int *currfd, struct pollfd* fd_structs, int timeout)
337 {
338         int r, idx;
339         do {
340                 struct timeval tv;
341                 struct timeval *ptv = NULL;
342                 if (timeout != -1) {
343                         tv.tv_sec  = timeout / 1000;
344                         tv.tv_usec = (timeout % 1000)*1000;
345                         ptv = &tv;
346                 }
347                 r = poll (fd_structs, count, timeout);
348         } while (keep_trying (r));
349
350         idx = -1;
351         if (r == 0)
352                 idx = timeout;
353         else if (r > 0) {
354                 int i;
355                 for (i = 0; i < count; ++i) {
356                         signal_info* h = signals [i];
357                         if (fd_structs[i].revents & POLLIN) {
358                                 int r;
359                                 char c;
360                                 do {
361                                         r = read (h->read_fd, &c, 1);
362                                 } while (keep_trying (r));
363                                 if (idx == -1)
364                                         idx = i;
365                         }
366                 }
367         }
368
369         return idx;
370 }
371
372 /*
373  * returns: -1 on error:
374  *          timeout on timeout
375  *          index into _signals array of signal that was generated on success
376  */
377 int
378 Mono_Unix_UnixSignal_WaitAny (void** _signals, int count, int timeout /* milliseconds */)
379 {
380         int r;
381         int currfd = 0;
382         struct pollfd fd_structs[NUM_SIGNALS];
383
384         signal_info** signals = (signal_info**) _signals;
385
386         if (count > NUM_SIGNALS)
387                 return -1;
388
389         if (acquire_mutex (&signals_mutex) == -1)
390                 return -1;
391
392         r = setup_pipes (signals, count, &fd_structs[0], &currfd);
393
394         release_mutex (&signals_mutex);
395
396         if (r == 0) {
397                 r = wait_for_any (signals, count, &currfd, &fd_structs[0], timeout);
398         }
399
400         if (acquire_mutex (&signals_mutex) == -1)
401                 return -1;
402
403         teardown_pipes (signals, count);
404
405         release_mutex (&signals_mutex);
406
407         return r;
408 }
409
410 #endif /* ndef HOST_WIN32 */
411
412
413 G_END_DECLS
414
415 /*
416  * vim: noexpandtab
417  */