]> jfr.im git - solanum.git/blob - libratbox/src/kqueue.c
kqueue: also use EV_ONESHOT for read events
[solanum.git] / libratbox / src / kqueue.c
1 /*
2 * ircd-ratbox: A slightly useful ircd.
3 * kqueue.c: FreeBSD kqueue compatible network routines.
4 *
5 * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center
6 * Copyright (C) 1996-2002 Hybrid Development Team
7 * Copyright (C) 2001 Adrian Chadd <adrian@creative.net.au>
8 * Copyright (C) 2002-2005 ircd-ratbox development team
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
23 * USA
24 *
25 * $Id: kqueue.c 25038 2008-01-23 16:03:08Z androsyn $
26 */
27
28 #include <libratbox_config.h>
29 #include <ratbox_lib.h>
30 #include <commio-int.h>
31 #include <event-int.h>
32
33 #if defined(HAVE_SYS_EVENT_H) && (HAVE_KEVENT)
34
35 #include <sys/event.h>
36
37 #define KE_LENGTH 128
38
39 /* jlemon goofed up and didn't add EV_SET until fbsd 4.3 */
40
41 #ifndef EV_SET
42 #define EV_SET(kevp, a, b, c, d, e, f) do { \
43 (kevp)->ident = (a); \
44 (kevp)->filter = (b); \
45 (kevp)->flags = (c); \
46 (kevp)->fflags = (d); \
47 (kevp)->data = (e); \
48 (kevp)->udata = (f); \
49 } while(0)
50 #endif
51
52 #ifdef EVFILT_TIMER
53 #define KQUEUE_SCHED_EVENT
54 #endif
55
56
57 static void kq_update_events(rb_fde_t *, short, PF *);
58 static int kq;
59 static struct timespec zero_timespec;
60
61 static struct kevent *kqlst; /* kevent buffer */
62 static int kqmax; /* max structs to buffer */
63 static int kqoff; /* offset into the buffer */
64
65
66 int
67 rb_setup_fd_kqueue(rb_fde_t * F)
68 {
69 return 0;
70 }
71
72 static void
73 kq_update_events(rb_fde_t * F, short filter, PF * handler)
74 {
75 PF *cur_handler;
76 int kep_flags;
77
78 switch (filter)
79 {
80 case EVFILT_READ:
81 cur_handler = F->read_handler;
82 break;
83 case EVFILT_WRITE:
84 cur_handler = F->write_handler;
85 break;
86 default:
87 /* XXX bad! -- adrian */
88 return;
89 break;
90 }
91
92 if((cur_handler == NULL && handler != NULL) || (cur_handler != NULL && handler == NULL))
93 {
94 struct kevent *kep;
95
96 kep = kqlst + kqoff;
97
98 if(handler != NULL)
99 {
100 kep_flags = EV_ADD | EV_ONESHOT;
101 }
102 else
103 {
104 kep_flags = EV_DELETE;
105 }
106
107 EV_SET(kep, (uintptr_t) F->fd, filter, kep_flags, 0, 0, (void *) F);
108
109 if(++kqoff == kqmax)
110 {
111 int ret;
112
113 ret = kevent(kq, kqlst, kqoff, NULL, 0, &zero_timespec);
114 /* jdc -- someone needs to do error checking... */
115 if(ret == -1)
116 {
117 rb_lib_log("kq_update_events(): kevent(): %s", strerror(errno));
118 return;
119 }
120 kqoff = 0;
121 }
122 }
123 }
124
125
126
127 /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
128 /* Public functions */
129
130
131 /*
132 * rb_init_netio
133 *
134 * This is a needed exported function which will be called to initialise
135 * the network loop code.
136 */
137 int
138 rb_init_netio_kqueue(void)
139 {
140 kq = kqueue();
141 if(kq < 0)
142 {
143 return errno;
144 }
145 kqmax = getdtablesize();
146 kqlst = rb_malloc(sizeof(struct kevent) * kqmax);
147 rb_open(kq, RB_FD_UNKNOWN, "kqueue fd");
148 zero_timespec.tv_sec = 0;
149 zero_timespec.tv_nsec = 0;
150
151 return 0;
152 }
153
154 /*
155 * rb_setselect
156 *
157 * This is a needed exported function which will be called to register
158 * and deregister interest in a pending IO state for a given FD.
159 */
160 void
161 rb_setselect_kqueue(rb_fde_t * F, unsigned int type, PF * handler, void *client_data)
162 {
163 lrb_assert(IsFDOpen(F));
164
165 if(type & RB_SELECT_READ)
166 {
167 kq_update_events(F, EVFILT_READ, handler);
168 F->read_handler = handler;
169 F->read_data = client_data;
170 }
171 if(type & RB_SELECT_WRITE)
172 {
173 kq_update_events(F, EVFILT_WRITE, handler);
174 F->write_handler = handler;
175 F->write_data = client_data;
176 }
177 }
178
179 /*
180 * Check all connections for new connections and input data that is to be
181 * processed. Also check for connections with data queued and whether we can
182 * write it out.
183 */
184
185 /*
186 * rb_select
187 *
188 * Called to do the new-style IO, courtesy of squid (like most of this
189 * new IO code). This routine handles the stuff we've hidden in
190 * rb_setselect and fd_table[] and calls callbacks for IO ready
191 * events.
192 */
193
194 int
195 rb_select_kqueue(long delay)
196 {
197 int num, i;
198 static struct kevent ke[KE_LENGTH];
199 struct timespec poll_time;
200 struct timespec *pt;
201 rb_fde_t *F;
202
203
204 if(delay < 0) {
205 pt = NULL;
206 }
207 else {
208 pt = &poll_time;
209 poll_time.tv_sec = delay / 1000;
210 poll_time.tv_nsec = (delay % 1000) * 1000000;
211 }
212
213 for (;;)
214 {
215 num = kevent(kq, kqlst, kqoff, ke, KE_LENGTH, pt);
216 kqoff = 0;
217
218 if(num >= 0)
219 break;
220
221 if(rb_ignore_errno(errno))
222 break;
223
224 rb_set_time();
225
226 return RB_ERROR;
227
228 /* NOTREACHED */
229 }
230
231 rb_set_time();
232
233 if(num == 0)
234 return RB_OK; /* No error.. */
235
236 for (i = 0; i < num; i++)
237 {
238 PF *hdl = NULL;
239
240 if(ke[i].flags & EV_ERROR)
241 {
242 errno = ke[i].data;
243 /* XXX error == bad! -- adrian */
244 continue; /* XXX! */
245 }
246
247 switch (ke[i].filter)
248 {
249
250 case EVFILT_READ:
251 F = ke[i].udata;
252 if((hdl = F->read_handler) != NULL)
253 {
254 F->read_handler = NULL;
255 hdl(F, F->read_data);
256 }
257
258 break;
259
260 case EVFILT_WRITE:
261 F = ke[i].udata;
262 if((hdl = F->write_handler) != NULL)
263 {
264 F->write_handler = NULL;
265 hdl(F, F->write_data);
266 }
267 break;
268 #if defined(EVFILT_TIMER)
269 case EVFILT_TIMER:
270 rb_run_event(ke[i].udata);
271 break;
272 #endif
273 default:
274 /* Bad! -- adrian */
275 break;
276 }
277 }
278 return RB_OK;
279 }
280 static int can_do_event = 0;
281 int
282 rb_kqueue_supports_event(void)
283 {
284 struct kevent kv;
285 struct timespec ts;
286 int xkq;
287
288 if(can_do_event == 1)
289 return 1;
290 if(can_do_event == -1)
291 return 0;
292
293 xkq = kqueue();
294 ts.tv_sec = 0;
295 ts.tv_nsec = 1000;
296
297
298 EV_SET(&kv, (uintptr_t) 0x0, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0, 1, 0);
299 if(kevent(xkq, &kv, 1, NULL, 0, NULL) < 0)
300 {
301 can_do_event = -1;
302 close(xkq);
303 return 0;
304 }
305 close(xkq);
306 can_do_event = 1;
307 return 1;
308 }
309
310 int
311 rb_kqueue_sched_event(struct ev_entry *event, int when)
312 {
313 struct kevent kev;
314 int kep_flags;
315
316 kep_flags = EV_ADD;
317 if(event->frequency == 0)
318 kep_flags |= EV_ONESHOT;
319 EV_SET(&kev, (uintptr_t) event, EVFILT_TIMER, kep_flags, 0, when * 1000, event);
320 if(kevent(kq, &kev, 1, NULL, 0, NULL) < 0)
321 return 0;
322 return 1;
323 }
324
325 void
326 rb_kqueue_unsched_event(struct ev_entry *event)
327 {
328 struct kevent kev;
329 EV_SET(&kev, (uintptr_t) event, EVFILT_TIMER, EV_DELETE, 0, 0, event);
330 kevent(kq, &kev, 1, NULL, 0, NULL);
331 }
332
333 void
334 rb_kqueue_init_event(void)
335 {
336 return;
337 }
338
339 #else /* kqueue not supported */
340 int
341 rb_init_netio_kqueue(void)
342 {
343 errno = ENOSYS;
344 return -1;
345 }
346
347 void
348 rb_setselect_kqueue(rb_fde_t * F, unsigned int type, PF * handler, void *client_data)
349 {
350 errno = ENOSYS;
351 return;
352 }
353
354 int
355 rb_select_kqueue(long delay)
356 {
357 errno = ENOSYS;
358 return -1;
359 }
360
361 int
362 rb_setup_fd_kqueue(rb_fde_t * F)
363 {
364 errno = ENOSYS;
365 return -1;
366 }
367
368 #endif
369
370 #if !defined(HAVE_KEVENT) || !defined(KQUEUE_SCHED_EVENT)
371 void
372 rb_kqueue_init_event(void)
373 {
374 return;
375 }
376
377 int
378 rb_kqueue_sched_event(struct ev_entry *event, int when)
379 {
380 errno = ENOSYS;
381 return -1;
382 }
383
384 void
385 rb_kqueue_unsched_event(struct ev_entry *event)
386 {
387 return;
388 }
389
390 int
391 rb_kqueue_supports_event(void)
392 {
393 errno = ENOSYS;
394 return 0;
395 }
396 #endif /* !HAVE_KEVENT || !KQUEUE_SCHED_EVENT */