blob: a81b2df0df68cc2171ce5af9c5a9694f825a77b9 [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 Abgrall69c5c4c2011-02-18 14:16:59 -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 Abgrall69c5c4c2011-02-18 14:16:59 -08009** http://www.apache.org/licenses/LICENSE-2.0
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080010**
JP Abgrall69c5c4c2011-02-18 14:16:59 -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
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080031
JP Abgrall69c5c4c2011-02-18 14:16:59 -080032/* !!! Do not enable DEBUG for the adb that will run as the server:
33** both stdout and stderr are used to communicate between the client
34** and server. Any extra output will cause failures.
35*/
36#define DEBUG 0 /* non-0 will break adb server */
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080037
38static void fatal(const char *fn, const char *fmt, ...)
39{
40 va_list ap;
41 va_start(ap, fmt);
42 fprintf(stderr, "%s:", fn);
43 vfprintf(stderr, fmt, ap);
44 va_end(ap);
45 abort();
46}
47
48#define FATAL(x...) fatal(__FUNCTION__, x)
49
50#if DEBUG
JP Abgrall69c5c4c2011-02-18 14:16:59 -080051#define D(...) \
52 do { \
53 fprintf(stderr, "%s::%s():", __FILE__, __FUNCTION__); \
54 fprintf(stderr, __VA_ARGS__); \
55 } while(0)
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080056static void dump_fde(fdevent *fde, const char *info)
57{
58 fprintf(stderr,"FDE #%03d %c%c%c %s\n", fde->fd,
59 fde->state & FDE_READ ? 'R' : ' ',
60 fde->state & FDE_WRITE ? 'W' : ' ',
61 fde->state & FDE_ERROR ? 'E' : ' ',
62 info);
63}
64#else
JP Abgrall69c5c4c2011-02-18 14:16:59 -080065#define D(...) ((void)0)
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080066#define dump_fde(fde, info) do { } while(0)
67#endif
68
69#define FDE_EVENTMASK 0x00ff
70#define FDE_STATEMASK 0xff00
71
72#define FDE_ACTIVE 0x0100
73#define FDE_PENDING 0x0200
74#define FDE_CREATED 0x0400
75
76static void fdevent_plist_enqueue(fdevent *node);
77static void fdevent_plist_remove(fdevent *node);
78static fdevent *fdevent_plist_dequeue(void);
79
80static fdevent list_pending = {
81 .next = &list_pending,
82 .prev = &list_pending,
83};
84
85static fdevent **fd_table = 0;
86static int fd_table_max = 0;
87
88#ifdef CRAPTASTIC
89//HAVE_EPOLL
90
91#include <sys/epoll.h>
92
93static int epoll_fd = -1;
94
95static void fdevent_init()
96{
97 /* XXX: what's a good size for the passed in hint? */
98 epoll_fd = epoll_create(256);
David 'Digit' Turnerf6330a22009-05-18 17:36:28 +020099
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800100 if(epoll_fd < 0) {
101 perror("epoll_create() failed");
102 exit(1);
103 }
104
105 /* mark for close-on-exec */
106 fcntl(epoll_fd, F_SETFD, FD_CLOEXEC);
107}
108
109static void fdevent_connect(fdevent *fde)
110{
111 struct epoll_event ev;
112
113 memset(&ev, 0, sizeof(ev));
114 ev.events = 0;
115 ev.data.ptr = fde;
116
David 'Digit' Turnerf6330a22009-05-18 17:36:28 +0200117#if 0
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800118 if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fde->fd, &ev)) {
119 perror("epoll_ctl() failed\n");
120 exit(1);
121 }
122#endif
123}
124
125static void fdevent_disconnect(fdevent *fde)
126{
127 struct epoll_event ev;
David 'Digit' Turnerf6330a22009-05-18 17:36:28 +0200128
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800129 memset(&ev, 0, sizeof(ev));
130 ev.events = 0;
131 ev.data.ptr = fde;
132
133 /* technically we only need to delete if we
134 ** were actively monitoring events, but let's
135 ** be aggressive and do it anyway, just in case
136 ** something's out of sync
137 */
138 epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fde->fd, &ev);
139}
140
141static void fdevent_update(fdevent *fde, unsigned events)
142{
143 struct epoll_event ev;
144 int active;
David 'Digit' Turnerf6330a22009-05-18 17:36:28 +0200145
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800146 active = (fde->state & FDE_EVENTMASK) != 0;
David 'Digit' Turnerf6330a22009-05-18 17:36:28 +0200147
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800148 memset(&ev, 0, sizeof(ev));
149 ev.events = 0;
150 ev.data.ptr = fde;
151
152 if(events & FDE_READ) ev.events |= EPOLLIN;
153 if(events & FDE_WRITE) ev.events |= EPOLLOUT;
154 if(events & FDE_ERROR) ev.events |= (EPOLLERR | EPOLLHUP);
155
156 fde->state = (fde->state & FDE_STATEMASK) | events;
157
158 if(active) {
159 /* we're already active. if we're changing to *no*
160 ** events being monitored, we need to delete, otherwise
161 ** we need to just modify
162 */
163 if(ev.events) {
164 if(epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fde->fd, &ev)) {
165 perror("epoll_ctl() failed\n");
166 exit(1);
167 }
168 } else {
169 if(epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fde->fd, &ev)) {
170 perror("epoll_ctl() failed\n");
171 exit(1);
172 }
173 }
174 } else {
175 /* we're not active. if we're watching events, we need
176 ** to add, otherwise we can just do nothing
177 */
178 if(ev.events) {
179 if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fde->fd, &ev)) {
180 perror("epoll_ctl() failed\n");
181 exit(1);
182 }
183 }
184 }
185}
186
187static void fdevent_process()
188{
189 struct epoll_event events[256];
190 fdevent *fde;
191 int i, n;
192
193 n = epoll_wait(epoll_fd, events, 256, -1);
194
195 if(n < 0) {
196 if(errno == EINTR) return;
197 perror("epoll_wait");
198 exit(1);
199 }
200
201 for(i = 0; i < n; i++) {
202 struct epoll_event *ev = events + i;
203 fde = ev->data.ptr;
204
205 if(ev->events & EPOLLIN) {
206 fde->events |= FDE_READ;
207 }
208 if(ev->events & EPOLLOUT) {
209 fde->events |= FDE_WRITE;
210 }
211 if(ev->events & (EPOLLERR | EPOLLHUP)) {
212 fde->events |= FDE_ERROR;
213 }
214 if(fde->events) {
215 if(fde->state & FDE_PENDING) continue;
216 fde->state |= FDE_PENDING;
217 fdevent_plist_enqueue(fde);
218 }
219 }
220}
221
222#else /* USE_SELECT */
223
224#ifdef HAVE_WINSOCK
225#include <winsock2.h>
226#else
227#include <sys/select.h>
228#endif
229
230static fd_set read_fds;
231static fd_set write_fds;
232static fd_set error_fds;
233
234static int select_n = 0;
235
236static void fdevent_init(void)
237{
238 FD_ZERO(&read_fds);
239 FD_ZERO(&write_fds);
240 FD_ZERO(&error_fds);
241}
242
243static void fdevent_connect(fdevent *fde)
244{
245 if(fde->fd >= select_n) {
246 select_n = fde->fd + 1;
247 }
248}
249
250static void fdevent_disconnect(fdevent *fde)
251{
252 int i, n;
David 'Digit' Turnerf6330a22009-05-18 17:36:28 +0200253
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800254 FD_CLR(fde->fd, &read_fds);
255 FD_CLR(fde->fd, &write_fds);
256 FD_CLR(fde->fd, &error_fds);
257
258 for(n = 0, i = 0; i < select_n; i++) {
259 if(fd_table[i] != 0) n = i;
260 }
261 select_n = n + 1;
262}
263
264static void fdevent_update(fdevent *fde, unsigned events)
265{
266 if(events & FDE_READ) {
267 FD_SET(fde->fd, &read_fds);
268 } else {
269 FD_CLR(fde->fd, &read_fds);
270 }
271 if(events & FDE_WRITE) {
272 FD_SET(fde->fd, &write_fds);
273 } else {
274 FD_CLR(fde->fd, &write_fds);
275 }
276 if(events & FDE_ERROR) {
277 FD_SET(fde->fd, &error_fds);
278 } else {
279 FD_CLR(fde->fd, &error_fds);
280 }
281
JP Abgrall69c5c4c2011-02-18 14:16:59 -0800282 fde->state = (fde->state & FDE_STATEMASK) | events;
283}
284
285#if DEBUG
286static void dump_all_fds(const char *extra_msg)
287{
288int i;
289 fdevent *fde;
290 // per fd: 4 digits (but really: log10(FD_SETSIZE)), 1 staus, 1 blank
291 char msg_buff[FD_SETSIZE*6 + 1], *pb=msg_buff;
292 size_t max_chars = FD_SETSIZE * 6 + 1;
293 int printed_out;
294#define SAFE_SPRINTF(...) \
295 do { \
296 printed_out = snprintf(pb, max_chars, __VA_ARGS__); \
297 if (printed_out <= 0) { \
298 D("... snprintf failed.\n"); \
299 return; \
300 } \
301 if (max_chars < (unsigned int)printed_out) { \
302 D("... snprintf out of space.\n"); \
303 return; \
304 } \
305 pb += printed_out; \
306 max_chars -= printed_out; \
307 } while(0)
308
309 for(i = 0; i < select_n; i++) {
310 fde = fd_table[i];
311 SAFE_SPRINTF("%d", i);
312 if(fde == 0) {
313 SAFE_SPRINTF("? ");
314 continue;
315 }
316 if(fcntl(i, F_GETFL, NULL) < 0) {
317 SAFE_SPRINTF("b");
318 }
319 SAFE_SPRINTF(" ");
320 }
321 D("%s fd_table[]->fd = {%s}\n", extra_msg, msg_buff);
322}
323#endif
324
325/* Looks at fd_table[] for bad FDs and sets bit in fds.
326** Returns the number of bad FDs.
327*/
328static int fdevent_fd_check(fd_set *fds)
329{
330 int i, n = 0;
331 fdevent *fde;
332
333 for(i = 0; i < select_n; i++) {
334 fde = fd_table[i];
335 if(fde == 0) continue;
336 if(fcntl(i, F_GETFL, NULL) < 0) {
337 FD_SET(i, fds);
338 n++;
339 fde->state |= FDE_DONT_CLOSE;
340
341 }
342 }
343 return n;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800344}
345
346static void fdevent_process()
347{
348 int i, n;
349 fdevent *fde;
350 unsigned events;
351 fd_set rfd, wfd, efd;
JP Abgrall69c5c4c2011-02-18 14:16:59 -0800352 struct timeval tv = { /*tv_sec=*/5, /*tv_usec=*/0 };
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800353
354 memcpy(&rfd, &read_fds, sizeof(fd_set));
355 memcpy(&wfd, &write_fds, sizeof(fd_set));
356 memcpy(&efd, &error_fds, sizeof(fd_set));
David 'Digit' Turnerf6330a22009-05-18 17:36:28 +0200357
JP Abgrall69c5c4c2011-02-18 14:16:59 -0800358 n = select(select_n, &rfd, &wfd, &efd, &tv);
359
360 D("select() returned n=%d, errno=%d\n", n, n<0?errno:0);
361
362#if DEBUG
363 dump_all_fds("post select()");
364#endif
David 'Digit' Turnerf6330a22009-05-18 17:36:28 +0200365
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800366 if(n < 0) {
JP Abgrall69c5c4c2011-02-18 14:16:59 -0800367 switch(errno) {
368 case EINTR: return;
369 case EBADF:
370 // Can't trust the FD sets after an error.
371 FD_ZERO(&wfd);
372 FD_ZERO(&efd);
373 FD_ZERO(&rfd);
374 break;
375 default:
376 D("Unexpected select() error=%d\n", errno);
377 return;
378 }
379 }
380 if(n <= 0) {
381 // We fake a read, as the rest of the code assumes
382 // that errors will be detected at that point.
383 n = fdevent_fd_check(&rfd);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800384 }
385
386 for(i = 0; (i < select_n) && (n > 0); i++) {
387 events = 0;
JP Abgrall69c5c4c2011-02-18 14:16:59 -0800388 if(FD_ISSET(i, &rfd)) { events |= FDE_READ; n--; }
389 if(FD_ISSET(i, &wfd)) { events |= FDE_WRITE; n--; }
390 if(FD_ISSET(i, &efd)) { events |= FDE_ERROR; n--; }
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800391
392 if(events) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800393 fde = fd_table[i];
394 if(fde == 0) FATAL("missing fde for fd %d\n", i);
395
396 fde->events |= events;
David 'Digit' Turnerf6330a22009-05-18 17:36:28 +0200397
JP Abgrall69c5c4c2011-02-18 14:16:59 -0800398 D("got events fde->fd=%d events=%04x, state=%04x\n",
399 fde->fd, fde->events, fde->state);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800400 if(fde->state & FDE_PENDING) continue;
401 fde->state |= FDE_PENDING;
402 fdevent_plist_enqueue(fde);
403 }
404 }
405}
406
407#endif
408
409static void fdevent_register(fdevent *fde)
410{
411 if(fde->fd < 0) {
412 FATAL("bogus negative fd (%d)\n", fde->fd);
413 }
David 'Digit' Turnerf6330a22009-05-18 17:36:28 +0200414
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800415 if(fde->fd >= fd_table_max) {
416 int oldmax = fd_table_max;
417 if(fde->fd > 32000) {
418 FATAL("bogus huuuuge fd (%d)\n", fde->fd);
419 }
420 if(fd_table_max == 0) {
421 fdevent_init();
422 fd_table_max = 256;
423 }
424 while(fd_table_max <= fde->fd) {
425 fd_table_max *= 2;
426 }
427 fd_table = realloc(fd_table, sizeof(fdevent*) * fd_table_max);
428 if(fd_table == 0) {
429 FATAL("could not expand fd_table to %d entries\n", fd_table_max);
430 }
431 memset(fd_table + oldmax, 0, sizeof(int) * (fd_table_max - oldmax));
432 }
433
434 fd_table[fde->fd] = fde;
435}
436
437static void fdevent_unregister(fdevent *fde)
438{
439 if((fde->fd < 0) || (fde->fd >= fd_table_max)) {
440 FATAL("fd out of range (%d)\n", fde->fd);
441 }
442
443 if(fd_table[fde->fd] != fde) {
JP Abgrall69c5c4c2011-02-18 14:16:59 -0800444 FATAL("fd_table out of sync [%d]\n", fde->fd);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800445 }
446
447 fd_table[fde->fd] = 0;
448
449 if(!(fde->state & FDE_DONT_CLOSE)) {
450 dump_fde(fde, "close");
451 close(fde->fd);
452 }
453}
454
455static void fdevent_plist_enqueue(fdevent *node)
456{
457 fdevent *list = &list_pending;
458
459 node->next = list;
460 node->prev = list->prev;
461 node->prev->next = node;
462 list->prev = node;
463}
464
465static void fdevent_plist_remove(fdevent *node)
466{
467 node->prev->next = node->next;
468 node->next->prev = node->prev;
469 node->next = 0;
470 node->prev = 0;
471}
472
473static fdevent *fdevent_plist_dequeue(void)
474{
475 fdevent *list = &list_pending;
476 fdevent *node = list->next;
David 'Digit' Turnerf6330a22009-05-18 17:36:28 +0200477
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800478 if(node == list) return 0;
David 'Digit' Turnerf6330a22009-05-18 17:36:28 +0200479
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800480 list->next = node->next;
481 list->next->prev = list;
482 node->next = 0;
483 node->prev = 0;
484
485 return node;
486}
487
488fdevent *fdevent_create(int fd, fd_func func, void *arg)
489{
490 fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
491 if(fde == 0) return 0;
492 fdevent_install(fde, fd, func, arg);
493 fde->state |= FDE_CREATED;
494 return fde;
495}
496
497void fdevent_destroy(fdevent *fde)
498{
499 if(fde == 0) return;
500 if(!(fde->state & FDE_CREATED)) {
501 FATAL("fde %p not created by fdevent_create()\n", fde);
502 }
503 fdevent_remove(fde);
504}
505
JP Abgrall69c5c4c2011-02-18 14:16:59 -0800506void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg)
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800507{
508 memset(fde, 0, sizeof(fdevent));
509 fde->state = FDE_ACTIVE;
510 fde->fd = fd;
511 fde->func = func;
512 fde->arg = arg;
513
514#ifndef HAVE_WINSOCK
515 fcntl(fd, F_SETFL, O_NONBLOCK);
516#endif
517 fdevent_register(fde);
518 dump_fde(fde, "connect");
519 fdevent_connect(fde);
520 fde->state |= FDE_ACTIVE;
521}
522
523void fdevent_remove(fdevent *fde)
524{
525 if(fde->state & FDE_PENDING) {
526 fdevent_plist_remove(fde);
527 }
528
529 if(fde->state & FDE_ACTIVE) {
530 fdevent_disconnect(fde);
JP Abgrall69c5c4c2011-02-18 14:16:59 -0800531 dump_fde(fde, "disconnect");
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800532 fdevent_unregister(fde);
533 }
534
535 fde->state = 0;
536 fde->events = 0;
537}
538
539
540void fdevent_set(fdevent *fde, unsigned events)
541{
542 events &= FDE_EVENTMASK;
David 'Digit' Turnerf6330a22009-05-18 17:36:28 +0200543
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800544 if((fde->state & FDE_EVENTMASK) == events) return;
David 'Digit' Turnerf6330a22009-05-18 17:36:28 +0200545
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800546 if(fde->state & FDE_ACTIVE) {
547 fdevent_update(fde, events);
548 dump_fde(fde, "update");
549 }
550
551 fde->state = (fde->state & FDE_STATEMASK) | events;
552
553 if(fde->state & FDE_PENDING) {
554 /* if we're pending, make sure
555 ** we don't signal an event that
556 ** is no longer wanted.
557 */
558 fde->events &= (~events);
559 if(fde->events == 0) {
560 fdevent_plist_remove(fde);
561 fde->state &= (~FDE_PENDING);
562 }
563 }
564}
565
566void fdevent_add(fdevent *fde, unsigned events)
567{
568 fdevent_set(
569 fde, (fde->state & FDE_EVENTMASK) | (events & FDE_EVENTMASK));
570}
571
572void fdevent_del(fdevent *fde, unsigned events)
573{
574 fdevent_set(
575 fde, (fde->state & FDE_EVENTMASK) & (~(events & FDE_EVENTMASK)));
576}
577
578void fdevent_loop()
579{
580 fdevent *fde;
David 'Digit' Turnerf6330a22009-05-18 17:36:28 +0200581
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800582 for(;;) {
JP Abgrall69c5c4c2011-02-18 14:16:59 -0800583 D("--- ---- waiting for events\n");
584
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800585 fdevent_process();
David 'Digit' Turnerf6330a22009-05-18 17:36:28 +0200586
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800587 while((fde = fdevent_plist_dequeue())) {
588 unsigned events = fde->events;
589 fde->events = 0;
590 fde->state &= (~FDE_PENDING);
591 dump_fde(fde, "callback");
592 fde->func(fde->fd, events, fde->arg);
593 }
594 }
595}