Merge with git+ssh://pasky.or.cz/srv/git/elinks.git
[elinks.git] / src / main / select.c
blob08cd4d563b987920546919eb67c2a88d7412ab8d
1 /* File descriptors managment and switching */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <errno.h>
8 #include <signal.h>
9 #include <string.h> /* FreeBSD FD_ZERO() macro calls bzero() */
10 #ifdef HAVE_SYS_SIGNAL_H
11 #include <sys/signal.h>
12 #endif
13 #include <sys/types.h>
14 #ifdef HAVE_SYS_WAIT_H
15 #include <sys/wait.h>
16 #endif
17 #ifdef HAVE_UNISTD_H
18 #include <unistd.h>
19 #endif
21 /* This must be here, thanks to BSD. */
22 #ifdef HAVE_INTTYPES_H
23 #include <inttypes.h> /* OMG */
24 #endif
25 #ifdef HAVE_SYS_SELECT_H
26 #include <sys/select.h>
27 #endif
29 #include "elinks.h"
31 #include "intl/gettext/libintl.h"
32 #include "main/main.h"
33 #include "main/select.h"
34 #include "main/timer.h"
35 #include "osdep/signals.h"
36 #include "terminal/terminal.h"
37 #include "util/error.h"
38 #include "util/memory.h"
39 #include "util/time.h"
42 #ifndef FD_SETSIZE
43 #define FD_SETSIZE 1024
44 #endif
46 struct thread {
47 select_handler_T read_func;
48 select_handler_T write_func;
49 select_handler_T error_func;
50 void *data;
53 static struct thread threads[FD_SETSIZE];
55 static fd_set w_read;
56 static fd_set w_write;
57 static fd_set w_error;
59 static fd_set x_read;
60 static fd_set x_write;
61 static fd_set x_error;
63 static int w_max;
65 int
66 get_file_handles_count(void)
68 int i = 0, j;
70 for (j = 0; j < FD_SETSIZE; j++)
71 if (threads[j].read_func
72 || threads[j].write_func
73 || threads[j].error_func)
74 i++;
75 return i;
78 struct bottom_half {
79 LIST_HEAD(struct bottom_half);
81 select_handler_T fn;
82 void *data;
85 static INIT_LIST_HEAD(bottom_halves);
87 int
88 register_bottom_half_do(select_handler_T fn, void *data)
90 struct bottom_half *bh;
92 foreach (bh, bottom_halves)
93 if (bh->fn == fn && bh->data == data)
94 return 0;
96 bh = mem_alloc(sizeof(*bh));
97 if (!bh) return -1;
98 bh->fn = fn;
99 bh->data = data;
100 add_to_list(bottom_halves, bh);
102 return 0;
105 void
106 check_bottom_halves(void)
108 while (!list_empty(bottom_halves)) {
109 struct bottom_half *bh = bottom_halves.prev;
110 select_handler_T fn = bh->fn;
111 void *data = bh->data;
113 del_from_list(bh);
114 mem_free(bh);
115 fn(data);
119 select_handler_T
120 get_handler(int fd, enum select_handler_type tp)
122 assertm(fd >= 0 && fd < FD_SETSIZE,
123 "get_handler: handle %d >= FD_SETSIZE %d",
124 fd, FD_SETSIZE);
125 if_assert_failed return NULL;
127 switch (tp) {
128 case SELECT_HANDLER_READ: return threads[fd].read_func;
129 case SELECT_HANDLER_WRITE: return threads[fd].write_func;
130 case SELECT_HANDLER_ERROR: return threads[fd].error_func;
131 case SELECT_HANDLER_DATA: return threads[fd].data;
134 INTERNAL("get_handler: bad type %d", tp);
135 return NULL;
138 void
139 set_handlers(int fd, select_handler_T read_func, select_handler_T write_func,
140 select_handler_T error_func, void *data)
142 assertm(fd >= 0 && fd < FD_SETSIZE,
143 "set_handlers: handle %d >= FD_SETSIZE %d",
144 fd, FD_SETSIZE);
145 if_assert_failed return;
147 threads[fd].read_func = read_func;
148 threads[fd].write_func = write_func;
149 threads[fd].error_func = error_func;
150 threads[fd].data = data;
152 if (read_func) {
153 FD_SET(fd, &w_read);
154 } else {
155 FD_CLR(fd, &w_read);
156 FD_CLR(fd, &x_read);
159 if (write_func) {
160 FD_SET(fd, &w_write);
161 } else {
162 FD_CLR(fd, &w_write);
163 FD_CLR(fd, &x_write);
166 if (error_func) {
167 FD_SET(fd, &w_error);
168 } else {
169 FD_CLR(fd, &w_error);
170 FD_CLR(fd, &x_error);
173 if (read_func || write_func || error_func) {
174 if (fd >= w_max) w_max = fd + 1;
175 } else if (fd == w_max - 1) {
176 int i;
178 for (i = fd - 1; i >= 0; i--)
179 if (FD_ISSET(i, &w_read)
180 || FD_ISSET(i, &w_write)
181 || FD_ISSET(i, &w_error))
182 break;
183 w_max = i + 1;
187 void
188 select_loop(void (*init)(void))
190 timeval_T last_time;
191 int select_errors = 0;
193 clear_signal_mask_and_handlers();
194 FD_ZERO(&w_read);
195 FD_ZERO(&w_write);
196 FD_ZERO(&w_error);
197 w_max = 0;
198 timeval_now(&last_time);
199 #ifdef SIGPIPE
200 signal(SIGPIPE, SIG_IGN);
201 #endif
202 init();
203 check_bottom_halves();
205 while (!program.terminate) {
206 struct timeval *timeout = NULL;
207 int n, i, has_timer;
208 timeval_T t;
210 check_signals();
211 check_timers(&last_time);
212 redraw_all_terminals();
214 memcpy(&x_read, &w_read, sizeof(fd_set));
215 memcpy(&x_write, &w_write, sizeof(fd_set));
216 memcpy(&x_error, &w_error, sizeof(fd_set));
218 if (program.terminate) break;
220 has_timer = get_next_timer_time(&t);
221 if (!w_max && !has_timer) break;
222 critical_section = 1;
224 if (check_signals()) {
225 critical_section = 0;
226 continue;
228 #if 0
230 int i;
232 printf("\nR:");
233 for (i = 0; i < 256; i++)
234 if (FD_ISSET(i, &x_read)) printf("%d,", i);
235 printf("\nW:");
236 for (i = 0; i < 256; i++)
237 if (FD_ISSET(i, &x_write)) printf("%d,", i);
238 printf("\nE:");
239 for (i = 0; i < 256; i++)
240 if (FD_ISSET(i, &x_error)) printf("%d,", i);
241 fflush(stdout);
243 #endif
244 if (has_timer) {
245 /* Be sure timeout is not negative. */
246 timeval_limit_to_zero(&t);
247 timeout = (struct timeval *) &t;
250 n = select(w_max, &x_read, &x_write, &x_error, timeout);
251 if (n < 0) {
252 critical_section = 0;
253 uninstall_alarm();
254 if (errno != EINTR) {
255 ERROR(gettext("The call to %s failed: %d (%s)"),
256 "select()", errno, (unsigned char *) strerror(errno));
257 if (++select_errors > 10) /* Infinite loop prevention. */
258 INTERNAL(gettext("%d select() failures."),
259 select_errors);
261 continue;
264 select_errors = 0;
265 critical_section = 0;
266 uninstall_alarm();
267 check_signals();
268 /*printf("sel: %d\n", n);*/
269 check_timers(&last_time);
271 i = -1;
272 while (n > 0 && ++i < w_max) {
273 int k = 0;
275 #if 0
276 printf("C %d : %d,%d,%d\n", i, FD_ISSET(i, &w_read),
277 FD_ISSET(i, &w_write), FD_ISSET(i, &w_error));
278 printf("A %d : %d,%d,%d\n", i, FD_ISSET(i, &x_read),
279 FD_ISSET(i, &x_write), FD_ISSET(i, &x_error));
280 #endif
281 if (FD_ISSET(i, &x_read)) {
282 if (threads[i].read_func) {
283 threads[i].read_func(threads[i].data);
284 check_bottom_halves();
286 k = 1;
289 if (FD_ISSET(i, &x_write)) {
290 if (threads[i].write_func) {
291 threads[i].write_func(threads[i].data);
292 check_bottom_halves();
294 k = 1;
297 if (FD_ISSET(i, &x_error)) {
298 if (threads[i].error_func) {
299 threads[i].error_func(threads[i].data);
300 check_bottom_halves();
302 k = 1;
305 n -= k;
310 static int
311 can_read_or_write(int fd, int write)
313 struct timeval tv = {0, 0};
314 fd_set fds;
315 fd_set *rfds = NULL;
316 fd_set *wfds = NULL;
318 FD_ZERO(&fds);
319 FD_SET(fd, &fds);
321 if (write)
322 wfds = &fds;
323 else
324 rfds = &fds;
326 return select(fd + 1, rfds, wfds, NULL, &tv);
330 can_read(int fd)
332 return can_read_or_write(fd, 0);
336 can_write(int fd)
338 return can_read_or_write(fd, 1);