1 /*
2 GSK - a library to write servers
3 Copyright (C) 1999-2000 Dave Benson
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19 Contact:
20 daveb@ffem.org <Dave Benson>
21 */
22
23 #include "gskmainloopkqueue.h"
24 #include "config.h"
25 static GObjectClass *parent_class = NULL;
26
27 #if HAVE_KQUEUE
28 #include <signal.h>
29 #include <errno.h>
30
31 /* Include headers for kqueue(), kevent() and struct kevent. */
32 #include <sys/types.h>
33 #include <sys/event.h>
34 #include <sys/time.h>
35 #endif
36
37 #include <unistd.h> /* needed for close() */
38 #include <string.h>
39
40 /* --- GskMainLoop methods --- */
41
42 static gboolean
gsk_main_loop_kqueue_setup(GskMainLoop * main_loop)43 gsk_main_loop_kqueue_setup (GskMainLoop *main_loop)
44 {
45 GskMainLoopKqueue *main_loop_kqueue = GSK_MAIN_LOOP_KQUEUE (main_loop);
46 #if HAVE_KQUEUE
47 main_loop_kqueue->kernel_queue_id = kqueue ();
48 return (main_loop_kqueue->kernel_queue_id >= 0);
49 #else
50 (void) main_loop_kqueue;
51 return FALSE;
52 #endif
53 }
54
55 #if HAVE_KQUEUE
56 static void
kqueue_flush_pending_changes(GskMainLoopKqueue * main_loop_kqueue)57 kqueue_flush_pending_changes (GskMainLoopKqueue *main_loop_kqueue)
58 {
59 if (main_loop_kqueue->num_updates > 0)
60 {
61 struct timespec timeout = { 0, 0 };
62 kevent (main_loop_kqueue->kernel_queue_id,
63 (const struct kevent *) main_loop_kqueue->kevent_array,
64 main_loop_kqueue->num_updates,
65 NULL, 0,
66 &timeout);
67 main_loop_kqueue->num_updates = 0;
68 }
69 }
70
71 static inline void
change_signal(int signo,gboolean ignore)72 change_signal (int signo, gboolean ignore)
73 {
74 struct sigaction action;
75 memset (&action, 0, sizeof (action));
76 action.sa_handler = ignore ? SIG_IGN : SIG_DFL;
77 sigaction (signo, &action, NULL);
78 }
79
do_nothing(int signo)80 static void do_nothing (int signo)
81 {
82 }
83
84 static inline void
almost_ignore_sigchld(void)85 almost_ignore_sigchld (void)
86 {
87 struct sigaction action;
88 memset (&action, 0, sizeof (action));
89 action.sa_handler = do_nothing;
90 sigaction (SIGCHLD, &action, NULL);
91 }
92
93 static inline struct kevent *
get_next_update(GskMainLoopKqueue * main_loop_kqueue)94 get_next_update ( GskMainLoopKqueue *main_loop_kqueue)
95 {
96 if (main_loop_kqueue->num_updates == main_loop_kqueue->max_updates)
97 {
98 if (main_loop_kqueue->max_updates == 0)
99 main_loop_kqueue->max_updates = 128;
100 else
101 main_loop_kqueue->max_updates *= 2;
102 main_loop_kqueue->kevent_array
103 = g_realloc (main_loop_kqueue->kevent_array,
104 sizeof (struct kevent) * main_loop_kqueue->max_updates);
105 }
106 return ((struct kevent *) (main_loop_kqueue->kevent_array))
107 + main_loop_kqueue->num_updates;
108 }
109
110
111 static void
gsk_main_loop_kqueue_change(GskMainLoop * main_loop,GskMainLoopChange * change)112 gsk_main_loop_kqueue_change (GskMainLoop *main_loop,
113 GskMainLoopChange *change)
114 {
115 GskMainLoopKqueue *main_loop_kqueue = GSK_MAIN_LOOP_KQUEUE (main_loop);
116 if (main_loop_kqueue->num_updates + 1 >= main_loop_kqueue->max_updates)
117 {
118 if (main_loop_kqueue->max_updates == 0)
119 main_loop_kqueue->max_updates = 128;
120 else
121 main_loop_kqueue->max_updates *= 2;
122 main_loop_kqueue->kevent_array
123 = g_realloc (main_loop_kqueue->kevent_array,
124 sizeof (struct kevent) * main_loop_kqueue->max_updates);
125 }
126 switch (change->type)
127 {
128 static gboolean has_ignored_sigchld = FALSE;
129 struct kevent *event;
130 case GSK_MAIN_LOOP_EVENT_PROCESS:
131 if (change->data.process.did_exit)
132 return;
133 event = get_next_update (main_loop_kqueue);
134 event->ident = change->data.process.pid;
135 event->filter = EVFILT_PROC;
136 event->flags = change->data.process.add ? (EV_ADD|EV_ONESHOT|EV_CLEAR) : EV_DELETE;
137 event->fflags = NOTE_EXIT;
138 event->data = 0;
139 event->udata = NULL;
140 main_loop_kqueue->num_updates++;
141 if (!has_ignored_sigchld)
142 {
143 almost_ignore_sigchld ();
144 has_ignored_sigchld = TRUE;
145 }
146 break;
147 case GSK_MAIN_LOOP_EVENT_SIGNAL:
148 event = get_next_update (main_loop_kqueue);
149 event->ident = change->data.signal.number;
150 event->filter = EVFILT_SIGNAL;
151 event->flags = change->data.signal.add ? EV_ADD : EV_DELETE;
152 event->fflags = 0;
153 event->data = 0;
154 event->udata = NULL;
155 change_signal (change->data.signal.number, change->data.signal.add);
156 main_loop_kqueue->num_updates++;
157 kqueue_flush_pending_changes (main_loop_kqueue);
158 break;
159 case GSK_MAIN_LOOP_EVENT_IO:
160 if ((change->data.io.old_events ^ change->data.io.events) & G_IO_IN)
161 {
162 event = get_next_update (main_loop_kqueue);
163 event->ident = change->data.io.fd;
164 event->filter = EVFILT_READ;
165 event->flags = (change->data.io.events & G_IO_IN) ? EV_ADD : EV_DELETE;
166 event->fflags = 0;
167 event->data = 0;
168 event->udata = NULL;
169 main_loop_kqueue->num_updates++;
170 }
171 if ((change->data.io.old_events ^ change->data.io.events) & G_IO_OUT)
172 {
173 event = get_next_update (main_loop_kqueue);
174 event->ident = change->data.io.fd;
175 event->filter = EVFILT_WRITE;
176 event->flags = (change->data.io.events & G_IO_OUT) ? EV_ADD : EV_DELETE;
177 event->fflags = 0;
178 event->data = 0;
179 event->udata = NULL;
180 main_loop_kqueue->num_updates++;
181 }
182 kqueue_flush_pending_changes (main_loop_kqueue);
183 break;
184 }
185 }
186
187 static guint
gsk_main_loop_kqueue_poll(GskMainLoop * main_loop,guint max_events_out,GskMainLoopEvent * events,gint timeout_milli)188 gsk_main_loop_kqueue_poll (GskMainLoop *main_loop,
189 guint max_events_out,
190 GskMainLoopEvent *events,
191 gint timeout_milli)
192 {
193 struct kevent *out = alloca (sizeof (struct kevent) * max_events_out);
194 GskMainLoopKqueue *main_loop_kqueue = GSK_MAIN_LOOP_KQUEUE (main_loop);
195 gint kevent_rv;
196 guint i;
197 guint rv = 0;
198 struct timespec timeout;
199 struct timespec *p_timeout;
200 if (timeout_milli < 0)
201 {
202 p_timeout = NULL;
203 }
204 else
205 {
206 timeout.tv_sec = timeout_milli / 1000;
207 timeout.tv_nsec = timeout_milli % 1000 * 1000 * 1000;
208 p_timeout = &timeout;
209 }
210 retry_kevent:
211 kevent_rv = kevent (main_loop_kqueue->kernel_queue_id,
212 main_loop_kqueue->kevent_array,
213 main_loop_kqueue->num_updates,
214 out,
215 max_events_out,
216 p_timeout);
217
218 if (kevent_rv < 0)
219 {
220 if (gsk_errno_is_ignorable (errno))
221 goto retry_kevent;
222 g_warning ("error running kevent: %s", g_strerror (errno));
223 return 0;
224 }
225
226 main_loop_kqueue->num_updates = 0;
227
228 for (i = 0; i < kevent_rv; i++)
229 {
230 switch (out[i].filter)
231 {
232 case EVFILT_READ:
233 events[rv].type = GSK_MAIN_LOOP_EVENT_IO;
234 events[rv].data.io.events = G_IO_IN;
235 events[rv].data.io.fd = out[i].ident;
236 rv++;
237 break;
238
239 case EVFILT_WRITE:
240 events[rv].type = GSK_MAIN_LOOP_EVENT_IO;
241 events[rv].data.io.events = G_IO_OUT;
242 events[rv].data.io.fd = out[i].ident;
243 rv++;
244 break;
245
246 case EVFILT_SIGNAL:
247 events[rv].type = GSK_MAIN_LOOP_EVENT_SIGNAL;
248 events[rv].data.signal = out[i].ident;
249 rv++;
250 break;
251
252 case EVFILT_PROC:
253 if (out[i].fflags == NOTE_EXIT)
254 {
255 events[rv].type = GSK_MAIN_LOOP_EVENT_PROCESS;
256 events[rv].data.process_wait_info.pid = out[i].ident;
257 if (gsk_main_loop_do_waitpid (out[i].ident,
258 &events[rv].data.process_wait_info))
259 rv++;
260 }
261 break;
262
263 default:
264 g_warning ("unexpected type of event from kevent (%d)",
265 out[i].filter);
266 }
267 }
268 return rv;
269 }
270 #endif
271
272 static void
gsk_main_loop_kqueue_finalize(GObject * object)273 gsk_main_loop_kqueue_finalize(GObject *object)
274 {
275 GskMainLoopKqueue *kqueue = GSK_MAIN_LOOP_KQUEUE (object);
276 gsk_main_loop_destroy_all_sources (GSK_MAIN_LOOP (object));
277 if (kqueue->kernel_queue_id >= 0)
278 close (kqueue->kernel_queue_id);
279 (*parent_class->finalize) (object);
280 }
281
282 /* --- class methods --- */
283 static void
gsk_main_loop_kqueue_init(GskMainLoopKqueue * kqueue)284 gsk_main_loop_kqueue_init (GskMainLoopKqueue *kqueue)
285 {
286 kqueue->kernel_queue_id = -1;
287 }
288
289 static void
gsk_main_loop_kqueue_class_init(GskMainLoopClass * main_loop_class)290 gsk_main_loop_kqueue_class_init (GskMainLoopClass *main_loop_class)
291 {
292 GObjectClass *object_class = G_OBJECT_CLASS (main_loop_class);
293 parent_class = g_type_class_peek_parent (main_loop_class);
294 main_loop_class->setup = gsk_main_loop_kqueue_setup;
295 #if HAVE_KQUEUE
296 main_loop_class->poll = gsk_main_loop_kqueue_poll;
297 main_loop_class->change = gsk_main_loop_kqueue_change;
298 #endif
299 object_class->finalize = gsk_main_loop_kqueue_finalize;
300 }
301
302 GType
gsk_main_loop_kqueue_get_type()303 gsk_main_loop_kqueue_get_type()
304 {
305 static GType main_loop_kqueue_type = 0;
306 if (!main_loop_kqueue_type)
307 {
308 static const GTypeInfo main_loop_kqueue_info =
309 {
310 sizeof(GskMainLoopKqueueClass),
311 (GBaseInitFunc) NULL,
312 (GBaseFinalizeFunc) NULL,
313 (GClassInitFunc) gsk_main_loop_kqueue_class_init,
314 NULL, /* class_finalize */
315 NULL, /* class_data */
316 sizeof (GskMainLoopKqueue),
317 0, /* n_preallocs */
318 (GInstanceInitFunc) gsk_main_loop_kqueue_init,
319 NULL /* value_table */
320 };
321 GType parent = GSK_TYPE_MAIN_LOOP;
322 main_loop_kqueue_type = g_type_register_static (parent,
323 "GskMainLoopKqueue",
324 &main_loop_kqueue_info, 0);
325 }
326 return main_loop_kqueue_type;
327 }
328