summaryrefslogtreecommitdiff
path: root/select.c
diff options
context:
space:
mode:
authorAnas Nashif <anas.nashif@intel.com>2012-12-07 02:53:31 -0800
committerAnas Nashif <anas.nashif@intel.com>2012-12-07 02:53:31 -0800
commitcbb6286cb92020dd7ae88798ed831ed76fd2130e (patch)
tree782a01c00d5e064aa67ea3f9241a8ef1de1060c6 /select.c
downloadlinks-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.c555
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();
+}