d995fa970d318612b800ca76430ee0aa767f8b8b
[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 #endif
25
26 G_BEGIN_DECLS
27
28 typedef void (*mph_sighandler_t)(int);
29 typedef struct Mono_Unix_UnixSignal_SignalInfo signal_info;
30
31 void*
32 Mono_Posix_Stdlib_SIG_DFL (void)
33 {
34         return SIG_DFL;
35 }
36
37 void*
38 Mono_Posix_Stdlib_SIG_ERR (void)
39 {
40         return SIG_ERR;
41 }
42
43 void*
44 Mono_Posix_Stdlib_SIG_IGN (void)
45 {
46         return SIG_IGN;
47 }
48
49 void
50 Mono_Posix_Stdlib_InvokeSignalHandler (int signum, void *handler)
51 {
52         mph_sighandler_t _h = (mph_sighandler_t) handler;
53         _h (signum);
54 }
55
56 #ifndef PLATFORM_WIN32
57
58 int
59 Mono_Posix_Syscall_psignal (int sig, const char* s)
60 {
61         errno = 0;
62         psignal (sig, s);
63         return errno == 0 ? 0 : -1;
64 }
65
66 #define NUM_SIGNALS 64
67 static signal_info signals[NUM_SIGNALS];
68
69 static void
70 default_handler (int signum)
71 {
72         int i;
73         for (i = 0; i < NUM_SIGNALS; ++i) {
74                 int fd;
75                 signal_info* h = &signals [i];
76                 if (g_atomic_int_get (&h->signum) != signum)
77                         continue;
78                 g_atomic_int_inc (&h->count);
79                 fd = g_atomic_int_get (&h->write_fd);
80                 if (fd > 0) {
81                         char c = signum;
82                         write (fd, &c, 1);
83                 }
84         }
85 }
86
87 static pthread_mutex_t signals_mutex = PTHREAD_MUTEX_INITIALIZER;
88
89 void*
90 Mono_Unix_UnixSignal_install (int sig)
91 {
92         int i, mr;
93         signal_info* h = NULL; 
94         int have_handler = 0;
95         void* handler;
96
97         mr = pthread_mutex_lock (&signals_mutex);
98         if (mr != 0) {
99                 errno = mr;
100                 return NULL;
101         }
102
103         for (i = 0; i < NUM_SIGNALS; ++i) {
104                 if (h == NULL && signals [i].signum == 0) {
105                         h = &signals [i];
106                         h->handler = signal (sig, default_handler);
107                         if (h->handler == SIG_ERR) {
108                                 h->handler = NULL;
109                                 h = NULL;
110                                 break;
111                         }
112                         else {
113                                 h->have_handler = 1;
114                         }
115                 }
116                 if (!have_handler && signals [i].signum == sig &&
117                                 signals [i].handler != default_handler) {
118                         have_handler = 1;
119                         handler = signals [i].handler;
120                 }
121                 if (h && have_handler)
122                         break;
123         }
124
125         if (h && have_handler) {
126                 h->have_handler = 1;
127                 h->handler      = handler;
128         }
129
130         if (h) {
131                 g_atomic_int_set (&h->count, 0);
132                 g_atomic_int_set (&h->signum, sig);
133         }
134
135         pthread_mutex_unlock (&signals_mutex);
136
137         return h;
138 }
139
140 static int
141 count_handlers (int signum)
142 {
143         int i;
144         int count = 0;
145         for (i = 0; i < NUM_SIGNALS; ++i) {
146                 if (signals [i].signum == signum)
147                         ++count;
148         }
149         return count;
150 }
151
152 int
153 Mono_Unix_UnixSignal_uninstall (void* info)
154 {
155         signal_info* h;
156         int mr, r = -1;
157
158         mr = pthread_mutex_lock (&signals_mutex);
159         if (mr != 0) {
160                 errno = mr;
161                 return -1;
162         }
163
164         h = info;
165
166         if (h == NULL || h < signals || h > &signals [NUM_SIGNALS])
167                 errno = EINVAL;
168         else {
169                 /* last UnixSignal -- we can unregister */
170                 if (h->have_handler && count_handlers (h->signum) == 1) {
171                         mph_sighandler_t p = signal (h->signum, h->handler);
172                         if (p != SIG_ERR)
173                                 r = 0;
174                         h->handler      = NULL;
175                         h->have_handler = 0;
176                 }
177                 h->signum = 0;
178         }
179
180         pthread_mutex_unlock (&signals_mutex);
181
182         return r;
183 }
184
185 static int
186 setup_pipes (signal_info** signals, int count, fd_set *read_fds, int *max_fd)
187 {
188         int i, r;
189         for (i = 0; i < count; ++i) {
190                 signal_info* h;
191                 int filedes[2];
192
193                 if ((r = pipe (filedes)) != 0) {
194                         break;
195                 }
196                 h = signals [i];
197                 h->read_fd  = filedes [0];
198                 h->write_fd = filedes [1];
199                 if (h->read_fd > *max_fd)
200                         *max_fd = h->read_fd;
201                 FD_SET (h->read_fd, read_fds);
202         }
203         return r;
204 }
205
206 static void
207 teardown_pipes (signal_info** signals, int count)
208 {
209         int i;
210         for (i = 0; i < count; ++i) {
211                 signal_info* h = signals [i];
212                 if (h->read_fd != 0)
213                         close (h->read_fd);
214                 if (h->write_fd != 0)
215                         close (h->write_fd);
216                 h->read_fd  = 0;
217                 h->write_fd = 0;
218         }
219 }
220
221 static int
222 wait_for_any (signal_info** signals, int count, int max_fd, fd_set* read_fds, int timeout)
223 {
224         int r;
225         do {
226                 struct timeval tv;
227                 struct timeval *ptv = NULL;
228                 if (timeout != -1) {
229                         tv.tv_sec  = timeout / 1000;
230                         tv.tv_usec = (timeout % 1000)*1000;
231                         ptv = &tv;
232                 }
233
234                 r = select (max_fd + 1, read_fds, NULL, NULL, ptv);
235         } while (r == -1 && errno == EINTR);
236
237         if (r > 0) {
238                 int i;
239                 for (i = 0; i < count; ++i) {
240                         signal_info* h = signals [i];
241                         if (FD_ISSET (h->read_fd, read_fds)) {
242                                 char c;
243                                 read (h->read_fd, &c, 1); /* ignore error */
244                         }
245                 }
246         }
247
248         return r;
249 }
250
251 /*
252  * returns: -1 on error:
253  *           0 on timeout
254  *         > 0 on success
255  */
256 int
257 Mono_Unix_UnixSignal_WaitAny (void** _signals, int count, int timeout /* milliseconds */)
258 {
259         fd_set read_fds;
260         int mr, r;
261         int max_fd = 0;
262
263         signal_info** signals = (signal_info**) _signals;
264
265         mr = pthread_mutex_lock (&signals_mutex);
266         if (mr != 0) {
267                 errno = mr;
268                 return -1;
269         }
270
271         FD_ZERO (&read_fds);
272
273         r = setup_pipes (signals, count, &read_fds, &max_fd);
274         if (r == 0) {
275                 r = wait_for_any (signals, count, max_fd, &read_fds, timeout);
276         }
277         teardown_pipes (signals, count);
278
279         pthread_mutex_unlock (&signals_mutex);
280
281         return r;
282 }
283
284 #endif /* ndef PLATFORM_WIN32 */
285
286
287 G_END_DECLS
288
289 /*
290  * vim: noexpandtab
291  */