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