blob: c179b201d32a871531aa0c5133afa7699b6dc8ab [file] [log] [blame]
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -08001/* http://frotznet.googlecode.com/svn/trunk/utils/fdevent.c
2**
3** Copyright 2006, Brian Swetland <swetland@frotz.net>
4**
JP Abgrall0e7c4272011-02-23 18:44:39 -08005** 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
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -08008**
JP Abgrall0e7c4272011-02-23 18:44:39 -08009** http://www.apache.org/licenses/LICENSE-2.0
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080010**
JP Abgrall0e7c4272011-02-23 18:44:39 -080011** 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
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080015** 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
David 'Digit' Turner414ff7d2009-05-18 17:07:46 +020029#include "fdevent.h"
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080030
JP Abgrall0e7c4272011-02-23 18:44:39 -080031#define TRACE(x...) fprintf(stderr,x)
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080032
JP Abgrall0e7c4272011-02-23 18:44:39 -080033#define DEBUG 0
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080034
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);
David 'Digit' Turnerf6330a22009-05-18 17:36:28 +020090
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080091 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
David 'Digit' Turnerf6330a22009-05-18 17:36:28 +0200108#if 0
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800109 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;
David 'Digit' Turnerf6330a22009-05-18 17:36:28 +0200119
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800120 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;
David 'Digit' Turnerf6330a22009-05-18 17:36:28 +0200136
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800137 active = (fde->state & FDE_EVENTMASK) != 0;
David 'Digit' Turnerf6330a22009-05-18 17:36:28 +0200138
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800139 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;
David 'Digit' Turnerf6330a22009-05-18 17:36:28 +0200244
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800245 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
JP Abgrall0e7c4272011-02-23 18:44:39 -0800273 fde->state = (fde->state & FDE_STATEMASK) | events;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800274}
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));
David 'Digit' Turnerf6330a22009-05-18 17:36:28 +0200286
JP Abgrall0e7c4272011-02-23 18:44:39 -0800287 n = select(select_n, &rfd, &wfd, &efd, 0);
David 'Digit' Turnerf6330a22009-05-18 17:36:28 +0200288
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800289 if(n < 0) {
JP Abgrall0e7c4272011-02-23 18:44:39 -0800290 if(errno == EINTR) return;
291 perror("select");
292 return;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800293 }
294
295 for(i = 0; (i < select_n) && (n > 0); i++) {
296 events = 0;
JP Abgrall0e7c4272011-02-23 18:44:39 -0800297 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;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800300
301 if(events) {
JP Abgrall0e7c4272011-02-23 18:44:39 -0800302 n--;
303
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800304 fde = fd_table[i];
305 if(fde == 0) FATAL("missing fde for fd %d\n", i);
306
307 fde->events |= events;
David 'Digit' Turnerf6330a22009-05-18 17:36:28 +0200308
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800309 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 }
David 'Digit' Turnerf6330a22009-05-18 17:36:28 +0200323
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800324 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) {
JP Abgrall0e7c4272011-02-23 18:44:39 -0800353 FATAL("fd_table out of sync");
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800354 }
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;
David 'Digit' Turnerf6330a22009-05-18 17:36:28 +0200386
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800387 if(node == list) return 0;
David 'Digit' Turnerf6330a22009-05-18 17:36:28 +0200388
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800389 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
JP Abgrall0e7c4272011-02-23 18:44:39 -0800415void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg)
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800416{
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);
JP Abgrall0e7c4272011-02-23 18:44:39 -0800440 dump_fde(fde, "disconnect");
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800441 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;
David 'Digit' Turnerf6330a22009-05-18 17:36:28 +0200452
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800453 if((fde->state & FDE_EVENTMASK) == events) return;
David 'Digit' Turnerf6330a22009-05-18 17:36:28 +0200454
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800455 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;
David 'Digit' Turnerf6330a22009-05-18 17:36:28 +0200490
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800491 for(;;) {
JP Abgrall0e7c4272011-02-23 18:44:39 -0800492#if DEBUG
493 fprintf(stderr,"--- ---- waiting for events\n");
494#endif
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800495 fdevent_process();
David 'Digit' Turnerf6330a22009-05-18 17:36:28 +0200496
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800497 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}
JP Abgrall0e7c4272011-02-23 18:44:39 -0800506