• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* http://frotznet.googlecode.com/svn/trunk/utils/fdevent.c
2 **
3 ** Copyright 2006, Brian Swetland <swetland@frotz.net>
4 **
5 ** Licensed under the Apache License, Version 2.0 (the "License");
6 ** you may not use this file except in compliance with the License.
7 ** You may obtain a copy of the License at
8 **
9 **     http://www.apache.org/licenses/LICENSE-2.0
10 **
11 ** Unless required by applicable law or agreed to in writing, software
12 ** distributed under the License is distributed on an "AS IS" BASIS,
13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ** See the License for the specific language governing permissions and
15 ** limitations under the License.
16 */
17 
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include <unistd.h>
22 #include <errno.h>
23 
24 #include <fcntl.h>
25 
26 #include <stdarg.h>
27 #include <stddef.h>
28 
29 #include "fdevent.h"
30 
31 #define TRACE(x...) fprintf(stderr,x)
32 
33 #define DEBUG 0
34 
fatal(const char * fn,const char * fmt,...)35 static void fatal(const char *fn, const char *fmt, ...)
36 {
37     va_list ap;
38     va_start(ap, fmt);
39     fprintf(stderr, "%s:", fn);
40     vfprintf(stderr, fmt, ap);
41     va_end(ap);
42     abort();
43 }
44 
45 #define FATAL(x...) fatal(__FUNCTION__, x)
46 
47 #if DEBUG
dump_fde(fdevent * fde,const char * info)48 static void dump_fde(fdevent *fde, const char *info)
49 {
50     fprintf(stderr,"FDE #%03d %c%c%c %s\n", fde->fd,
51             fde->state & FDE_READ ? 'R' : ' ',
52             fde->state & FDE_WRITE ? 'W' : ' ',
53             fde->state & FDE_ERROR ? 'E' : ' ',
54             info);
55 }
56 #else
57 #define dump_fde(fde, info) do { } while(0)
58 #endif
59 
60 #define FDE_EVENTMASK  0x00ff
61 #define FDE_STATEMASK  0xff00
62 
63 #define FDE_ACTIVE     0x0100
64 #define FDE_PENDING    0x0200
65 #define FDE_CREATED    0x0400
66 
67 static void fdevent_plist_enqueue(fdevent *node);
68 static void fdevent_plist_remove(fdevent *node);
69 static fdevent *fdevent_plist_dequeue(void);
70 
71 static fdevent list_pending = {
72     .next = &list_pending,
73     .prev = &list_pending,
74 };
75 
76 static fdevent **fd_table = 0;
77 static int fd_table_max = 0;
78 
79 #ifdef CRAPTASTIC
80 //HAVE_EPOLL
81 
82 #include <sys/epoll.h>
83 
84 static int epoll_fd = -1;
85 
fdevent_init()86 static void fdevent_init()
87 {
88         /* XXX: what's a good size for the passed in hint? */
89     epoll_fd = epoll_create(256);
90 
91     if(epoll_fd < 0) {
92         perror("epoll_create() failed");
93         exit(1);
94     }
95 
96         /* mark for close-on-exec */
97     fcntl(epoll_fd, F_SETFD, FD_CLOEXEC);
98 }
99 
fdevent_connect(fdevent * fde)100 static void fdevent_connect(fdevent *fde)
101 {
102     struct epoll_event ev;
103 
104     memset(&ev, 0, sizeof(ev));
105     ev.events = 0;
106     ev.data.ptr = fde;
107 
108 #if 0
109     if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fde->fd, &ev)) {
110         perror("epoll_ctl() failed\n");
111         exit(1);
112     }
113 #endif
114 }
115 
fdevent_disconnect(fdevent * fde)116 static void fdevent_disconnect(fdevent *fde)
117 {
118     struct epoll_event ev;
119 
120     memset(&ev, 0, sizeof(ev));
121     ev.events = 0;
122     ev.data.ptr = fde;
123 
124         /* technically we only need to delete if we
125         ** were actively monitoring events, but let's
126         ** be aggressive and do it anyway, just in case
127         ** something's out of sync
128         */
129     epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fde->fd, &ev);
130 }
131 
fdevent_update(fdevent * fde,unsigned events)132 static void fdevent_update(fdevent *fde, unsigned events)
133 {
134     struct epoll_event ev;
135     int active;
136 
137     active = (fde->state & FDE_EVENTMASK) != 0;
138 
139     memset(&ev, 0, sizeof(ev));
140     ev.events = 0;
141     ev.data.ptr = fde;
142 
143     if(events & FDE_READ) ev.events |= EPOLLIN;
144     if(events & FDE_WRITE) ev.events |= EPOLLOUT;
145     if(events & FDE_ERROR) ev.events |= (EPOLLERR | EPOLLHUP);
146 
147     fde->state = (fde->state & FDE_STATEMASK) | events;
148 
149     if(active) {
150             /* we're already active. if we're changing to *no*
151             ** events being monitored, we need to delete, otherwise
152             ** we need to just modify
153             */
154         if(ev.events) {
155             if(epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fde->fd, &ev)) {
156                 perror("epoll_ctl() failed\n");
157                 exit(1);
158             }
159         } else {
160             if(epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fde->fd, &ev)) {
161                 perror("epoll_ctl() failed\n");
162                 exit(1);
163             }
164         }
165     } else {
166             /* we're not active.  if we're watching events, we need
167             ** to add, otherwise we can just do nothing
168             */
169         if(ev.events) {
170             if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fde->fd, &ev)) {
171                 perror("epoll_ctl() failed\n");
172                 exit(1);
173             }
174         }
175     }
176 }
177 
fdevent_process()178 static void fdevent_process()
179 {
180     struct epoll_event events[256];
181     fdevent *fde;
182     int i, n;
183 
184     n = epoll_wait(epoll_fd, events, 256, -1);
185 
186     if(n < 0) {
187         if(errno == EINTR) return;
188         perror("epoll_wait");
189         exit(1);
190     }
191 
192     for(i = 0; i < n; i++) {
193         struct epoll_event *ev = events + i;
194         fde = ev->data.ptr;
195 
196         if(ev->events & EPOLLIN) {
197             fde->events |= FDE_READ;
198         }
199         if(ev->events & EPOLLOUT) {
200             fde->events |= FDE_WRITE;
201         }
202         if(ev->events & (EPOLLERR | EPOLLHUP)) {
203             fde->events |= FDE_ERROR;
204         }
205         if(fde->events) {
206             if(fde->state & FDE_PENDING) continue;
207             fde->state |= FDE_PENDING;
208             fdevent_plist_enqueue(fde);
209         }
210     }
211 }
212 
213 #else /* USE_SELECT */
214 
215 #ifdef HAVE_WINSOCK
216 #include <winsock2.h>
217 #else
218 #include <sys/select.h>
219 #endif
220 
221 static fd_set read_fds;
222 static fd_set write_fds;
223 static fd_set error_fds;
224 
225 static int select_n = 0;
226 
fdevent_init(void)227 static void fdevent_init(void)
228 {
229     FD_ZERO(&read_fds);
230     FD_ZERO(&write_fds);
231     FD_ZERO(&error_fds);
232 }
233 
fdevent_connect(fdevent * fde)234 static void fdevent_connect(fdevent *fde)
235 {
236     if(fde->fd >= select_n) {
237         select_n = fde->fd + 1;
238     }
239 }
240 
fdevent_disconnect(fdevent * fde)241 static void fdevent_disconnect(fdevent *fde)
242 {
243     int i, n;
244 
245     FD_CLR(fde->fd, &read_fds);
246     FD_CLR(fde->fd, &write_fds);
247     FD_CLR(fde->fd, &error_fds);
248 
249     for(n = 0, i = 0; i < select_n; i++) {
250         if(fd_table[i] != 0) n = i;
251     }
252     select_n = n + 1;
253 }
254 
fdevent_update(fdevent * fde,unsigned events)255 static void fdevent_update(fdevent *fde, unsigned events)
256 {
257     if(events & FDE_READ) {
258         FD_SET(fde->fd, &read_fds);
259     } else {
260         FD_CLR(fde->fd, &read_fds);
261     }
262     if(events & FDE_WRITE) {
263         FD_SET(fde->fd, &write_fds);
264     } else {
265         FD_CLR(fde->fd, &write_fds);
266     }
267     if(events & FDE_ERROR) {
268         FD_SET(fde->fd, &error_fds);
269     } else {
270         FD_CLR(fde->fd, &error_fds);
271     }
272 
273     fde->state = (fde->state & FDE_STATEMASK) | events;
274 }
275 
fdevent_process()276 static void fdevent_process()
277 {
278     int i, n;
279     fdevent *fde;
280     unsigned events;
281     fd_set rfd, wfd, efd;
282 
283     memcpy(&rfd, &read_fds, sizeof(fd_set));
284     memcpy(&wfd, &write_fds, sizeof(fd_set));
285     memcpy(&efd, &error_fds, sizeof(fd_set));
286 
287     n = select(select_n, &rfd, &wfd, &efd, 0);
288 
289     if(n < 0) {
290         if(errno == EINTR) return;
291         perror("select");
292         return;
293     }
294 
295     for(i = 0; (i < select_n) && (n > 0); i++) {
296         events = 0;
297         if(FD_ISSET(i, &rfd)) events |= FDE_READ;
298         if(FD_ISSET(i, &wfd)) events |= FDE_WRITE;
299         if(FD_ISSET(i, &efd)) events |= FDE_ERROR;
300 
301         if(events) {
302             n--;
303 
304             fde = fd_table[i];
305             if(fde == 0) FATAL("missing fde for fd %d\n", i);
306 
307             fde->events |= events;
308 
309             if(fde->state & FDE_PENDING) continue;
310             fde->state |= FDE_PENDING;
311             fdevent_plist_enqueue(fde);
312         }
313     }
314 }
315 
316 #endif
317 
fdevent_register(fdevent * fde)318 static void fdevent_register(fdevent *fde)
319 {
320     if(fde->fd < 0) {
321         FATAL("bogus negative fd (%d)\n", fde->fd);
322     }
323 
324     if(fde->fd >= fd_table_max) {
325         int oldmax = fd_table_max;
326         if(fde->fd > 32000) {
327             FATAL("bogus huuuuge fd (%d)\n", fde->fd);
328         }
329         if(fd_table_max == 0) {
330             fdevent_init();
331             fd_table_max = 256;
332         }
333         while(fd_table_max <= fde->fd) {
334             fd_table_max *= 2;
335         }
336         fd_table = realloc(fd_table, sizeof(fdevent*) * fd_table_max);
337         if(fd_table == 0) {
338             FATAL("could not expand fd_table to %d entries\n", fd_table_max);
339         }
340         memset(fd_table + oldmax, 0, sizeof(int) * (fd_table_max - oldmax));
341     }
342 
343     fd_table[fde->fd] = fde;
344 }
345 
fdevent_unregister(fdevent * fde)346 static void fdevent_unregister(fdevent *fde)
347 {
348     if((fde->fd < 0) || (fde->fd >= fd_table_max)) {
349         FATAL("fd out of range (%d)\n", fde->fd);
350     }
351 
352     if(fd_table[fde->fd] != fde) {
353         FATAL("fd_table out of sync");
354     }
355 
356     fd_table[fde->fd] = 0;
357 
358     if(!(fde->state & FDE_DONT_CLOSE)) {
359         dump_fde(fde, "close");
360         close(fde->fd);
361     }
362 }
363 
fdevent_plist_enqueue(fdevent * node)364 static void fdevent_plist_enqueue(fdevent *node)
365 {
366     fdevent *list = &list_pending;
367 
368     node->next = list;
369     node->prev = list->prev;
370     node->prev->next = node;
371     list->prev = node;
372 }
373 
fdevent_plist_remove(fdevent * node)374 static void fdevent_plist_remove(fdevent *node)
375 {
376     node->prev->next = node->next;
377     node->next->prev = node->prev;
378     node->next = 0;
379     node->prev = 0;
380 }
381 
fdevent_plist_dequeue(void)382 static fdevent *fdevent_plist_dequeue(void)
383 {
384     fdevent *list = &list_pending;
385     fdevent *node = list->next;
386 
387     if(node == list) return 0;
388 
389     list->next = node->next;
390     list->next->prev = list;
391     node->next = 0;
392     node->prev = 0;
393 
394     return node;
395 }
396 
fdevent_create(int fd,fd_func func,void * arg)397 fdevent *fdevent_create(int fd, fd_func func, void *arg)
398 {
399     fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
400     if(fde == 0) return 0;
401     fdevent_install(fde, fd, func, arg);
402     fde->state |= FDE_CREATED;
403     return fde;
404 }
405 
fdevent_destroy(fdevent * fde)406 void fdevent_destroy(fdevent *fde)
407 {
408     if(fde == 0) return;
409     if(!(fde->state & FDE_CREATED)) {
410         FATAL("fde %p not created by fdevent_create()\n", fde);
411     }
412     fdevent_remove(fde);
413 }
414 
fdevent_install(fdevent * fde,int fd,fd_func func,void * arg)415 void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg)
416 {
417     memset(fde, 0, sizeof(fdevent));
418     fde->state = FDE_ACTIVE;
419     fde->fd = fd;
420     fde->func = func;
421     fde->arg = arg;
422 
423 #ifndef HAVE_WINSOCK
424     fcntl(fd, F_SETFL, O_NONBLOCK);
425 #endif
426     fdevent_register(fde);
427     dump_fde(fde, "connect");
428     fdevent_connect(fde);
429     fde->state |= FDE_ACTIVE;
430 }
431 
fdevent_remove(fdevent * fde)432 void fdevent_remove(fdevent *fde)
433 {
434     if(fde->state & FDE_PENDING) {
435         fdevent_plist_remove(fde);
436     }
437 
438     if(fde->state & FDE_ACTIVE) {
439         fdevent_disconnect(fde);
440         dump_fde(fde, "disconnect");
441         fdevent_unregister(fde);
442     }
443 
444     fde->state = 0;
445     fde->events = 0;
446 }
447 
448 
fdevent_set(fdevent * fde,unsigned events)449 void fdevent_set(fdevent *fde, unsigned events)
450 {
451     events &= FDE_EVENTMASK;
452 
453     if((fde->state & FDE_EVENTMASK) == events) return;
454 
455     if(fde->state & FDE_ACTIVE) {
456         fdevent_update(fde, events);
457         dump_fde(fde, "update");
458     }
459 
460     fde->state = (fde->state & FDE_STATEMASK) | events;
461 
462     if(fde->state & FDE_PENDING) {
463             /* if we're pending, make sure
464             ** we don't signal an event that
465             ** is no longer wanted.
466             */
467         fde->events &= (~events);
468         if(fde->events == 0) {
469             fdevent_plist_remove(fde);
470             fde->state &= (~FDE_PENDING);
471         }
472     }
473 }
474 
fdevent_add(fdevent * fde,unsigned events)475 void fdevent_add(fdevent *fde, unsigned events)
476 {
477     fdevent_set(
478         fde, (fde->state & FDE_EVENTMASK) | (events & FDE_EVENTMASK));
479 }
480 
fdevent_del(fdevent * fde,unsigned events)481 void fdevent_del(fdevent *fde, unsigned events)
482 {
483     fdevent_set(
484         fde, (fde->state & FDE_EVENTMASK) & (~(events & FDE_EVENTMASK)));
485 }
486 
fdevent_loop()487 void fdevent_loop()
488 {
489     fdevent *fde;
490 
491     for(;;) {
492 #if DEBUG
493         fprintf(stderr,"--- ---- waiting for events\n");
494 #endif
495         fdevent_process();
496 
497         while((fde = fdevent_plist_dequeue())) {
498             unsigned events = fde->events;
499             fde->events = 0;
500             fde->state &= (~FDE_PENDING);
501             dump_fde(fde, "callback");
502             fde->func(fde->fd, events, fde->arg);
503         }
504     }
505 }
506 
507