xref: /f-stack/lib/ff_epoll.c (revision 8d6a4447)
1 #include <stdio.h>
2 #include <stdint.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #include <stdarg.h>
6 #include <sched.h>
7 #include <fcntl.h>
8 #include <errno.h>
9 #include <assert.h>
10 #include <unistd.h>
11 #include <netinet/in.h>
12 #include <sys/types.h>
13 #include <sys/socket.h>
14 #include <sys/time.h>
15 #include <sys/select.h>
16 #include <sys/syscall.h>
17 #include <arpa/inet.h>
18 #include <sys/epoll.h>
19 
20 #include "ff_api.h"
21 #include "ff_errno.h"
22 
23 
24 int
ff_epoll_create(int size)25 ff_epoll_create(int size __attribute__((__unused__)))
26 {
27     return ff_kqueue();
28 }
29 
30 int
ff_epoll_ctl(int epfd,int op,int fd,struct epoll_event * event)31 ff_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
32 {
33     /*
34      * Since kqueue uses EVFILT_READ and EVFILT_WRITE filters to
35      * handle read/write events, so we need two kevents.
36      */
37     const int changes = 2;
38     struct kevent kev[changes];
39     int flags = 0;
40     int read_flags, write_flags;
41 
42     if ((!event && op != EPOLL_CTL_DEL) ||
43         (op != EPOLL_CTL_ADD &&
44          op != EPOLL_CTL_MOD &&
45          op != EPOLL_CTL_DEL)) {
46         errno = EINVAL;
47         return -1;
48     }
49 
50     /*
51      * EPOLL_CTL_DEL doesn't need to care for event->events.
52      */
53     if (op == EPOLL_CTL_DEL) {
54         EV_SET(&kev[0], fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
55         EV_SET(&kev[1], fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
56 
57         return ff_kevent(epfd, kev, changes, NULL, 0, NULL);
58     }
59 
60     /*
61      * FIXME:
62      *
63      * Kqueue doesn't have edge-triggered mode that exactly
64      * same with epoll, the most similar way is setting EV_CLEAR
65      * or EV_DISPATCH flag, but there are still some differences.
66      *
67      * EV_CLEAR:after the event is retrieved by the user,
68      *    its state is reset.
69      * EV_DISPATCH: disable the event source immediately
70      *    after delivery of an event.
71      *
72      * Here we use EV_CLEAR temporarily.
73      *
74      */
75     if (event->events & EPOLLET) {
76         flags |= EV_CLEAR;
77     }
78 
79     if (event->events & EPOLLONESHOT) {
80         flags |= EV_ONESHOT;
81     }
82 
83     if (op == EPOLL_CTL_ADD) {
84         flags |= EV_ADD;
85     }
86 
87     read_flags = write_flags = flags | EV_DISABLE;
88 
89     if (event->events & EPOLLIN) {
90         read_flags &= ~EV_DISABLE;
91         read_flags |= EV_ENABLE;
92     }
93 
94     if (event->events & EPOLLOUT) {
95         write_flags &= ~EV_DISABLE;
96         write_flags |= EV_ENABLE;
97     }
98 
99     // Fix #124: set user data
100     EV_SET(&kev[0], fd, EVFILT_READ, read_flags, 0, 0, event->data.ptr);
101     EV_SET(&kev[1], fd, EVFILT_WRITE, write_flags, 0, 0, event->data.ptr);
102 
103     return ff_kevent(epfd, kev, changes, NULL, 0, NULL);
104 }
105 
106 static void
ff_event_to_epoll(void ** ev,struct kevent * kev)107 ff_event_to_epoll(void **ev, struct kevent *kev)
108 {
109     unsigned int event_one = 0;
110     struct epoll_event **ppev = (struct epoll_event **)ev;
111 
112     if (kev->filter == EVFILT_READ) {
113         if (kev->data || !(kev->flags & EV_EOF)) {
114             event_one |= EPOLLIN;
115         }
116     } else if (kev->filter == EVFILT_WRITE) {
117         event_one |= EPOLLOUT;
118     }
119 
120     if (kev->flags & EV_ERROR) {
121         event_one |= EPOLLERR;
122     }
123 
124     if (kev->flags & EV_EOF) {
125         event_one |= EPOLLHUP;
126 
127         if (kev->fflags) {
128             event_one |= EPOLLERR;
129         }
130 
131         if (kev->filter == EVFILT_READ) {
132             event_one |= EPOLLIN;
133         } else if (kev->filter == EVFILT_WRITE) {
134             event_one |= EPOLLERR;
135         }
136     }
137 
138     (*ppev)->events   = event_one;
139     // Fix #124: get user data
140     if (kev->udata != NULL)
141         (*ppev)->data.ptr  = kev->udata;
142     else
143         (*ppev)->data.fd = kev->ident;
144     (*ppev)++;
145 }
146 
147 int
ff_epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout)148 ff_epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
149 {
150     int i, ret;
151     if (!events || maxevents < 1) {
152         errno = EINVAL;
153         return -1;
154     }
155 
156     return ff_kevent_do_each(epfd, NULL, 0, events, maxevents, NULL, ff_event_to_epoll);
157 }
158 
159