xref: /lighttpd1.4/src/fdevent_impl.c (revision df362494)
1 #include "first.h"
2 
3 #include "fdevent_impl.h"
4 #include "fdevent.h"
5 #include "buffer.h"
6 #include "log.h"
7 
8 #include <sys/types.h>
9 #include <unistd.h>
10 #include <errno.h>
11 #include <stdlib.h>
12 #include <string.h>
13 
14 #ifdef _WIN32
15 #include <winsock2.h>   /* closesocket */
16 #endif
17 
18 #ifdef FDEVENT_USE_LINUX_EPOLL
19 __attribute_cold__
20 static int fdevent_linux_sysepoll_init(struct fdevents *ev);
21 #endif
22 #ifdef FDEVENT_USE_FREEBSD_KQUEUE
23 __attribute_cold__
24 static int fdevent_freebsd_kqueue_init(struct fdevents *ev);
25 #endif
26 #ifdef FDEVENT_USE_SOLARIS_PORT
27 __attribute_cold__
28 static int fdevent_solaris_port_init(struct fdevents *ev);
29 #endif
30 #ifdef FDEVENT_USE_SOLARIS_DEVPOLL
31 __attribute_cold__
32 static int fdevent_solaris_devpoll_init(struct fdevents *ev);
33 #endif
34 #ifdef FDEVENT_USE_POLL
35 __attribute_cold__
36 static int fdevent_poll_init(struct fdevents *ev);
37 #endif
38 #ifdef FDEVENT_USE_SELECT
39 __attribute_cold__
40 static int fdevent_select_init(struct fdevents *ev);
41 #endif
42 
43 
44 int
45 fdevent_config (const char **event_handler_name, log_error_st *errh)
46 {
47     static const struct ev_map { fdevent_handler_t et; const char *name; }
48       event_handlers[] =
49     {
50         /* - epoll is most reliable
51          * - select works everywhere
52          */
53       #ifdef FDEVENT_USE_LINUX_EPOLL
54         { FDEVENT_HANDLER_LINUX_SYSEPOLL, "linux-sysepoll" },
55         { FDEVENT_HANDLER_LINUX_SYSEPOLL, "epoll" },
56       #endif
57       #ifdef FDEVENT_USE_SOLARIS_PORT
58         { FDEVENT_HANDLER_SOLARIS_PORT,   "solaris-eventports" },
59       #endif
60       #ifdef FDEVENT_USE_SOLARIS_DEVPOLL
61         { FDEVENT_HANDLER_SOLARIS_DEVPOLL,"solaris-devpoll" },
62       #endif
63       #ifdef FDEVENT_USE_FREEBSD_KQUEUE
64         { FDEVENT_HANDLER_FREEBSD_KQUEUE, "freebsd-kqueue" },
65         { FDEVENT_HANDLER_FREEBSD_KQUEUE, "kqueue" },
66       #endif
67       #ifdef FDEVENT_USE_POLL
68         { FDEVENT_HANDLER_POLL,           "poll" },
69       #endif
70       #ifdef FDEVENT_USE_SELECT
71         { FDEVENT_HANDLER_SELECT,         "select" },
72       #endif
73         { FDEVENT_HANDLER_UNSET,          NULL }
74     };
75 
76     const char *event_handler = *event_handler_name;
77     fdevent_handler_t et = FDEVENT_HANDLER_UNSET;
78 
79     if (NULL != event_handler && 0 == strcmp(event_handler, "libev"))
80         event_handler = NULL;
81   #ifdef FDEVENT_USE_POLL
82     if (NULL != event_handler && 0 == strcmp(event_handler, "select"))
83         event_handler = "poll";
84   #endif
85 
86     if (NULL == event_handler) {
87         /* choose a good default
88          *
89          * the event_handler list is sorted by 'goodness'
90          * taking the first available should be the best solution
91          */
92         et = event_handlers[0].et;
93         *event_handler_name = event_handlers[0].name;
94 
95         if (FDEVENT_HANDLER_UNSET == et) {
96             log_error(errh, __FILE__, __LINE__,
97               "sorry, there is no event handler for this system");
98 
99             return -1;
100         }
101     }
102     else {
103         /*
104          * User override
105          */
106 
107         for (uint32_t i = 0; event_handlers[i].name; ++i) {
108             if (0 == strcmp(event_handlers[i].name, event_handler)) {
109                 et = event_handlers[i].et;
110                 break;
111             }
112         }
113 
114         if (FDEVENT_HANDLER_UNSET == et) {
115             log_error(errh, __FILE__, __LINE__,
116               "the selected event-handler in unknown or not supported: %s",
117               event_handler);
118             return -1;
119         }
120     }
121 
122     return et;
123 }
124 
125 
126 const char *
127 fdevent_show_event_handlers (void)
128 {
129     return
130       "\nEvent Handlers:\n\n"
131      #ifdef FDEVENT_USE_SELECT
132       "\t+ select (generic)\n"
133      #else
134       "\t- select (generic)\n"
135      #endif
136      #ifdef FDEVENT_USE_POLL
137       "\t+ poll (Unix)\n"
138      #else
139       "\t- poll (Unix)\n"
140      #endif
141      #ifdef FDEVENT_USE_LINUX_EPOLL
142       "\t+ epoll (Linux)\n"
143      #else
144       "\t- epoll (Linux)\n"
145      #endif
146      #ifdef FDEVENT_USE_SOLARIS_DEVPOLL
147       "\t+ /dev/poll (Solaris)\n"
148      #else
149       "\t- /dev/poll (Solaris)\n"
150      #endif
151      #ifdef FDEVENT_USE_SOLARIS_PORT
152       "\t+ eventports (Solaris)\n"
153      #else
154       "\t- eventports (Solaris)\n"
155      #endif
156      #ifdef FDEVENT_USE_FREEBSD_KQUEUE
157       "\t+ kqueue (FreeBSD)\n"
158      #else
159       "\t- kqueue (FreeBSD)\n"
160      #endif
161       ;
162 }
163 
164 
165 fdevents *
166 fdevent_init (const char *event_handler, int *max_fds, int *cur_fds, log_error_st *errh)
167 {
168     fdevents *ev;
169     uint32_t maxfds = (0 != *max_fds)
170       ? (uint32_t)*max_fds
171       : 4096;
172     int type = fdevent_config(&event_handler, errh);
173     if (type <= 0) return NULL;
174 
175     fdevent_socket_nb_cloexec_init();
176 
177       #ifdef FDEVENT_USE_SELECT
178     /* select limits itself
179      * as it is a hard limit and will lead to a segfault we add some safety
180      * */
181     if (type == FDEVENT_HANDLER_SELECT) {
182         if (maxfds > (uint32_t)FD_SETSIZE - 200)
183             maxfds = (uint32_t)FD_SETSIZE - 200;
184     }
185       #endif
186     *max_fds = (int)maxfds;
187     ++maxfds; /*(+1 for event-handler fd)*/
188 
189     ev = ck_calloc(1, sizeof(*ev));
190     ev->errh = errh;
191     ev->cur_fds = cur_fds;
192     ev->event_handler = event_handler;
193     ev->fdarray = ck_calloc(maxfds, sizeof(*ev->fdarray));
194     ev->maxfds = maxfds;
195 
196     switch(type) {
197      #ifdef FDEVENT_USE_POLL
198       case FDEVENT_HANDLER_POLL:
199         if (0 == fdevent_poll_init(ev)) return ev;
200         break;
201      #endif
202      #ifdef FDEVENT_USE_SELECT
203       case FDEVENT_HANDLER_SELECT:
204         if (0 == fdevent_select_init(ev)) return ev;
205         break;
206      #endif
207      #ifdef FDEVENT_USE_LINUX_EPOLL
208       case FDEVENT_HANDLER_LINUX_SYSEPOLL:
209         if (0 == fdevent_linux_sysepoll_init(ev)) return ev;
210         break;
211      #endif
212      #ifdef FDEVENT_USE_SOLARIS_DEVPOLL
213       case FDEVENT_HANDLER_SOLARIS_DEVPOLL:
214         if (0 == fdevent_solaris_devpoll_init(ev)) return ev;
215         break;
216      #endif
217      #ifdef FDEVENT_USE_SOLARIS_PORT
218       case FDEVENT_HANDLER_SOLARIS_PORT:
219         if (0 == fdevent_solaris_port_init(ev)) return ev;
220         break;
221      #endif
222      #ifdef FDEVENT_USE_FREEBSD_KQUEUE
223       case FDEVENT_HANDLER_FREEBSD_KQUEUE:
224         if (0 == fdevent_freebsd_kqueue_init(ev)) return ev;
225         break;
226      #endif
227       /*case FDEVENT_HANDLER_UNSET:*/
228       default:
229         break;
230     }
231 
232     free(ev->fdarray);
233     free(ev);
234 
235     log_error(errh, __FILE__, __LINE__,
236       "event-handler failed: %s; "
237       "try to set server.event-handler = \"poll\" or \"select\"",
238       event_handler);
239     return NULL;
240 }
241 
242 
243 void
244 fdevent_free (fdevents *ev)
245 {
246     if (!ev) return;
247     if (ev->free) ev->free(ev);
248 
249     for (uint32_t i = 0; i < ev->maxfds; ++i) {
250         /* (fdevent_sched_run() should already have been run,
251          *  but take reasonable precautions anyway) */
252         if (ev->fdarray[i])
253             free((fdnode *)((uintptr_t)ev->fdarray[i] & ~0x3));
254     }
255 
256     free(ev->fdarray);
257     free(ev);
258 }
259 
260 
261 int
262 fdevent_reset (fdevents *ev)
263 {
264     int rc = (NULL != ev->reset) ? ev->reset(ev) : 0;
265     if (-1 == rc) {
266         log_error(ev->errh, __FILE__, __LINE__,
267           "event-handler failed: %s; "
268           "try to set server.event-handler = \"poll\" or \"select\"",
269           ev->event_handler ? ev->event_handler : "");
270     }
271     return rc;
272 }
273 
274 
275 static void
276 fdevent_sched_run (fdevents * const ev)
277 {
278     for (fdnode *fdn = ev->pendclose; fdn; ) {
279         int fd, rc;
280       #ifdef _WIN32
281         rc = (uintptr_t)fdn & 0x3;
282       #endif
283         fdn = (fdnode *)((uintptr_t)fdn & ~0x3);
284         fd = fdn->fd;
285       #ifdef _WIN32
286         if (rc == 0x1) {
287             rc = closesocket(fd);
288         }
289         else if (rc == 0x2) {
290             rc = close(fd);
291         }
292       #else
293         rc = close(fd);
294       #endif
295 
296         if (0 != rc) {
297             log_perror(ev->errh, __FILE__, __LINE__, "close failed %d", fd);
298         }
299         else {
300             --(*ev->cur_fds);
301         }
302 
303         fdnode * const fdn_tmp = fdn;
304         fdn = (fdnode *)fdn->ctx; /* next */
305         /*(fdevent_unregister)*/
306         free(fdn_tmp); /*fdnode_free(fdn_tmp);*/
307         ev->fdarray[fd] = NULL;
308     }
309     ev->pendclose = NULL;
310 }
311 
312 
313 int
314 fdevent_poll (fdevents * const ev, const int timeout_ms)
315 {
316     const int n = ev->poll(ev, ev->pendclose ? 0 : timeout_ms);
317     if (n >= 0)
318         fdevent_sched_run(ev);
319     else if (errno != EINTR)
320         log_perror(ev->errh, __FILE__, __LINE__, "fdevent_poll failed");
321     return n;
322 }
323 
324 
325 #ifdef FDEVENT_USE_LINUX_EPOLL
326 
327 #include <sys/epoll.h>
328 
329 static int
330 fdevent_linux_sysepoll_event_del (fdevents *ev, fdnode *fdn)
331 {
332     return epoll_ctl(ev->epoll_fd, EPOLL_CTL_DEL, fdn->fd, NULL);
333 }
334 
335 static int
336 fdevent_linux_sysepoll_event_set (fdevents *ev, fdnode *fdn, int events)
337 {
338     int op = (-1 == fdn->fde_ndx) ? EPOLL_CTL_ADD : EPOLL_CTL_MOD;
339     int fd = fdn->fde_ndx = fdn->fd;
340     struct epoll_event ep;
341   #ifndef EPOLLRDHUP
342     events &= ~FDEVENT_RDHUP;
343   #endif
344     ep.events = events | EPOLLERR | EPOLLHUP;
345     ep.data.ptr = fdn;
346     return epoll_ctl(ev->epoll_fd, op, fd, &ep);
347 }
348 
349 static int
350 fdevent_linux_sysepoll_poll (fdevents * const ev, int timeout_ms)
351 {
352     struct epoll_event * const restrict epoll_events = ev->epoll_events;
353     int n = epoll_wait(ev->epoll_fd, epoll_events, ev->maxfds, timeout_ms);
354     for (int i = 0; i < n; ++i) {
355         fdnode * const fdn = (fdnode *)epoll_events[i].data.ptr;
356         int revents = epoll_events[i].events;
357         if ((fdevent_handler)NULL != fdn->handler)
358             (*fdn->handler)(fdn->ctx, revents);
359     }
360     return n;
361 }
362 
363 __attribute_cold__
364 static void
365 fdevent_linux_sysepoll_free (fdevents *ev)
366 {
367     close(ev->epoll_fd);
368     free(ev->epoll_events);
369 }
370 
371 __attribute_cold__
372 static int
373 fdevent_linux_sysepoll_init (fdevents *ev)
374 {
375     force_assert(EPOLLIN    == FDEVENT_IN);
376     force_assert(EPOLLPRI   == FDEVENT_PRI);
377     force_assert(EPOLLOUT   == FDEVENT_OUT);
378     force_assert(EPOLLERR   == FDEVENT_ERR);
379     force_assert(EPOLLHUP   == FDEVENT_HUP);
380   #ifdef EPOLLRDHUP
381     force_assert(EPOLLRDHUP == FDEVENT_RDHUP);
382   #endif
383 
384     ev->type      = FDEVENT_HANDLER_LINUX_SYSEPOLL;
385     ev->event_set = fdevent_linux_sysepoll_event_set;
386     ev->event_del = fdevent_linux_sysepoll_event_del;
387     ev->poll      = fdevent_linux_sysepoll_poll;
388     ev->free      = fdevent_linux_sysepoll_free;
389 
390   #ifdef EPOLL_CLOEXEC
391     if (-1 == (ev->epoll_fd = epoll_create1(EPOLL_CLOEXEC))) return -1;
392   #else
393     if (-1 == (ev->epoll_fd = epoll_create(ev->maxfds))) return -1;
394     fdevent_setfd_cloexec(ev->epoll_fd);
395   #endif
396 
397     ev->epoll_events = ck_calloc(ev->maxfds, sizeof(*ev->epoll_events));
398 
399     return 0;
400 }
401 
402 #endif /* FDEVENT_USE_LINUX_EPOLL */
403 
404 
405 #ifdef FDEVENT_USE_FREEBSD_KQUEUE
406 
407 #include <sys/event.h>
408 #include <sys/time.h>
409 #include <fcntl.h>
410 
411 static int
412 fdevent_freebsd_kqueue_event_del (fdevents *ev, fdnode *fdn)
413 {
414     struct kevent kev[2];
415     struct timespec ts = {0, 0};
416     int fd = fdn->fd;
417     int n = 0;
418     int oevents = fdn->events;
419 
420     if (oevents & FDEVENT_IN)  {
421         EV_SET(&kev[n], fd, EVFILT_READ, EV_DELETE, 0, 0, fdn);
422         n++;
423     }
424     if (oevents & FDEVENT_OUT)  {
425         EV_SET(&kev[n], fd, EVFILT_WRITE, EV_DELETE, 0, 0, fdn);
426         n++;
427     }
428 
429     return (0 != n) ? kevent(ev->kq_fd, kev, n, NULL, 0, &ts) : 0;
430     /*(kevent() changelist still processed on EINTR,
431      * but EINTR should not be received since 0 == nevents)*/
432 }
433 
434 static int
435 fdevent_freebsd_kqueue_event_set (fdevents *ev, fdnode *fdn, int events)
436 {
437     struct kevent kev[2];
438     struct timespec ts = {0, 0};
439     int fd = fdn->fde_ndx = fdn->fd;
440     int n = 0;
441     int oevents = fdn->events;
442     int addevents = events & ~oevents;
443     int delevents = ~events & oevents;
444 
445     if (addevents & FDEVENT_IN)  {
446         EV_SET(&kev[n], fd, EVFILT_READ, EV_ADD, 0, 0, fdn);
447         n++;
448     }
449     else if (delevents & FDEVENT_IN) {
450         EV_SET(&kev[n], fd, EVFILT_READ, EV_DELETE, 0, 0, fdn);
451         n++;
452     }
453 
454     if (addevents & FDEVENT_OUT)  {
455         EV_SET(&kev[n], fd, EVFILT_WRITE, EV_ADD, 0, 0, fdn);
456         n++;
457     }
458     else if (delevents & FDEVENT_OUT) {
459         EV_SET(&kev[n], fd, EVFILT_WRITE, EV_DELETE, 0, 0, fdn);
460         n++;
461     }
462 
463     return (0 != n) ? kevent(ev->kq_fd, kev, n, NULL, 0, &ts) : 0;
464     /*(kevent() changelist still processed on EINTR,
465      * but EINTR should not be received since 0 == nevents)*/
466 }
467 
468 static int
469 fdevent_freebsd_kqueue_poll (fdevents * const ev, int timeout_ms)
470 {
471     struct timespec ts;
472     ts.tv_sec  = timeout_ms / 1000;
473     ts.tv_nsec = (timeout_ms % 1000) * 1000000;
474 
475     struct kevent * const restrict kq_results = ev->kq_results;
476     const int n = kevent(ev->kq_fd, NULL, 0, kq_results, ev->maxfds, &ts);
477 
478     for (int i = 0; i < n; ++i) {
479         fdnode * const fdn = (fdnode *)kq_results[i].udata;
480         int filt = kq_results[i].filter;
481         int e = kq_results[i].flags;
482         if ((fdevent_handler)NULL != fdn->handler) {
483             int revents = (filt == EVFILT_READ) ? FDEVENT_IN : FDEVENT_OUT;
484             if (e & EV_EOF)
485                 revents |= (filt == EVFILT_READ ? FDEVENT_RDHUP : FDEVENT_HUP);
486             if (e & EV_ERROR)
487                 revents |= FDEVENT_ERR;
488             (*fdn->handler)(fdn->ctx, revents);
489         }
490     }
491     return n;
492 }
493 
494 __attribute_cold__
495 static int
496 fdevent_freebsd_kqueue_reset (fdevents *ev)
497 {
498   #ifdef __NetBSD__
499     ev->kq_fd = kqueue1(O_NONBLOCK|O_CLOEXEC|O_NOSIGPIPE);
500     return (-1 != ev->kq_fd) ? 0 : -1;
501   #else
502     ev->kq_fd = kqueue();
503     if (-1 == ev->kq_fd) return -1;
504     fdevent_setfd_cloexec(ev->kq_fd);
505     return 0;
506   #endif
507 }
508 
509 __attribute_cold__
510 static void
511 fdevent_freebsd_kqueue_free (fdevents *ev)
512 {
513     close(ev->kq_fd);
514     free(ev->kq_results);
515 }
516 
517 __attribute_cold__
518 static int
519 fdevent_freebsd_kqueue_init (fdevents *ev)
520 {
521     ev->type       = FDEVENT_HANDLER_FREEBSD_KQUEUE;
522     ev->event_set  = fdevent_freebsd_kqueue_event_set;
523     ev->event_del  = fdevent_freebsd_kqueue_event_del;
524     ev->poll       = fdevent_freebsd_kqueue_poll;
525     ev->reset      = fdevent_freebsd_kqueue_reset;
526     ev->free       = fdevent_freebsd_kqueue_free;
527     ev->kq_fd      = -1;
528     ev->kq_results = ck_calloc(ev->maxfds, sizeof(*ev->kq_results));
529     return 0;
530 }
531 
532 #endif /* FDEVENT_USE_FREEBSD_KQUEUE */
533 
534 
535 #ifdef FDEVENT_USE_SOLARIS_PORT
536 
537 #include <sys/poll.h>
538 #include <fcntl.h>
539 
540 static int
541 fdevent_solaris_port_event_del (fdevents *ev, fdnode *fdn)
542 {
543     return port_dissociate(ev->port_fd, PORT_SOURCE_FD, fdn->fd);
544 }
545 
546 static int
547 fdevent_solaris_port_event_set (fdevents *ev, fdnode *fdn, int events)
548 {
549     int fd = fdn->fde_ndx = fdn->fd;
550     intptr_t ud = events & (POLLIN|POLLOUT);
551     return port_associate(ev->port_fd,PORT_SOURCE_FD,fd,(int)ud,(void*)ud);
552 }
553 
554 /* if there is any error it will return the return values of port_getn,
555  * otherwise it will return number of events */
556 static int
557 fdevent_solaris_port_poll (fdevents *ev, int timeout_ms)
558 {
559     const int pfd = ev->port_fd;
560     int ret;
561     unsigned int available_events, wait_for_events = 0;
562 
563     struct timespec  timeout;
564 
565     timeout.tv_sec  = timeout_ms/1000L;
566     timeout.tv_nsec = (timeout_ms % 1000L) * 1000000L;
567 
568     /* get the number of file descriptors with events */
569     if ((ret = port_getn(pfd, ev->port_events, 0, &wait_for_events, &timeout)) < 0) return ret;
570 
571     /* wait for at least one event */
572     if (0 == wait_for_events) wait_for_events = 1;
573 
574     available_events = wait_for_events;
575 
576     /* get the events of the file descriptors */
577     if ((ret = port_getn(pfd, ev->port_events, ev->maxfds, &available_events, &timeout)) < 0) {
578         /* if errno == ETIME and available_event == wait_for_events we didn't get any events */
579         /* for other errors we didn't get any events either */
580         if (!(errno == ETIME && wait_for_events != available_events)) return ret;
581     }
582 
583     for (int i = 0; i < (int)available_events; ++i) {
584         int fd = (int)ev->port_events[i].portev_object;
585         fdnode * const fdn = ev->fdarray[fd];
586         const intptr_t ud = (intptr_t)ev->port_events[i].portev_user;
587         int revents = ev->port_events[i].portev_events;
588         if (0 == ((uintptr_t)fdn & 0x3)) {
589             if (port_associate(pfd,PORT_SOURCE_FD,fd,(int)ud,(void*)ud) < 0)
590                 log_error(ev->errh,__FILE__,__LINE__,"port_associate failed");
591             (*fdn->handler)(fdn->ctx, revents);
592         }
593         else {
594             fdn->fde_ndx = -1;
595         }
596     }
597     return available_events;
598 }
599 
600 __attribute_cold__
601 static void
602 fdevent_solaris_port_free (fdevents *ev)
603 {
604     close(ev->port_fd);
605     free(ev->port_events);
606 }
607 
608 __attribute_cold__
609 static int
610 fdevent_solaris_port_init (fdevents *ev)
611 {
612     force_assert(POLLIN    == FDEVENT_IN);
613     force_assert(POLLPRI   == FDEVENT_PRI);
614     force_assert(POLLOUT   == FDEVENT_OUT);
615     force_assert(POLLERR   == FDEVENT_ERR);
616     force_assert(POLLHUP   == FDEVENT_HUP);
617     force_assert(POLLNVAL  == FDEVENT_NVAL);
618   #ifdef POLLRDHUP
619     force_assert(POLLRDHUP == FDEVENT_RDHUP);
620   #endif
621 
622     ev->type        = FDEVENT_HANDLER_SOLARIS_PORT;
623     ev->event_set   = fdevent_solaris_port_event_set;
624     ev->event_del   = fdevent_solaris_port_event_del;
625     ev->poll        = fdevent_solaris_port_poll;
626     ev->free        = fdevent_solaris_port_free;
627     ev->port_events = ck_calloc(ev->maxfds, sizeof(*ev->port_events));
628 
629     if ((ev->port_fd = port_create()) < 0) return -1;
630 
631     return 0;
632 }
633 
634 #endif /* FDEVENT_USE_SOLARIS_PORT */
635 
636 
637 #ifdef FDEVENT_USE_SOLARIS_DEVPOLL
638 
639 #include <sys/devpoll.h>
640 #include <sys/ioctl.h>
641 #include <fcntl.h>
642 
643 static int
644 fdevent_solaris_devpoll_event_del (fdevents *ev, fdnode *fdn)
645 {
646     struct pollfd pfd;
647     pfd.fd = fdn->fd;
648     pfd.events = POLLREMOVE;
649     pfd.revents = 0;
650     return (-1 != write(ev->devpoll_fd, &pfd, sizeof(pfd))) ? 0 : -1;
651 }
652 
653 static int
654 fdevent_solaris_devpoll_event_set (fdevents *ev, fdnode *fdn, int events)
655 {
656     struct pollfd pfd;
657     pfd.fd = fdn->fde_ndx = fdn->fd;
658   #ifndef POLLRDHUP
659     events &= ~FDEVENT_RDHUP;
660   #endif
661     pfd.events = events;
662     pfd.revents = 0;
663     return (-1 != write(ev->devpoll_fd, &pfd, sizeof(pfd))) ? 0 : -1;
664 }
665 
666 static int
667 fdevent_solaris_devpoll_poll (fdevents *ev, int timeout_ms)
668 {
669     fdnode ** const fdarray = ev->fdarray;
670     struct pollfd * const devpollfds = ev->devpollfds;
671     struct dvpoll dopoll;
672 
673     dopoll.dp_timeout = timeout_ms;
674     dopoll.dp_nfds = ev->maxfds - 1;
675     dopoll.dp_fds = devpollfds;
676 
677     const int n = ioctl(ev->devpoll_fd, DP_POLL, &dopoll);
678 
679     for (int i = 0; i < n; ++i) {
680         fdnode * const fdn = fdarray[devpollfds[i].fd];
681         int revents = devpollfds[i].revents;
682         if (0 == ((uintptr_t)fdn & 0x3))
683             (*fdn->handler)(fdn->ctx, revents);
684     }
685     return n;
686 }
687 
688 __attribute_cold__
689 static int
690 fdevent_solaris_devpoll_reset (fdevents *ev)
691 {
692     /* a forked process does only inherit the filedescriptor,
693      * but every operation on the device will lead to a EACCES */
694     ev->devpoll_fd = fdevent_open_cloexec("/dev/poll", 1, O_RDWR, 0);
695     return (ev->devpoll_fd >= 0) ? 0 : -1;
696 }
697 
698 __attribute_cold__
699 static void
700 fdevent_solaris_devpoll_free (fdevents *ev)
701 {
702     free(ev->devpollfds);
703     close(ev->devpoll_fd);
704 }
705 
706 __attribute_cold__
707 static int
708 fdevent_solaris_devpoll_init (fdevents *ev)
709 {
710     force_assert(POLLIN    == FDEVENT_IN);
711     force_assert(POLLPRI   == FDEVENT_PRI);
712     force_assert(POLLOUT   == FDEVENT_OUT);
713     force_assert(POLLERR   == FDEVENT_ERR);
714     force_assert(POLLHUP   == FDEVENT_HUP);
715     force_assert(POLLNVAL  == FDEVENT_NVAL);
716   #ifdef POLLRDHUP
717     force_assert(POLLRDHUP == FDEVENT_RDHUP);
718   #endif
719 
720     ev->type       = FDEVENT_HANDLER_SOLARIS_DEVPOLL;
721     ev->event_set  = fdevent_solaris_devpoll_event_set;
722     ev->event_del  = fdevent_solaris_devpoll_event_del;
723     ev->poll       = fdevent_solaris_devpoll_poll;
724     ev->reset      = fdevent_solaris_devpoll_reset;
725     ev->free       = fdevent_solaris_devpoll_free;
726     ev->devpoll_fd = -1;
727     ev->devpollfds = ck_calloc(ev->maxfds, sizeof(*ev->devpollfds));
728     return 0;
729 }
730 
731 #endif /* FDEVENT_USE_SOLARIS_DEVPOLL */
732 
733 
734 #ifdef FDEVENT_USE_POLL
735 
736 #ifdef HAVE_POLL_H
737 #include <poll.h>
738 #else
739 #include <sys/poll.h>
740 #endif
741 
742 static int
743 fdevent_poll_event_del (fdevents *ev, fdnode *fdn)
744 {
745     int fd = fdn->fd;
746     int k = fdn->fde_ndx;
747     if ((uint32_t)k >= ev->used || ev->pollfds[k].fd != fd)
748         return (errno = EINVAL, -1);
749 
750     ev->pollfds[k].fd = -1;
751     /* ev->pollfds[k].events = 0; */
752     /* ev->pollfds[k].revents = 0; */
753 
754     if (ev->unused.size == ev->unused.used) {
755         ck_realloc_u32((void **)&ev->unused.ptr, ev->unused.size,
756                        16, sizeof(*ev->unused.ptr));
757         ev->unused.size += 16;
758     }
759 
760     ev->unused.ptr[ev->unused.used++] = k;
761 
762     return 0;
763 }
764 
765 static int
766 fdevent_poll_event_set (fdevents *ev, fdnode *fdn, int events)
767 {
768     int fd = fdn->fd;
769     int k = fdn->fde_ndx;
770 
771   #ifndef POLLRDHUP
772     events &= ~FDEVENT_RDHUP;
773   #endif
774 
775     if (k >= 0) {
776         if ((uint32_t)k >= ev->used || ev->pollfds[k].fd != fd)
777             return (errno = EINVAL, -1);
778         ev->pollfds[k].events = events;
779         return 0;
780     }
781 
782     if (ev->unused.used > 0) {
783         k = ev->unused.ptr[--ev->unused.used];
784     }
785     else {
786         if (ev->size == ev->used) {
787             ck_realloc_u32((void **)&ev->pollfds, ev->size,
788                            16, sizeof(*ev->pollfds));
789             ev->size += 16;
790         }
791 
792         k = ev->used++;
793     }
794 
795     fdn->fde_ndx = k;
796     ev->pollfds[k].fd = fd;
797     ev->pollfds[k].events = events;
798 
799     return 0;
800 }
801 
802 static int
803 fdevent_poll_poll (fdevents *ev, int timeout_ms)
804 {
805     struct pollfd * const restrict pfds = ev->pollfds;
806     fdnode ** const fdarray = ev->fdarray;
807     const int n = poll(pfds, ev->used, timeout_ms);
808     for (int i = 0, m = 0; m < n; ++i) {
809         if (0 == pfds[i].revents) continue;
810         fdnode *fdn = fdarray[pfds[i].fd];
811         if (0 == ((uintptr_t)fdn & 0x3))
812             (*fdn->handler)(fdn->ctx, pfds[i].revents);
813         ++m;
814     }
815     return n;
816 }
817 
818 __attribute_cold__
819 static void
820 fdevent_poll_free (fdevents *ev)
821 {
822     free(ev->pollfds);
823     if (ev->unused.ptr) free(ev->unused.ptr);
824 }
825 
826 __attribute_cold__
827 static int
828 fdevent_poll_init (fdevents *ev)
829 {
830     force_assert(POLLIN    == FDEVENT_IN);
831     force_assert(POLLPRI   == FDEVENT_PRI);
832     force_assert(POLLOUT   == FDEVENT_OUT);
833     force_assert(POLLERR   == FDEVENT_ERR);
834     force_assert(POLLHUP   == FDEVENT_HUP);
835     force_assert(POLLNVAL  == FDEVENT_NVAL);
836   #ifdef POLLRDHUP
837     force_assert(POLLRDHUP == FDEVENT_RDHUP);
838   #endif
839 
840     ev->type      = FDEVENT_HANDLER_POLL;
841     ev->event_set = fdevent_poll_event_set;
842     ev->event_del = fdevent_poll_event_del;
843     ev->poll      = fdevent_poll_poll;
844     ev->free      = fdevent_poll_free;
845     return 0;
846 }
847 
848 #endif /* FDEVENT_USE_POLL */
849 
850 
851 #ifdef FDEVENT_USE_SELECT
852 
853 #include "sys-time.h"
854 
855 __attribute_cold__
856 static int
857 fdevent_select_reset (fdevents *ev)
858 {
859     FD_ZERO(&(ev->select_set_read));
860     FD_ZERO(&(ev->select_set_write));
861     FD_ZERO(&(ev->select_set_error));
862     ev->select_max_fd = -1;
863     return 0;
864 }
865 
866 static int
867 fdevent_select_event_del (fdevents *ev, fdnode *fdn)
868 {
869     int fd = fdn->fd;
870     FD_CLR(fd, &(ev->select_set_read));
871     FD_CLR(fd, &(ev->select_set_write));
872     FD_CLR(fd, &(ev->select_set_error));
873     return 0;
874 }
875 
876 static int
877 fdevent_select_event_set (fdevents *ev, fdnode *fdn, int events)
878 {
879     int fd = fdn->fde_ndx = fdn->fd;
880 
881     /* we should be protected by max-fds, but you never know */
882     force_assert(fd < ((int)FD_SETSIZE));
883 
884     if (events & FDEVENT_IN)
885         FD_SET(fd, &(ev->select_set_read));
886     else
887         FD_CLR(fd, &(ev->select_set_read));
888 
889     if (events & FDEVENT_OUT)
890         FD_SET(fd, &(ev->select_set_write));
891     else
892         FD_CLR(fd, &(ev->select_set_write));
893 
894     FD_SET(fd, &(ev->select_set_error));
895 
896     if (fd > ev->select_max_fd) ev->select_max_fd = fd;
897 
898     return 0;
899 }
900 
901 static int
902 fdevent_select_event_get_revent (const fdevents *ev, int ndx)
903 {
904     int revents = 0;
905     if (FD_ISSET(ndx, &ev->select_read))  revents |= FDEVENT_IN;
906     if (FD_ISSET(ndx, &ev->select_write)) revents |= FDEVENT_OUT;
907     if (FD_ISSET(ndx, &ev->select_error)) revents |= FDEVENT_ERR;
908     return revents;
909 }
910 
911 static int
912 fdevent_select_event_next_fdndx (const fdevents *ev, int ndx)
913 {
914     const int max_fd = ev->select_max_fd + 1;
915     for (int i = (ndx < 0) ? 0 : ndx + 1; i < max_fd; ++i) {
916         if (FD_ISSET(i, &(ev->select_read)))  return i;
917         if (FD_ISSET(i, &(ev->select_write))) return i;
918         if (FD_ISSET(i, &(ev->select_error))) return i;
919     }
920 
921     return -1;
922 }
923 
924 static int
925 fdevent_select_poll (fdevents *ev, int timeout_ms)
926 {
927     int n;
928     struct timeval tv;
929 
930     tv.tv_sec =  timeout_ms / 1000;
931     tv.tv_usec = (timeout_ms % 1000) * 1000;
932 
933     ev->select_read  = ev->select_set_read;
934     ev->select_write = ev->select_set_write;
935     ev->select_error = ev->select_set_error;
936 
937     n = select(ev->select_max_fd + 1,
938                &ev->select_read, &ev->select_write, &ev->select_error, &tv);
939     for (int ndx = -1, i = 0; i < n; ++i) {
940         fdnode *fdn;
941         ndx = fdevent_select_event_next_fdndx(ev, ndx);
942         if (-1 == ndx) break;
943         fdn = ev->fdarray[ndx];
944         if (0 == ((uintptr_t)fdn & 0x3)) {
945             int revents = fdevent_select_event_get_revent(ev, ndx);
946             (*fdn->handler)(fdn->ctx, revents);
947         }
948     }
949     return n;
950 }
951 
952 __attribute_cold__
953 static int fdevent_select_init (fdevents *ev)
954 {
955     ev->type      = FDEVENT_HANDLER_SELECT;
956     ev->event_set = fdevent_select_event_set;
957     ev->event_del = fdevent_select_event_del;
958     ev->poll      = fdevent_select_poll;
959     ev->reset     = fdevent_select_reset;
960     return 0;
961 }
962 
963 #endif /* FDEVENT_USE_SELECT */
964