]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/io.c
io.c: try to cut down the number of ifdefs.
[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.29 2008/03/27 15:47:21 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 /* Enables extra debug messages in event add/delete/callback code. */
30 /* #define DEBUG_IO */
31
32 typedef struct {
33 #ifdef PROTOTYPES
34  void (*callback)(int, short);
35 #else
36  void (*callback)();
37 #endif
38  short what;
39 } io_event;
40
41 #define INIT_IOEVENT            { NULL, -1, 0, NULL }
42 #define IO_ERROR                4
43
44 #ifdef HAVE_EPOLL_CREATE
45 #  define IO_USE_EPOLL          1
46 #  ifdef HAVE_SELECT
47 #    define IO_USE_SELECT       1
48 #  endif
49 #else
50 #  ifdef HAVE_KQUEUE
51 #    define IO_USE_KQUEUE       1
52 #  else
53 #    ifdef HAVE_SYS_DEVPOLL_H
54 #      define IO_USE_DEVPOLL    1
55 #    else
56 #      ifdef HAVE_POLL
57 #        define IO_USE_POLL     1
58 #      else
59 #        ifdef HAVE_SELECT
60 #          define IO_USE_SELECT 1
61 #        else
62 #          error "no IO API available!?"
63 #        endif /* HAVE_SELECT */
64 #      endif /* HAVE_POLL */
65 #    endif /* HAVE_SYS_DEVPOLL_H */
66 #  endif /* HAVE_KQUEUE */
67 #endif /* HAVE_EPOLL_CREATE */
68
69 static bool library_initialized = false;
70
71 #ifdef IO_USE_EPOLL
72 #include <sys/epoll.h>
73
74 static int io_masterfd = -1;
75 static bool io_event_change_epoll(int fd, short what, const int action);
76 static int io_dispatch_epoll(struct timeval *tv);
77 #endif
78
79 #ifdef IO_USE_KQUEUE
80 #include <sys/types.h>
81 #include <sys/event.h>
82 static array io_evcache;
83 static int io_masterfd;
84
85 static int io_dispatch_kqueue(struct timeval *tv);
86 static bool io_event_change_kqueue(int, short, const int action);
87 #endif
88
89 #ifdef IO_USE_POLL
90 #include <poll.h>
91
92 static array pollfds;
93 static int poll_maxfd;
94
95 static bool io_event_change_poll PARAMS((int fd, short what));
96 #endif
97
98 #ifdef IO_USE_DEVPOLL
99 #include <sys/devpoll.h>
100 static int io_masterfd;
101
102 static bool io_event_change_devpoll(int fd, short what);
103 #endif
104
105 #ifdef IO_USE_SELECT
106 #include "defines.h"    /* for conn.h */
107 #include "conn.h"       /* for CONN_IDX (needed by resolve.h) */
108 #include "resolve.h"    /* for RES_STAT (needed by conf.h) */
109 #include "conf.h"       /* for Conf_MaxConnections */
110
111 static fd_set readers;
112 static fd_set writers;
113 static int select_maxfd;                /* the select() interface sucks badly */
114 static int io_dispatch_select(struct timeval *tv);
115
116 #ifndef IO_USE_EPOLL
117 #define io_masterfd -1
118 #endif
119 #endif /* IO_USE_SELECT */
120
121 static array io_events;
122
123 static void io_docallback PARAMS((int fd, short what));
124
125 #ifdef DEBUG_IO
126 static void io_debug(const char *s, int fd, int what)
127 {
128         Log(LOG_DEBUG, "%s: %d, %d\n", s, fd, what);
129 }
130 #else
131 static inline void io_debug(const char UNUSED *s,int UNUSED a, int UNUSED b) {/*NOTHING*/}
132 #endif
133
134 static io_event *
135 io_event_get(int fd)
136 {
137         io_event *i;
138
139         assert(fd >= 0);
140
141         i = (io_event *) array_get(&io_events, sizeof(io_event), (size_t) fd);
142
143         assert(i != NULL);
144
145         return i;
146 }
147
148
149 #ifdef IO_USE_DEVPOLL
150 static int
151 io_dispatch_devpoll(struct timeval *tv)
152 {
153         struct dvpoll dvp;
154         time_t sec = tv->tv_sec * 1000;
155         int i, total, ret, timeout = tv->tv_usec + sec;
156         short what;
157         struct pollfd p[100];
158
159         if (timeout < 0)
160                 timeout = 1000;
161
162         total = 0;
163         do {
164                 dvp.dp_timeout = timeout;
165                 dvp.dp_nfds = 100;
166                 dvp.dp_fds = p;
167                 ret = ioctl(io_masterfd, DP_POLL, &dvp);
168                 total += ret;
169                 if (ret <= 0)
170                         return total;
171                 for (i=0; i < ret ; i++) {
172                         what = 0;
173                         if (p[i].revents & (POLLIN|POLLPRI))
174                                 what = IO_WANTREAD;
175
176                         if (p[i].revents & POLLOUT)
177                                 what |= IO_WANTWRITE;
178
179                         if (p[i].revents && !what) {
180                                 /* other flag is set, probably POLLERR */
181                                 what = IO_ERROR;
182                         }
183                         io_docallback(p[i].fd, what);
184                 }
185         } while (ret == 100);
186
187         return total;
188 }
189
190
191 static bool
192 io_event_change_devpoll(int fd, short what)
193 {
194         struct pollfd p;
195
196         p.events = 0;
197
198         if (what & IO_WANTREAD)
199                 p.events = POLLIN | POLLPRI;
200         if (what & IO_WANTWRITE)
201                 p.events |= POLLOUT;
202
203         p.fd = fd;
204         return write(io_masterfd, &p, sizeof p) == (ssize_t)sizeof p;
205 }
206
207 static void
208 io_close_devpoll(int fd)
209 {
210         struct pollfd p;
211         p.events = POLLREMOVE;
212         p.fd = fd;
213         write(io_masterfd, &p, sizeof p);
214 }
215
216 static void
217 io_library_init_devpoll(unsigned int eventsize)
218 {
219         io_masterfd = open("/dev/poll", O_RDWR);
220         if (io_masterfd >= 0)
221                 library_initialized = true;
222         Log(LOG_INFO, "IO subsystem: /dev/poll (initial maxfd %u, masterfd %d).",
223                 eventsize, io_masterfd);
224 }
225 #else
226 static inline void io_close_devpoll(int UNUSED x) {/* NOTHING */}
227 static inline void io_library_init_devpoll(unsigned int UNUSED ev) {/*NOTHING*/}
228 #endif
229
230
231 #ifdef IO_USE_POLL
232 static int
233 io_dispatch_poll(struct timeval *tv)
234 {
235         time_t sec = tv->tv_sec * 1000;
236         int i, ret, timeout = tv->tv_usec + sec;
237         int fds_ready;
238         short what;
239         struct pollfd *p = array_start(&pollfds);
240
241         if (timeout < 0)
242                 timeout = 1000;
243
244         ret = poll(p, poll_maxfd + 1, timeout);
245         if (ret <= 0)
246                 return ret;
247
248         fds_ready = ret;
249         for (i=0; i <= poll_maxfd; i++) {
250                 what = 0;
251                 if (p[i].revents & (POLLIN|POLLPRI))
252                         what = IO_WANTREAD;
253
254                 if (p[i].revents & POLLOUT)
255                         what |= IO_WANTWRITE;
256
257                 if (p[i].revents && !what) {
258                         /* other flag is set, probably POLLERR */
259                         what = IO_ERROR;
260                 }
261                 if (what) {
262                         fds_ready--;
263                         io_docallback(i, what);
264                 }
265                 if (fds_ready <= 0)
266                         break;
267         }
268
269         return ret;
270 }
271
272 static bool
273 io_event_change_poll(int fd, short what)
274 {
275         struct pollfd *p;
276         short events = 0;
277
278         if (what & IO_WANTREAD)
279                 events = POLLIN | POLLPRI;
280         if (what & IO_WANTWRITE)
281                 events |= POLLOUT;
282
283         p = array_alloc(&pollfds, sizeof *p, fd);
284         if (p) {
285                 p->events = events;
286                 p->fd = fd;
287                 if (fd > poll_maxfd)
288                         poll_maxfd = fd;
289         }
290         return p != NULL;
291 }
292
293 static void
294 io_close_poll(int fd)
295 {
296         struct pollfd *p;
297         p = array_get(&pollfds, sizeof *p, fd);
298         if (!p) return;
299
300         p->fd = -1;
301         if (fd == poll_maxfd) {
302                 while (poll_maxfd > 0) {
303                         --poll_maxfd;
304                         p = array_get(&pollfds, sizeof *p, poll_maxfd);
305                         if (p && p->fd >= 0)
306                                 break;
307                 }
308         }
309 }
310
311 static void
312 io_library_init_poll(unsigned int eventsize)
313 {
314         struct pollfd *p;
315         array_init(&pollfds);
316         poll_maxfd = 0;
317         Log(LOG_INFO, "IO subsystem: poll (initial maxfd %u).",
318             eventsize);
319         p = array_alloc(&pollfds, sizeof(struct pollfd), eventsize);
320         if (p) {
321                 unsigned i;
322                 p = array_start(&pollfds);
323                 for (i = 0; i < eventsize; i++)
324                         p[i].fd = -1;
325
326                 library_initialized = true;
327         }
328 }
329 #else
330 static inline void io_close_poll(int UNUSED x) {/* NOTHING */}
331 static inline void io_library_init_poll(unsigned int UNUSED ev) {/*NOTHING*/}
332 #endif
333
334
335 #ifdef IO_USE_SELECT
336 static int
337 io_dispatch_select(struct timeval *tv)
338 {
339         fd_set readers_tmp = readers;
340         fd_set writers_tmp = writers;
341         short what;
342         int ret, i;
343         int fds_ready;
344         ret = select(select_maxfd + 1, &readers_tmp, &writers_tmp, NULL, tv);
345         if (ret <= 0)
346                 return ret;
347
348         fds_ready = ret;
349
350         for (i = 0; i <= select_maxfd; i++) {
351                 what = 0;
352                 if (FD_ISSET(i, &readers_tmp)) {
353                         what = IO_WANTREAD;
354                         fds_ready--;
355                 }
356
357                 if (FD_ISSET(i, &writers_tmp)) {
358                         what |= IO_WANTWRITE;
359                         fds_ready--;
360                 }
361                 if (what)
362                         io_docallback(i, what);
363                 if (fds_ready <= 0)
364                         break;
365         }
366
367         return ret;
368 }
369
370 static void
371 io_library_init_select(unsigned int eventsize)
372 {
373         if (library_initialized)
374                 return;
375         Log(LOG_INFO, "IO subsystem: select (initial maxfd %u).",
376             eventsize);
377         FD_ZERO(&readers);
378         FD_ZERO(&writers);
379 #ifdef FD_SETSIZE
380         if (Conf_MaxConnections >= (int)FD_SETSIZE) {
381                 Log(LOG_WARNING,
382                     "MaxConnections (%d) exceeds limit (%u), changed MaxConnections to %u.",
383                     Conf_MaxConnections, FD_SETSIZE, FD_SETSIZE - 1);
384
385                 Conf_MaxConnections = FD_SETSIZE - 1;
386         }
387 #endif /* FD_SETSIZE */
388         library_initialized = true;
389 }
390
391 static void
392 io_close_select(int fd)
393 {
394         io_event *i;
395
396         if (io_masterfd >= 0)   /* Are we using epoll()? */
397                 return;
398
399         FD_CLR(fd, &writers);
400         FD_CLR(fd, &readers);
401
402         i = io_event_get(fd);
403         if (!i) return;
404
405         if (fd == select_maxfd) {
406                 while (select_maxfd>0) {
407                         --select_maxfd; /* find largest fd */
408                         i = io_event_get(select_maxfd);
409                         if (i && i->callback) break;
410                 }
411         }
412 }
413 #else
414 static inline void io_library_init_select(int UNUSED x) {/* NOTHING */}
415 static inline void io_close_select(int UNUSED x) {/* NOTHING */}
416 #endif /* SELECT */
417
418
419 #ifdef IO_USE_EPOLL
420 static bool
421 io_event_change_epoll(int fd, short what, const int action)
422 {
423         struct epoll_event ev = { 0, {0} };
424         ev.data.fd = fd;
425
426         if (what & IO_WANTREAD)
427                 ev.events = EPOLLIN | EPOLLPRI;
428         if (what & IO_WANTWRITE)
429                 ev.events |= EPOLLOUT;
430
431         return epoll_ctl(io_masterfd, action, fd, &ev) == 0;
432 }
433
434 static int
435 io_dispatch_epoll(struct timeval *tv)
436 {
437         time_t sec = tv->tv_sec * 1000;
438         int i, total = 0, ret, timeout = tv->tv_usec + sec;
439         struct epoll_event epoll_ev[100];
440         short type;
441
442         if (timeout < 0)
443                 timeout = 1000;
444
445         do {
446                 ret = epoll_wait(io_masterfd, epoll_ev, 100, timeout);
447                 total += ret;
448                 if (ret <= 0)
449                         return total;
450
451                 for (i = 0; i < ret; i++) {
452                         type = 0;
453                         if (epoll_ev[i].events & (EPOLLERR | EPOLLHUP))
454                                 type = IO_ERROR;
455
456                         if (epoll_ev[i].events & (EPOLLIN | EPOLLPRI))
457                                 type |= IO_WANTREAD;
458
459                         if (epoll_ev[i].events & EPOLLOUT)
460                                 type |= IO_WANTWRITE;
461
462                         io_docallback(epoll_ev[i].data.fd, type);
463                 }
464
465                 timeout = 0;
466         } while (ret == 100);
467
468         return total;
469 }
470
471 static void
472 io_library_init_epoll(unsigned int eventsize)
473 {
474         int ecreate_hint = (int)eventsize;
475         if (ecreate_hint <= 0)
476                 ecreate_hint = 128;
477         io_masterfd = epoll_create(ecreate_hint);
478         if (io_masterfd >= 0) {
479                 library_initialized = true;
480                 Log(LOG_INFO,
481                     "IO subsystem: epoll (hint size %d, initial maxfd %u, masterfd %d).",
482                     ecreate_hint, eventsize, io_masterfd);
483                 return;
484         }
485 #ifdef IO_USE_SELECT
486         Log(LOG_INFO, "Can't initialize epoll() IO interface, falling back to select() ...");
487 #endif
488 }
489 #else
490 static inline void io_library_init_epoll(unsigned int UNUSED ev) {/* NOTHING */}
491 #endif /* IO_USE_EPOLL */
492
493
494 #ifdef IO_USE_KQUEUE
495 static bool
496 io_event_kqueue_commit_cache(void)
497 {
498         struct kevent *events;
499         bool ret;
500         int len = (int) array_length(&io_evcache, sizeof (struct kevent));
501
502         if (!len) /* nothing to do */
503                 return true;
504
505         assert(len>0);
506
507         if (len < 0) {
508                 array_free(&io_evcache);
509                 return false;
510         }
511
512         events = array_start(&io_evcache);
513
514         assert(events != NULL);
515
516         ret = kevent(io_masterfd, events, len, NULL, 0, NULL) == 0;
517         if (ret)
518                 array_trunc(&io_evcache);
519         return ret;
520 }
521
522 static bool
523 io_event_change_kqueue(int fd, short what, const int action)
524 {
525         struct kevent kev;
526         bool ret = true;
527
528         if (what & IO_WANTREAD) {
529                 EV_SET(&kev, fd, EVFILT_READ, action, 0, 0, 0);
530                 ret = array_catb(&io_evcache, (char*) &kev, sizeof (kev));
531                 if (!ret)
532                         ret = kevent(io_masterfd, &kev,1, NULL, 0, NULL) == 0;
533         }
534
535         if (ret && (what & IO_WANTWRITE)) {
536                 EV_SET(&kev, fd, EVFILT_WRITE, action, 0, 0, 0);
537                 ret = array_catb(&io_evcache, (char*) &kev, sizeof (kev));
538                 if (!ret)
539                         ret = kevent(io_masterfd, &kev, 1, NULL, 0, NULL) == 0;
540         }
541
542         if (array_length(&io_evcache, sizeof kev) >= 100)
543                 io_event_kqueue_commit_cache();
544         return ret;
545 }
546
547 static int
548 io_dispatch_kqueue(struct timeval *tv)
549 {
550         int i, total = 0, ret;
551         struct kevent kev[100];
552         struct kevent *newevents;
553         struct timespec ts;
554         int newevents_len;
555         ts.tv_sec = tv->tv_sec;
556         ts.tv_nsec = tv->tv_usec * 1000;
557
558         do {
559                 newevents_len = (int) array_length(&io_evcache, sizeof (struct kevent));
560                 newevents = (newevents_len > 0) ? array_start(&io_evcache) : NULL;
561                 assert(newevents_len >= 0);
562
563                 ret = kevent(io_masterfd, newevents, newevents_len, kev, 100, &ts);
564                 if (newevents && ret != -1)
565                         array_trunc(&io_evcache);
566
567                 total += ret;
568                 if (ret <= 0)
569                         return total;
570
571                 for (i = 0; i < ret; i++) {
572                         io_debug("dispatch_kqueue: fd, kev.flags", (int)kev[i].ident, kev[i].flags);
573                         if (kev[i].flags & (EV_EOF|EV_ERROR)) {
574                                 if (kev[i].flags & EV_ERROR)
575                                         Log(LOG_ERR, "kevent fd %d: EV_ERROR (%s)",
576                                                 (int)kev[i].ident, strerror((int)kev[i].data));
577                                 io_docallback((int)kev[i].ident, IO_ERROR);
578                                 continue;
579                         }
580
581                         switch (kev[i].filter) {
582                         case EVFILT_READ:
583                                 io_docallback((int)kev[i].ident, IO_WANTREAD);
584                                 break;
585                         case EVFILT_WRITE:
586                                 io_docallback((int)kev[i].ident, IO_WANTWRITE);
587                                 break;
588                         default:
589                                 LogDebug("Unknown kev.filter number %d for fd %d",
590                                         kev[i].filter, kev[i].ident);
591                                 /* Fall through */
592                         case EV_ERROR:
593                                 io_docallback((int)kev[i].ident, IO_ERROR);
594                                 break;
595                         }
596                 }
597                 ts.tv_sec = 0;
598                 ts.tv_nsec = 0;
599         } while (ret == 100);
600
601         return total;
602 }
603
604 static void
605 io_library_init_kqueue(unsigned int eventsize)
606 {
607         io_masterfd = kqueue();
608
609         Log(LOG_INFO,
610             "IO subsystem: kqueue (initial maxfd %u, masterfd %d)",
611             eventsize, io_masterfd);
612         if (io_masterfd >= 0)
613                 library_initialized = true;
614 }
615 #else
616 static inline void io_library_init_kqueue(unsigned int UNUSED ev) {/* NOTHING */}
617 #endif
618
619
620 bool
621 io_library_init(unsigned int eventsize)
622 {
623         if (library_initialized)
624                 return true;
625 #ifdef IO_USE_SELECT
626 #ifndef FD_SETSIZE
627         Log(LOG_WARNING,
628             "FD_SETSIZE undefined, don't know how many descriptors select() can handle on your platform ...");
629 #else
630         if (eventsize >= FD_SETSIZE)
631                 eventsize = FD_SETSIZE - 1;
632 #endif /* FD_SETSIZE */
633 #endif /* IO_USE_SELECT */
634         if ((eventsize > 0) && !array_alloc(&io_events, sizeof(io_event), (size_t)eventsize))
635                 eventsize = 0;
636
637         io_library_init_epoll(eventsize);
638         io_library_init_kqueue(eventsize);
639         io_library_init_devpoll(eventsize);
640         io_library_init_poll(eventsize);
641         io_library_init_select(eventsize);
642
643         return library_initialized;
644 }
645
646
647 void
648 io_library_shutdown(void)
649 {
650 #ifdef IO_USE_SELECT
651         FD_ZERO(&readers);
652         FD_ZERO(&writers);
653 #endif
654 #if defined(IO_USE_EPOLL) || defined(IO_USE_KQUEUE) || defined(IO_USE_DEVPOLL)
655         if (io_masterfd >= 0)
656                 close(io_masterfd);
657         io_masterfd = -1;
658 #endif
659 #ifdef IO_USE_KQUEUE
660         array_free(&io_evcache);
661 #endif
662         library_initialized = false;
663 }
664
665
666 bool
667 io_event_setcb(int fd, void (*cbfunc) (int, short))
668 {
669         io_event *i = io_event_get(fd);
670         if (!i)
671                 return false;
672
673         i->callback = cbfunc;
674         return true;
675 }
676
677
678 static bool
679 backend_create_ev(int fd, short what)
680 {
681         bool ret;
682 #ifdef IO_USE_DEVPOLL
683         ret = io_event_change_devpoll(fd, what);
684 #endif
685 #ifdef IO_USE_POLL
686         ret = io_event_change_poll(fd, what);
687 #endif
688 #ifdef IO_USE_EPOLL
689         ret = io_event_change_epoll(fd, what, EPOLL_CTL_ADD);
690 #endif
691 #ifdef IO_USE_KQUEUE
692         ret = io_event_change_kqueue(fd, what, EV_ADD|EV_ENABLE);
693 #endif
694 #ifdef IO_USE_SELECT
695         if (io_masterfd < 0)
696                 ret = io_event_add(fd, what);
697 #endif
698         return ret;
699 }
700
701
702 bool
703 io_event_create(int fd, short what, void (*cbfunc) (int, short))
704 {
705         bool ret;
706         io_event *i;
707
708         assert(fd >= 0);
709 #if defined(IO_USE_SELECT) && defined(FD_SETSIZE)
710         if (fd >= FD_SETSIZE) {
711                 Log(LOG_ERR,
712                     "fd %d exceeds FD_SETSIZE (%u) (select can't handle more file descriptors)",
713                     fd, FD_SETSIZE);
714                 return false;
715         }
716 #endif
717         i = (io_event *) array_alloc(&io_events, sizeof(io_event), (size_t) fd);
718         if (!i) {
719                 Log(LOG_WARNING,
720                     "array_alloc failed: could not allocate space for %d io_event structures",
721                     fd);
722                 return false;
723         }
724
725         i->callback = cbfunc;
726         i->what = 0;
727         ret = backend_create_ev(fd, what);
728         if (ret)
729                 i->what = what;
730         return ret;
731 }
732
733
734 bool
735 io_event_add(int fd, short what)
736 {
737         io_event *i = io_event_get(fd);
738
739         if (!i) return false;
740
741         if ((i->what & what) == what) /* event type is already registered */
742                 return true;
743
744         io_debug("io_event_add: fd, what", fd, what);
745
746         i->what |= what;
747 #ifdef IO_USE_EPOLL
748         if (io_masterfd >= 0)
749                 return io_event_change_epoll(fd, i->what, EPOLL_CTL_MOD);
750 #endif
751 #ifdef IO_USE_KQUEUE
752         return io_event_change_kqueue(fd, what, EV_ADD | EV_ENABLE);
753 #endif
754 #ifdef IO_USE_DEVPOLL
755         return io_event_change_devpoll(fd, i->what);
756 #endif
757 #ifdef IO_USE_POLL
758         return io_event_change_poll(fd, i->what);
759 #endif
760 #ifdef IO_USE_SELECT
761         if (fd > select_maxfd)
762                 select_maxfd = fd;
763
764         if (what & IO_WANTREAD)
765                 FD_SET(fd, &readers);
766         if (what & IO_WANTWRITE)
767                 FD_SET(fd, &writers);
768
769         return true;
770 #endif
771         return false;
772 }
773
774
775 bool
776 io_setnonblock(int fd)
777 {
778         int flags = fcntl(fd, F_GETFL);
779         if (flags == -1)
780                 return false;
781 #ifndef O_NONBLOCK
782 #define O_NONBLOCK O_NDELAY
783 #endif
784         flags |= O_NONBLOCK;
785
786         return fcntl(fd, F_SETFL, flags) == 0;
787 }
788
789
790 bool
791 io_close(int fd)
792 {
793         io_event *i;
794
795         i = io_event_get(fd);
796 #ifdef IO_USE_KQUEUE
797         if (array_length(&io_evcache, sizeof (struct kevent)))  /* pending data in cache? */
798                 io_event_kqueue_commit_cache();
799
800         /* both kqueue and epoll remove fd from all sets automatically on the last close
801          * of the descriptor. since we don't know if this is the last close we'll have
802          * to remove the set explicitly. */
803         if (i) {
804                 io_event_change_kqueue(fd, i->what, EV_DELETE);
805                 io_event_kqueue_commit_cache();
806         }
807 #endif
808         io_close_devpoll(fd);
809         io_close_poll(fd);
810         io_close_select(fd);
811 #ifdef IO_USE_EPOLL
812         io_event_change_epoll(fd, 0, EPOLL_CTL_DEL);
813 #endif
814         if (i) {
815                 i->callback = NULL;
816                 i->what = 0;
817         }
818         return close(fd) == 0;
819 }
820
821
822 bool
823 io_event_del(int fd, short what)
824 {
825         io_event *i = io_event_get(fd);
826
827         io_debug("io_event_del: trying to delete eventtype; fd, what", fd, what);
828         if (!i) return false;
829
830         if (!(i->what & what)) /* event is already disabled */
831                 return true;
832
833         i->what &= ~what;
834 #ifdef IO_USE_DEVPOLL
835         return io_event_change_devpoll(fd, i->what);
836 #endif
837 #ifdef IO_USE_POLL
838         return io_event_change_poll(fd, i->what);
839 #endif
840 #ifdef IO_USE_EPOLL
841         if (io_masterfd >= 0)
842                 return io_event_change_epoll(fd, i->what, EPOLL_CTL_MOD);
843 #endif
844 #ifdef IO_USE_KQUEUE
845         return io_event_change_kqueue(fd, what, EV_DISABLE);
846 #endif
847 #ifdef IO_USE_SELECT
848         if (what & IO_WANTWRITE)
849                 FD_CLR(fd, &writers);
850
851         if (what & IO_WANTREAD)
852                 FD_CLR(fd, &readers);
853         return true;
854 #endif
855         return false;
856 }
857
858
859 int
860 io_dispatch(struct timeval *tv)
861 {
862 #ifdef IO_USE_EPOLL
863         if (io_masterfd >= 0)
864                 return io_dispatch_epoll(tv);
865 #endif
866 #ifdef IO_USE_SELECT
867         return io_dispatch_select(tv);
868 #endif
869 #ifdef IO_USE_KQUEUE
870         return io_dispatch_kqueue(tv);
871 #endif
872 #ifdef IO_USE_DEVPOLL
873         return io_dispatch_devpoll(tv);
874 #endif
875 #ifdef IO_USE_POLL
876         return io_dispatch_poll(tv);
877 #endif
878         return -1;
879 }
880
881
882 /* call the callback function inside the struct matching fd */
883 static void
884 io_docallback(int fd, short what)
885 {
886         io_event *i = io_event_get(fd);
887
888         io_debug("io_docallback; fd, what", fd, what);
889
890         if (i->callback) {      /* callback might be NULL if a previous callback function
891                                    called io_close on this fd */
892                 i->callback(fd, (what & IO_ERROR) ? i->what : what);
893         }
894         /* if error indicator is set, we return the event(s) that were registered */
895 }