diff options
author | Anas Nashif <anas.nashif@intel.com> | 2012-12-07 02:53:31 -0800 |
---|---|---|
committer | Anas Nashif <anas.nashif@intel.com> | 2012-12-07 02:53:31 -0800 |
commit | cbb6286cb92020dd7ae88798ed831ed76fd2130e (patch) | |
tree | 782a01c00d5e064aa67ea3f9241a8ef1de1060c6 /select.c | |
download | links-cbb6286cb92020dd7ae88798ed831ed76fd2130e.tar.gz links-cbb6286cb92020dd7ae88798ed831ed76fd2130e.tar.bz2 links-cbb6286cb92020dd7ae88798ed831ed76fd2130e.zip |
Imported Upstream version 2.6upstream/2.6upstream
Diffstat (limited to 'select.c')
-rw-r--r-- | select.c | 555 |
1 files changed, 555 insertions, 0 deletions
diff --git a/select.c b/select.c new file mode 100644 index 0000000..0e2b245 --- /dev/null +++ b/select.c @@ -0,0 +1,555 @@ +/* select.c + * Select Loop + * (c) 2002 Mikulas Patocka + * This file is a part of the Links program, released under GPL. + */ + +#include "links.h" + +/* +#define DEBUG_CALLS +*/ + +struct thread { + void (*read_func)(void *); + void (*write_func)(void *); + void (*error_func)(void *); + void *data; +}; + +static struct thread threads[FD_SETSIZE]; + +static fd_set w_read; +static fd_set w_write; +static fd_set w_error; + +static fd_set x_read; +static fd_set x_write; +static fd_set x_error; + +static int w_max; + +static int timer_id = 0; + +struct timer { + struct timer *next; + struct timer *prev; + ttime interval; + void (*func)(void *); + void *data; + int id; +}; + +static struct list_head timers = {&timers, &timers}; + + +ttime get_time(void) +{ + struct timeval tv; + int rs; + EINTRLOOP(rs, gettimeofday(&tv, NULL)); + return (uttime)tv.tv_sec * 1000 + tv.tv_usec / 1000; +} + +int can_write(int fd) +{ + fd_set fds; + struct timeval tv = {0, 0}; + int rs; + FD_ZERO(&fds); + if (fd < 0) + internal("can_write: handle %d", fd); + if (fd >= (int)FD_SETSIZE) { + error("too big handle %d", fd); + fatal_tty_exit(); + exit(RET_FATAL); + } + FD_SET(fd, &fds); + EINTRLOOP(rs, select(fd + 1, NULL, &fds, NULL, &tv)); + return rs; +} + +int can_read(int fd) +{ + fd_set fds; + struct timeval tv = {0, 0}; + int rs; + FD_ZERO(&fds); + if (fd < 0) + internal("can_read: handle %d", fd); + if (fd >= (int)FD_SETSIZE) { + error("too big handle %d", fd); + fatal_tty_exit(); + exit(RET_FATAL); + } + FD_SET(fd, &fds); + EINTRLOOP(rs, select(fd + 1, &fds, NULL, NULL, &tv)); + return rs; +} + +unsigned long select_info(int type) +{ + int i = 0, j; + struct cache_entry *ce; + switch (type) { + case CI_FILES: + for (j = 0; j < (int)FD_SETSIZE; j++) + if (threads[j].read_func || threads[j].write_func || threads[j].error_func) i++; + return i; + case CI_TIMERS: + foreach(ce, timers) i++; + return i; + default: + internal("select_info_info: bad request"); + } + return 0; +} + +struct bottom_half { + struct bottom_half *next; + struct bottom_half *prev; + void (*fn)(void *); + void *data; +}; + +static struct list_head bottom_halves = { &bottom_halves, &bottom_halves }; + +int register_bottom_half(void (*fn)(void *), void *data) +{ + struct bottom_half *bh; + foreach(bh, bottom_halves) if (bh->fn == fn && bh->data == data) return 0; + bh = mem_alloc(sizeof(struct bottom_half)); + bh->fn = fn; + bh->data = data; + add_to_list(bottom_halves, bh); + return 0; +} + +void unregister_bottom_half(void (*fn)(void *), void *data) +{ + struct bottom_half *bh; + retry: + foreach(bh, bottom_halves) if (bh->fn == fn && bh->data == data) { + del_from_list(bh); + mem_free(bh); + goto retry; + } +} + +void check_bottom_halves(void) +{ + struct bottom_half *bh; + void (*fn)(void *); + void *data; + rep: + if (list_empty(bottom_halves)) return; + bh = bottom_halves.prev; + fn = bh->fn; + data = bh->data; + del_from_list(bh); + mem_free(bh); +#ifdef DEBUG_CALLS + fprintf(stderr, "call: bh %p\n", fn); +#endif + pr(fn(data)) {free_list(bottom_halves); return;}; +#ifdef DEBUG_CALLS + fprintf(stderr, "bh done\n"); +#endif + goto rep; +} + +#define CHK_BH if (!list_empty(bottom_halves)) check_bottom_halves() + +static ttime last_time; + +static void check_timers(void) +{ + ttime interval = (uttime)get_time() - (uttime)last_time; + struct timer * volatile t; /* volatile because of setjmp */ + foreach(t, timers) t->interval -= (uttime)interval; + /*ch:*/ + foreach(t, timers) if (t->interval <= 0) { + struct timer *tt; +#ifdef DEBUG_CALLS + fprintf(stderr, "call: timer %p\n", t->func); +#endif + pr(t->func(t->data)) { + del_from_list((struct timer *)timers.next); + return; + } +#ifdef DEBUG_CALLS + fprintf(stderr, "timer done\n"); +#endif + CHK_BH; + tt = t->prev; + del_from_list(t); + mem_free(t); + t = tt; + } else break; + last_time += (uttime)interval; +} + +int install_timer(ttime t, void (*func)(void *), void *data) +{ + struct timer *tm, *tt; + tm = mem_alloc(sizeof(struct timer)); + tm->interval = t; + tm->func = func; + tm->data = data; + new_id: + tm->id = timer_id; + timer_id++; + if (timer_id == MAXINT) timer_id = 0; + foreach(tt, timers) if (tt->id == tm->id) goto new_id; + foreach(tt, timers) if (tt->interval >= t) break; + add_at_pos(tt->prev, tm); + return tm->id; +} + +void kill_timer(int id) +{ + struct timer *tm; + int k = 0; + foreach(tm, timers) if (tm->id == id) { + struct timer *tt = tm; + del_from_list(tm); + tm = tm->prev; + mem_free(tt); + k++; + } + if (!k) internal("trying to kill nonexisting timer"); + if (k >= 2) internal("more timers with same id"); +} + +void *get_handler(int fd, int tp) +{ + if (fd < 0) + internal("get_handler: handle %d", fd); + if (fd >= (int)FD_SETSIZE) { + error("too big handle %d", fd); + fatal_tty_exit(); + exit(RET_FATAL); + return NULL; + } + switch (tp) { + case H_READ: return threads[fd].read_func; + case H_WRITE: return threads[fd].write_func; + case H_ERROR: return threads[fd].error_func; + case H_DATA: return threads[fd].data; + } + internal("get_handler: bad type %d", tp); + return NULL; +} + +void set_handlers(int fd, void (*read_func)(void *), void (*write_func)(void *), void (*error_func)(void *), void *data) +{ + if (fd < 0) + internal("set_handlers: handle %d", fd); + if (fd >= (int)FD_SETSIZE) { + error("too big handle %d", fd); + fatal_tty_exit(); + exit(RET_FATAL); + return; + } + threads[fd].read_func = read_func; + threads[fd].write_func = write_func; + threads[fd].error_func = error_func; + threads[fd].data = data; + if (read_func) FD_SET(fd, &w_read); + else { + FD_CLR(fd, &w_read); + FD_CLR(fd, &x_read); + } + if (write_func) FD_SET(fd, &w_write); + else { + FD_CLR(fd, &w_write); + FD_CLR(fd, &x_write); + } + if (error_func) FD_SET(fd, &w_error); + else { + FD_CLR(fd, &w_error); + FD_CLR(fd, &x_error); + } + if (read_func || write_func || error_func) { + if (fd >= w_max) w_max = fd + 1; + } else if (fd == w_max - 1) { + int i; + for (i = fd - 1; i >= 0; i--) + if (FD_ISSET(i, &w_read) || FD_ISSET(i, &w_write) || + FD_ISSET(i, &w_error)) break; + w_max = i + 1; + } +} + +#define NUM_SIGNALS 32 + +struct signal_handler { + void (*fn)(void *); + void *data; + int critical; +}; + +static int signal_mask[NUM_SIGNALS]; +static struct signal_handler signal_handlers[NUM_SIGNALS]; + +static int signal_pipe[2]; + +static void signal_break(void *data) +{ + unsigned char c; + while (can_read(signal_pipe[0])) { + int rd; + EINTRLOOP(rd, read(signal_pipe[0], &c, 1)); + if (rd != 1) break; + } +} + +SIGNAL_HANDLER static void got_signal(int sig) +{ + int sv_errno = errno; + /*fprintf(stderr, "ERROR: signal number: %d\n", sig);*/ + if (sig >= NUM_SIGNALS || sig < 0) { + /*error("ERROR: bad signal number: %d", sig);*/ + goto ret; + } + if (!signal_handlers[sig].fn) goto ret; + if (signal_handlers[sig].critical) { + signal_handlers[sig].fn(signal_handlers[sig].data); + goto ret; + } + signal_mask[sig] = 1; + ret: + if (can_write(signal_pipe[1])) { + int wr; + EINTRLOOP(wr, write(signal_pipe[1], "x", 1)); + } + errno = sv_errno; +} + +static struct sigaction sa_zero; + +void install_signal_handler(int sig, void (*fn)(void *), void *data, int critical) +{ + int rs; + struct sigaction sa = sa_zero; + if (sig >= NUM_SIGNALS || sig < 0) { + internal("bad signal number: %d", sig); + return; + } + if (!fn) sa.sa_handler = SIG_IGN; + else sa.sa_handler = (void *)got_signal; + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + if (!fn) + EINTRLOOP(rs, sigaction(sig, &sa, NULL)); + signal_handlers[sig].fn = fn; + signal_handlers[sig].data = data; + signal_handlers[sig].critical = critical; + if (fn) + EINTRLOOP(rs, sigaction(sig, &sa, NULL)); +} + +void interruptible_signal(int sig, int in) +{ + struct sigaction sa = sa_zero; + int rs; + if (sig >= NUM_SIGNALS || sig < 0) { + internal("bad signal number: %d", sig); + return; + } + if (!signal_handlers[sig].fn) return; + sa.sa_handler = (void *)got_signal; + sigfillset(&sa.sa_mask); + if (!in) sa.sa_flags = SA_RESTART; + EINTRLOOP(rs, sigaction(sig, &sa, NULL)); +} + +static int check_signals(void) +{ + int i, r = 0; + for (i = 0; i < NUM_SIGNALS; i++) + if (signal_mask[i]) { + signal_mask[i] = 0; + if (signal_handlers[i].fn) { +#ifdef DEBUG_CALLS + fprintf(stderr, "call: signal %d -> %p\n", i, signal_handlers[i].fn); +#endif + pr(signal_handlers[i].fn(signal_handlers[i].data)) return 1; +#ifdef DEBUG_CALLS + fprintf(stderr, "signal done\n"); +#endif + } + CHK_BH; + r = 1; + } + return r; +} + +static void sigchld(void *p) +{ + pid_t pid; +#ifndef WNOHANG + EINTRLOOP(pid, wait(NULL)); +#else + do { + EINTRLOOP(pid, waitpid(-1, NULL, WNOHANG)); + } while (pid > 0); +#endif +} + +void set_sigcld(void) +{ + install_signal_handler(SIGCHLD, sigchld, NULL, 1); +} + +int terminate_loop = 0; + +void select_loop(void (*init)(void)) +{ + struct stat st; + int rs; + EINTRLOOP(rs, stat(".", &st)); + if (rs && getenv("HOME")) + EINTRLOOP(rs, chdir(getenv("HOME"))); + memset(&sa_zero, 0, sizeof sa_zero); + memset(signal_mask, 0, sizeof signal_mask); + memset(signal_handlers, 0, sizeof signal_handlers); + FD_ZERO(&w_read); + FD_ZERO(&w_write); + FD_ZERO(&w_error); + w_max = 0; + last_time = get_time(); + ignore_signals(); + if (c_pipe(signal_pipe)) { + error("ERROR: can't create pipe for signal handling"); + fatal_tty_exit(); + exit(RET_FATAL); + } + EINTRLOOP(rs, fcntl(signal_pipe[0], F_SETFL, O_NONBLOCK)); + EINTRLOOP(rs, fcntl(signal_pipe[1], F_SETFL, O_NONBLOCK)); + set_handlers(signal_pipe[0], signal_break, NULL, NULL, NULL); + init(); + CHK_BH; + while (!terminate_loop) { + volatile int n; /* volatile because of setjmp */ + int i; + struct timeval tv; + struct timeval *tm = NULL; + check_signals(); + check_timers(); + check_timers(); + if (!F) redraw_all_terminals(); +#ifdef OS_BAD_SIGNALS + /* Cygwin has buggy signals that sometimes don't interrupt select. + So don't wait indefinitely in it. */ + tv.tv_sec = 1; + tv.tv_usec = 0; + tm = &tv; +#endif + if (!list_empty(timers)) { + ttime tt = (uttime)((struct timer *)timers.next)->interval + 1; + if (tt < 0) tt = 0; +#ifdef OS_BAD_SIGNALS + if (tt < 1000) +#endif + { + tv.tv_sec = tt / 1000; + tv.tv_usec = (tt % 1000) * 1000; + tm = &tv; + } + } + memcpy(&x_read, &w_read, sizeof(fd_set)); + memcpy(&x_write, &w_write, sizeof(fd_set)); + memcpy(&x_error, &w_error, sizeof(fd_set)); + /*rep_sel:*/ + if (terminate_loop) break; + if (!w_max && list_empty(timers)) { + /*internal("select_loop: no more events to wait for");*/ + break; + } + if (check_signals()) { + continue; + } + /*{ + int i; + printf("\nR:"); + for (i = 0; i < 256; i++) if (FD_ISSET(i, &x_read)) printf("%d,", i); + printf("\nW:"); + for (i = 0; i < 256; i++) if (FD_ISSET(i, &x_write)) printf("%d,", i); + printf("\nE:"); + for (i = 0; i < 256; i++) if (FD_ISSET(i, &x_error)) printf("%d,", i); + fflush(stdout); + }*/ +#ifdef DEBUG_CALLS + fprintf(stderr, "select\n"); +#endif + if ((n = loop_select(w_max, &x_read, &x_write, &x_error, tm)) < 0) { +#ifdef DEBUG_CALLS + fprintf(stderr, "select intr\n"); +#endif + if (errno != EINTR) { + error("ERROR: select failed: %s", strerror(errno)); + fatal_tty_exit(); + exit(RET_FATAL); + } + continue; + } +#ifdef DEBUG_CALLS + fprintf(stderr, "select done\n"); +#endif + check_signals(); + /*printf("sel: %d\n", n);*/ + check_timers(); + i = -1; + while (n > 0 && ++i < w_max) { + int k = 0; + /*printf("C %d : %d,%d,%d\n",i,FD_ISSET(i, &w_read),FD_ISSET(i, &w_write),FD_ISSET(i, &w_error)); + printf("A %d : %d,%d,%d\n",i,FD_ISSET(i, &x_read),FD_ISSET(i, &x_write),FD_ISSET(i, &x_error));*/ + if (FD_ISSET(i, &x_read)) { + if (threads[i].read_func) { +#ifdef DEBUG_CALLS + fprintf(stderr, "call: read %d -> %p\n", i, threads[i].read_func); +#endif + pr(threads[i].read_func(threads[i].data)) continue; +#ifdef DEBUG_CALLS + fprintf(stderr, "read done\n"); +#endif + CHK_BH; + } + k = 1; + } + if (FD_ISSET(i, &x_write)) { + if (threads[i].write_func) { +#ifdef DEBUG_CALLS + fprintf(stderr, "call: write %d -> %p\n", i, threads[i].write_func); +#endif + pr(threads[i].write_func(threads[i].data)) continue; +#ifdef DEBUG_CALLS + fprintf(stderr, "write done\n"); +#endif + CHK_BH; + } + k = 1; + } + if (FD_ISSET(i, &x_error)) { + if (threads[i].error_func) { +#ifdef DEBUG_CALLS + fprintf(stderr, "call: error %d -> %p\n", i, threads[i].error_func); +#endif + pr(threads[i].error_func(threads[i].data)) continue; +#ifdef DEBUG_CALLS + fprintf(stderr, "error done\n"); +#endif + CHK_BH; + } + k = 1; + } + n -= k; + } + nopr(); + } +#ifdef DEBUG_CALLS + fprintf(stderr, "exit loop\n"); +#endif + nopr(); +} |