fdevent.c revision f6330a2eeb78c0971f33feee7fd1ee06472a7dba
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
35static 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
48static 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
67static void fdevent_plist_enqueue(fdevent *node);
68static void fdevent_plist_remove(fdevent *node);
69static fdevent *fdevent_plist_dequeue(void);
70
71static fdevent list_pending = {
72    .next = &list_pending,
73    .prev = &list_pending,
74};
75
76static fdevent **fd_table = 0;
77static int fd_table_max = 0;
78
79#ifdef CRAPTASTIC
80//HAVE_EPOLL
81
82#include <sys/epoll.h>
83
84static int epoll_fd = -1;
85
86static 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
100static 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
116static 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
132static 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
178static 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
221static fd_set read_fds;
222static fd_set write_fds;
223static fd_set error_fds;
224
225static int select_n = 0;
226
227static void fdevent_init(void)
228{
229    FD_ZERO(&read_fds);
230    FD_ZERO(&write_fds);
231    FD_ZERO(&error_fds);
232}
233
234static void fdevent_connect(fdevent *fde)
235{
236    if(fde->fd >= select_n) {
237        select_n = fde->fd + 1;
238    }
239}
240
241static 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
255static 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
276static 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
318static 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
346static 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
364static 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
374static 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
382static 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
397fdevent *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
406void 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
415void 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
432void 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
449void 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
475void fdevent_add(fdevent *fde, unsigned events)
476{
477    fdevent_set(
478        fde, (fde->state & FDE_EVENTMASK) | (events & FDE_EVENTMASK));
479}
480
481void fdevent_del(fdevent *fde, unsigned events)
482{
483    fdevent_set(
484        fde, (fde->state & FDE_EVENTMASK) & (~(events & FDE_EVENTMASK)));
485}
486
487void 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