]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/io.c
add new IO layer
[ngircd-alex.git] / src / ngircd / io.c
1 /*
2  * This program is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 2 of the License, or
5  * (at your option) any later version.
6  * Please read the file COPYING, README and AUTHORS for more information.
7  *
8  * I/O abstraction interface.
9  * Copyright (c) 2005 Florian Westphal (westphal@foo.fh-furtwangen.de)
10  *
11  */
12
13 #include "portab.h"
14
15 static char UNUSED id[] = "$Id: io.c,v 1.1 2005/07/07 18:38:35 fw Exp $";
16
17 #include <assert.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <sys/time.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24
25 #include "array.h"
26 #include "io.h"
27 #include "log.h"
28
29
30 typedef struct {
31  void (*callback)(int, short);
32  int fd;
33  short what;
34 } io_event;
35
36 #define INIT_IOEVENT    { NULL, -1, 0, NULL }
37 #define IO_ERROR        4
38
39 #ifdef HAVE_EPOLL_CREATE
40 #define IO_USE_EPOLL    1
41 #else
42 # ifdef HAVE_KQUEUE
43 #define IO_USE_KQUEUE   1
44 # else
45 #define IO_USE_SELECT   1
46 #endif
47 #endif
48
49
50 #ifdef IO_USE_EPOLL
51 #include <sys/epoll.h>
52
53 static int io_masterfd;
54 static bool io_event_new_epoll(int fd, short what);
55 static bool io_event_change_epoll(int fd, short what);
56 static int io_dispatch_epoll(struct timeval *tv);
57 #endif
58
59 #ifdef IO_USE_KQUEUE
60 #include <sys/types.h>
61 #include <sys/event.h>
62 static array io_evcache;
63 static int io_masterfd;
64
65 static int io_dispatch_kqueue(struct timeval *tv);
66 static bool io_event_add_kqueue(int, short);
67 #endif
68
69 #ifdef IO_USE_SELECT
70 #include "defines.h"    /* for conn.h */
71 #include "conn.h"       /* for CONN_IDX (needed by resolve.h) */
72 #include "resolve.h"    /* for RES_STAT (needed by conf.h) */
73 #include "conf.h"       /* for Conf_MaxConnections */
74
75 static fd_set readers;
76 static fd_set writers;
77 static int select_maxfd;                /* the select() interface sucks badly */
78 static int io_dispatch_select(struct timeval *tv);
79 #endif
80
81 static array io_events;
82
83 static void io_docallback PARAMS((int fd, short what));
84
85 static io_event *
86 io_event_get(int fd)
87 {
88         io_event *i;
89         assert(fd >= 0);
90         i = (io_event *) array_get(&io_events, sizeof(io_event), fd);
91         assert(i);
92
93         if (!i) {
94 #ifdef DEBUG
95                 Log(LOG_DEBUG, "io_event_add(): EMPTY FOR fd %d", fd);
96 #endif
97                 return NULL;
98         }
99         return i;
100 }
101
102
103 bool
104 io_library_init(unsigned int eventsize)
105 {
106 #ifdef IO_USE_EPOLL
107         int ecreate_hint = (int)eventsize;
108         if (ecreate_hint <= 0)
109                 ecreate_hint = 128;
110 #endif
111
112 #ifdef IO_USE_SELECT
113 #ifdef FD_SETSIZE
114         if (eventsize >= FD_SETSIZE)
115                 eventsize = FD_SETSIZE - 1;
116 #endif
117 #endif
118         if (eventsize && !array_alloc(&io_events, sizeof(io_event), eventsize))
119                 eventsize = 0;
120
121 #ifdef IO_USE_EPOLL
122         io_masterfd = epoll_create(ecreate_hint);
123         Log(LOG_INFO,
124             "io subsystem: using epoll (hint size %d), initial io_event maxfd: %u, io_masterfd %d",
125             ecreate_hint, eventsize, io_masterfd);
126         return io_masterfd >= 0;
127 #endif
128 #ifdef IO_USE_SELECT
129         Log(LOG_INFO, "io subsystem: using select, initial io_event maxfd: %u",
130             eventsize);
131         FD_ZERO(&readers);
132         FD_ZERO(&writers);
133 #ifdef FD_SETSIZE
134         if (Conf_MaxConnections >= FD_SETSIZE) {
135                 Log(LOG_WARNING,
136                     "Conf_MaxConnections (%d) exceeds limit (%u), changed Conf_MaxConnections to %u",
137                     Conf_MaxConnections, FD_SETSIZE, FD_SETSIZE - 1);
138
139                 Conf_MaxConnections = FD_SETSIZE - 1;
140         }
141 #else
142         Log(LOG_WARNING,
143             "FD_SETSIZE undefined, don't know how many descriptors select() can handle on your platform");
144 #endif
145         return true;
146 #endif
147 #ifdef IO_USE_KQUEUE
148         io_masterfd = kqueue();
149
150         Log(LOG_INFO,
151             "io subsystem: using kqueue, initial io_event maxfd: %u, io_masterfd %d",
152             eventsize, io_masterfd);
153         return io_masterfd >= 0;
154 #endif
155 }
156
157
158 bool
159 io_library_shutdown(void)
160 {
161         unsigned int len = array_length(&io_events, sizeof(io_event));
162
163         while (len--) {
164                 if (NULL == io_event_get(len))
165                         continue;
166         }
167 #ifndef IO_USE_SELECT
168         close(io_masterfd);     /* kqueue, epoll */
169         io_masterfd = -1;
170 #else
171         FD_ZERO(&readers);
172         FD_ZERO(&writers);
173 #endif
174 #ifdef IO_USE_KQUEUE
175         array_free(&io_evcache);
176 #endif
177         return true;
178 }
179
180
181 bool
182 io_event_setcb(int fd, void (*cbfunc) (int, short))
183 {
184         io_event *i = io_event_get(fd);
185         if (!i)
186                 return false;
187
188         i->callback = cbfunc;
189         return true;
190 }
191
192
193 bool
194 io_event_create(int fd, short what, void (*cbfunc) (int, short))
195 {
196         io_event *i;
197
198         assert(fd >= 0);
199
200 #ifdef IO_USE_SELECT
201 #ifdef FD_SETSIZE
202         if (fd >= FD_SETSIZE) {
203                 Log(LOG_ERR,
204                     "fd %d exceeds FD_SETSIZE (%u) (select can't handle more file descriptors)",
205                     fd, FD_SETSIZE);
206                 return false;
207         }
208 #endif                          /* FD_SETSIZE */
209 #endif                          /* IO_USE_SELECT */
210
211         i = (io_event *) array_alloc(&io_events, sizeof(io_event), fd);
212         if (!i) {
213                 Log(LOG_WARNING,
214                     "array_alloc failed: could not allocate space for %d io_event structures",
215                     fd);
216                 return false;
217         }
218
219         i->fd = fd;
220         i->callback = cbfunc;
221 #ifdef IO_USE_EPOLL
222         i->what = what;
223         return io_event_new_epoll(fd, what);
224 #endif
225 #ifdef IO_USE_KQUEUE
226         i->what = what;
227         return io_event_add_kqueue(fd, what);
228 #endif
229 #ifdef IO_USE_SELECT
230         i->what = 0;
231         return io_event_add(fd, what);
232 #endif
233 }
234
235
236 #ifdef IO_USE_EPOLL
237 static bool
238 io_event_new_epoll(int fd, short what)
239 {
240         struct epoll_event ev = { 0, {0} };
241         ev.data.fd = fd;
242
243         if (what & IO_WANTREAD)
244                 ev.events = EPOLLIN | EPOLLPRI;
245         if (what & IO_WANTWRITE)
246                 ev.events |= EPOLLOUT;
247
248         return epoll_ctl(io_masterfd, EPOLL_CTL_ADD, fd, &ev) == 0;
249 }
250
251
252 static bool
253 io_event_change_epoll(int fd, short what)
254 {
255         struct epoll_event ev = { 0, {0} };
256         ev.data.fd = fd;
257
258         if (what & IO_WANTREAD)
259                 ev.events = EPOLLIN | EPOLLPRI;
260         if (what & IO_WANTWRITE)
261                 ev.events |= EPOLLOUT;
262
263         return epoll_ctl(io_masterfd, EPOLL_CTL_MOD, fd, &ev) == 0;
264 }
265 #endif
266
267 #ifdef IO_USE_KQUEUE
268 static bool
269 io_event_kqueue_commit_cache(void)
270 {
271         struct kevent *events;
272         bool ret;
273         int len = (int) array_length(&io_evcache, sizeof (struct kevent));
274  
275         if (!len) /* nothing to do */
276                 return true;
277
278         assert(len>0);
279
280         if (len < 0) {
281                 array_free(&io_evcache);
282                 return false;
283         }
284
285         events = array_start(&io_evcache);
286
287         assert(events);
288
289         ret = kevent(io_masterfd, events, len, NULL, 0, NULL) == 0;
290         if (ret)
291                 array_trunc(&io_evcache);
292         return ret;
293 }
294
295
296 static bool
297 io_event_add_kqueue(int fd, short what)
298 {
299         struct kevent kev;
300         short filter = 0;
301         unsigned int len = array_length(&io_evcache, sizeof kev);
302         bool ret;
303
304         if (what & IO_WANTREAD)
305                 filter = EVFILT_READ;
306
307         if (what & IO_WANTWRITE)
308                 filter |= EVFILT_WRITE;
309
310         if (len >= 100) {
311                 ret = io_event_kqueue_commit_cache();
312                 if (ret)
313                         array_trunc(&io_evcache);
314         }
315
316         EV_SET(&kev, fd, filter, EV_ADD | EV_ENABLE, 0, 0, NULL);
317         return array_catb(&io_evcache, (char*) &kev, sizeof (kev));
318 }
319 #endif
320
321
322 bool
323 io_event_add(int fd, short what)
324 {
325         io_event *i = io_event_get(fd);
326
327         assert(i);
328
329         if (!i)
330                 return false;
331         if (i->what == what)
332                 return true;
333 #ifdef DEBUG
334         Log(LOG_DEBUG, "io_event_add(): fd %d (arg: %d), what %d.", i->fd, fd,
335             what);
336 #endif
337
338         i->what |= what;
339
340 #ifdef IO_USE_EPOLL
341         return io_event_change_epoll(fd, i->what);
342 #endif
343
344 #ifdef IO_USE_KQUEUE
345         return io_event_add_kqueue(fd, what);
346 #endif
347
348 #ifdef IO_USE_SELECT
349         if (fd > select_maxfd)
350                 select_maxfd = fd;
351
352         if (what & IO_WANTREAD)
353                 FD_SET(fd, &readers);
354         if (what & IO_WANTWRITE)
355                 FD_SET(fd, &writers);
356
357         return true;
358 #endif
359 }
360
361
362 bool
363 io_setnonblock(int fd)
364 {
365         int flags = fcntl(fd, F_GETFL);
366         if (flags == -1)
367                 return false;
368
369 #ifndef O_NONBLOCK
370 #define O_NONBLOCK O_NDELAY
371 #endif
372         flags |= O_NONBLOCK;
373
374         return fcntl(fd, F_SETFL, flags) == 0;
375 }
376
377
378 bool
379 io_close(int fd)
380 {
381         io_event *i = io_event_get(fd);
382         if (i) {
383                 memset(i, 0, sizeof(io_event));
384                 i->fd = -1;
385         }
386 #ifdef IO_USE_SELECT
387         FD_CLR(fd, &writers);
388         FD_CLR(fd, &readers);
389
390         if (fd == select_maxfd)
391                 select_maxfd--;
392 #endif
393 #ifdef IO_USE_KQUEUE
394         if (array_length(&io_evcache, sizeof (struct kevent)))  /* pending data in cache? */
395                 io_event_kqueue_commit_cache();
396 #endif
397         return close(fd) == 0;  /* both epoll an kqueue will remove fd from all sets automatically */
398 }
399
400
401 bool
402 io_event_del(int fd, short what)
403 {
404 #ifdef IO_USE_KQUEUE
405         struct kevent kev;
406         short filter = 0;
407 #endif
408         io_event *i = io_event_get(fd);
409 #ifdef DEBUG
410         Log(LOG_DEBUG, "io_event_del(): trying to delete eventtype %d on fd %d",
411             what, fd);
412 #endif
413         assert(i);
414         if (!i)
415                 return true;
416
417         i->what &= ~what;
418
419 #ifdef IO_USE_EPOLL
420         return io_event_change_epoll(fd, i->what);
421 #endif
422
423 #ifdef IO_USE_KQUEUE
424         if (what & IO_WANTREAD)
425                 filter = EVFILT_READ;
426
427         if (what & IO_WANTWRITE)
428                 filter |= EVFILT_WRITE;
429
430         EV_SET(&kev, fd, filter, EV_DELETE, 0, 0, NULL);
431         return kevent(io_masterfd, &kev, 1, NULL, 0, NULL) == 0;
432 #endif
433
434 #ifdef IO_USE_SELECT
435         if (what & IO_WANTWRITE)
436                 FD_CLR(i->fd, &writers);
437
438         if (what & IO_WANTREAD)
439                 FD_CLR(i->fd, &readers);
440
441         return true;
442 #endif
443 }
444
445
446 #ifdef IO_USE_SELECT
447 static int
448 io_dispatch_select(struct timeval *tv)
449 {
450         fd_set readers_tmp = readers;
451         fd_set writers_tmp = writers;
452         short what;
453         int ret, i;
454         int fds_ready;
455         ret = select(select_maxfd + 1, &readers_tmp, &writers_tmp, NULL, tv);
456         if (ret <= 0)
457                 return ret;
458
459         fds_ready = ret;
460
461         for (i = 0; i <= select_maxfd; i++) {
462                 what = 0;
463                 if (FD_ISSET(i, &readers_tmp)) {
464                         what = IO_WANTREAD;
465                         fds_ready--;
466                 }
467
468                 if (FD_ISSET(i, &writers_tmp)) {
469                         what |= IO_WANTWRITE;
470                         fds_ready--;
471                 }
472                 if (what)
473                         io_docallback(i, what);
474                 if (fds_ready <= 0)
475                         break;
476         }
477
478         return ret;
479 }
480 #endif
481
482
483 #ifdef IO_USE_EPOLL
484 static int
485 io_dispatch_epoll(struct timeval *tv)
486 {
487         time_t sec = tv->tv_sec * 1000;
488         int i, total = 0, ret, timeout = tv->tv_usec + sec;
489         struct epoll_event epoll_ev[100];
490         short type;
491
492         if (timeout < 0)
493                 timeout = 1000;
494
495         do {
496                 ret = epoll_wait(io_masterfd, epoll_ev, 100, timeout);
497                 total += ret;
498                 if (ret <= 0)
499                         return total;
500
501                 for (i = 0; i < ret; i++) {
502                         type = 0;
503                         if (epoll_ev[i].events & (EPOLLERR | EPOLLHUP))
504                                 type = IO_ERROR;
505
506                         if (epoll_ev[i].events & (EPOLLIN | EPOLLPRI))
507                                 type |= IO_WANTREAD;
508
509                         if (epoll_ev[i].events & EPOLLOUT)
510                                 type |= IO_WANTWRITE;
511
512                         io_docallback(epoll_ev[i].data.fd, type);
513                 }
514
515                 timeout = 0;
516         } while (ret == 100);
517
518         return total;
519 }
520 #endif
521
522
523 #ifdef IO_USE_KQUEUE
524 static int
525 io_dispatch_kqueue(struct timeval *tv)
526 {
527         int i, total = 0, ret;
528         struct kevent kev[100];
529         struct kevents *newevents;
530         struct timespec ts;
531         int newevents_len;
532         short type;
533         ts.tv_sec = tv->tv_sec;
534         ts.tv_nsec = tv->tv_usec * 1000;
535         
536         do {
537                 newevents_len = array_length(&io_evcache, sizeof (struct kevent));
538                 newevents = (newevents_len > 0) ? array_start(&io_evcache) : NULL;
539                 assert(newevents_len >= 0);
540                 if (newevents_len < 0)
541                         newevents_len = 0;
542 #ifdef DEBUG
543                 if (newevents_len)
544                         assert(newevents);
545 #endif
546
547                 ret = kevent(io_masterfd, newevents, newevents_len, kev, 100, &ts);
548                 if ((newevents_len>0) && ret != -1)
549                         array_trunc(&io_evcache);
550
551                 total += ret;
552                 if (ret <= 0)
553                         return total;
554
555                 for (i = 0; i < ret; i++) {
556                         type = 0;
557                         if (kev[i].flags & EV_EOF)
558                                 type = IO_ERROR;
559
560                         if (kev[i].filter & EV_ERROR)
561                                 type = IO_ERROR;
562
563                         if (kev[i].filter & EVFILT_READ)
564                                 type |= IO_WANTREAD;
565
566                         if (kev[i].filter & EVFILT_WRITE)
567                                 type |= IO_WANTWRITE;
568
569                         io_docallback(kev[i].ident, type);
570                 }
571
572                 ts.tv_sec = 0;
573                 ts.tv_nsec = 0;
574
575         } while (ret == 100);
576
577         return total;
578 }
579 #endif
580
581
582 int
583 io_dispatch(struct timeval *tv)
584 {
585 #ifdef IO_USE_SELECT
586         return io_dispatch_select(tv);
587 #endif
588 #ifdef IO_USE_KQUEUE
589         return io_dispatch_kqueue(tv);
590 #endif
591 #ifdef IO_USE_EPOLL
592         return io_dispatch_epoll(tv);
593 #endif
594 }
595
596
597 /* call the callback function inside the struct matching fd */
598 static void
599 io_docallback(int fd, short what)
600 {
601         io_event *i;
602 #ifdef DEBUG
603         Log(LOG_DEBUG, "doing callback for fd %d, what %d", fd, what);
604 #endif
605         i = io_event_get(fd);
606         assert(i);
607
608         if (i->callback)        /* callback might be 0 if previous callback function called io_close on this fd */
609                 i->callback(fd, (what & IO_ERROR) ? i->what : what);
610         /* if error indicator is set, we return the event(s) the app asked for */
611 }