* signal.c: Fix the Win32 build.
[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                 signal_info* h = &signals [i];
75                 if (h->signum != signum)
76                         continue;
77                 ++h->count;
78                 if (h->write_fd > 0) {
79                         char c = signum;
80                         write (h->write_fd, &c, 1);
81                 }
82         }
83 }
84
85 static pthread_mutex_t signals_mutex = PTHREAD_MUTEX_INITIALIZER;
86
87 void*
88 Mono_Unix_UnixSignal_install (int sig)
89 {
90         int i, mr;
91         signal_info* h = NULL; 
92         int have_handler = 0;
93         void* handler;
94
95         mr = pthread_mutex_lock (&signals_mutex);
96         if (mr != 0) {
97                 errno = mr;
98                 return NULL;
99         }
100
101         for (i = 0; i < NUM_SIGNALS; ++i) {
102                 if (h == NULL && signals [i].signum == 0) {
103                         h = &signals [i];
104                         h->handler = signal (sig, default_handler);
105                         if (h->handler == SIG_ERR) {
106                                 h->handler = NULL;
107                                 h = NULL;
108                                 break;
109                         }
110                         else {
111                                 h->signum       = sig;
112                                 h->count        = 0;
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         pthread_mutex_unlock (&signals_mutex);
131
132         return h;
133 }
134
135 static int
136 count_handlers (int signum)
137 {
138         int i;
139         int count = 0;
140         for (i = 0; i < NUM_SIGNALS; ++i) {
141                 if (signals [i].signum == signum)
142                         ++count;
143         }
144         return count;
145 }
146
147 int
148 Mono_Unix_UnixSignal_uninstall (void* info)
149 {
150         signal_info* h;
151         int mr, r = -1;
152
153         mr = pthread_mutex_lock (&signals_mutex);
154         if (mr != 0) {
155                 errno = mr;
156                 return -1;
157         }
158
159         h = info;
160
161         if (h == NULL || h < signals || h > &signals [NUM_SIGNALS])
162                 errno = EINVAL;
163         else {
164                 /* last UnixSignal -- we can unregister */
165                 if (h->have_handler && count_handlers (h->signum) == 1) {
166                         mph_sighandler_t p = signal (h->signum, h->handler);
167                         if (p != SIG_ERR)
168                                 r = 0;
169                         h->handler      = NULL;
170                         h->have_handler = 0;
171                 }
172                 h->signum = 0;
173         }
174
175         pthread_mutex_unlock (&signals_mutex);
176
177         return r;
178 }
179
180 static int
181 setup_pipes (signal_info** signals, int count, fd_set *read_fds, int *max_fd)
182 {
183         int i, r;
184         for (i = 0; i < count; ++i) {
185                 signal_info* h;
186                 int filedes[2];
187
188                 if ((r = pipe (filedes)) != 0) {
189                         break;
190                 }
191                 h = signals [i];
192                 h->read_fd  = filedes [0];
193                 h->write_fd = filedes [1];
194                 if (h->read_fd > *max_fd)
195                         *max_fd = h->read_fd;
196                 FD_SET (h->read_fd, read_fds);
197         }
198         return r;
199 }
200
201 static void
202 teardown_pipes (signal_info** signals, int count)
203 {
204         int i;
205         for (i = 0; i < count; ++i) {
206                 signal_info* h = signals [i];
207                 if (h->read_fd != 0)
208                         close (h->read_fd);
209                 if (h->write_fd != 0)
210                         close (h->write_fd);
211                 h->read_fd  = 0;
212                 h->write_fd = 0;
213         }
214 }
215
216 static int
217 wait_for_any (signal_info** signals, int count, int max_fd, fd_set* read_fds, int timeout)
218 {
219         int r;
220         do {
221                 struct timeval tv;
222                 struct timeval *ptv = NULL;
223                 if (timeout != -1) {
224                         tv.tv_sec  = timeout / 1000;
225                         tv.tv_usec = (timeout % 1000)*1000;
226                         ptv = &tv;
227                 }
228
229                 r = select (max_fd + 1, read_fds, NULL, NULL, ptv);
230         } while (r == -1 && errno == EINTR);
231
232         if (r > 0) {
233                 int i;
234                 for (i = 0; i < count; ++i) {
235                         signal_info* h = signals [i];
236                         if (FD_ISSET (h->read_fd, read_fds)) {
237                                 char c;
238                                 read (h->read_fd, &c, 1); /* ignore error */
239                         }
240                 }
241         }
242
243         return r;
244 }
245
246 /*
247  * returns: -1 on error:
248  *           0 on timeout
249  *         > 0 on success
250  */
251 int
252 Mono_Unix_UnixSignal_WaitAny (void** _signals, int count, int timeout /* milliseconds */)
253 {
254         fd_set read_fds;
255         int mr, r;
256         int max_fd = 0;
257
258         signal_info** signals = (signal_info**) _signals;
259
260         mr = pthread_mutex_lock (&signals_mutex);
261         if (mr != 0) {
262                 errno = mr;
263                 return -1;
264         }
265
266         FD_ZERO (&read_fds);
267
268         r = setup_pipes (signals, count, &read_fds, &max_fd);
269         if (r == 0) {
270                 r = wait_for_any (signals, count, max_fd, &read_fds, timeout);
271         }
272         teardown_pipes (signals, count);
273
274         pthread_mutex_unlock (&signals_mutex);
275
276         return r;
277 }
278
279 #endif /* ndef PLATFORM_WIN32 */
280
281
282 G_END_DECLS
283
284 /*
285  * vim: noexpandtab
286  */