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