ELinks 0.12pre5.GIT
[elinks.git] / src / main / select.c
blob83d80197a48597c4f1e44c4d5e42f7f1218d236f
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 #ifdef __GNU__ /* For GNU Hurd bug workaround in set_handlers() */
14 #include <sys/stat.h> /* OS/2 needs this after sys/types.h */
15 #endif
16 #include <sys/types.h>
17 #ifdef HAVE_SYS_WAIT_H
18 #include <sys/wait.h>
19 #endif
20 #ifdef HAVE_UNISTD_H
21 #include <unistd.h>
22 #endif
24 /* This must be here, thanks to BSD. */
25 #ifdef HAVE_INTTYPES_H
26 #include <inttypes.h> /* OMG */
27 #endif
28 #ifdef HAVE_SYS_SELECT_H
29 #include <sys/select.h>
30 #endif
32 #include "elinks.h"
34 #include "intl/gettext/libintl.h"
35 #include "main/main.h"
36 #include "main/select.h"
37 #include "main/timer.h"
38 #include "osdep/signals.h"
39 #include "terminal/terminal.h"
40 #include "util/error.h"
41 #include "util/memory.h"
42 #include "util/time.h"
45 #ifndef FD_SETSIZE
46 #define FD_SETSIZE 1024
47 #endif
49 struct thread {
50 select_handler_T read_func;
51 select_handler_T write_func;
52 select_handler_T error_func;
53 void *data;
56 static struct thread threads[FD_SETSIZE];
58 static fd_set w_read;
59 static fd_set w_write;
60 static fd_set w_error;
62 static fd_set x_read;
63 static fd_set x_write;
64 static fd_set x_error;
66 static int w_max;
68 int
69 get_file_handles_count(void)
71 int i = 0, j;
73 for (j = 0; j < FD_SETSIZE; j++)
74 if (threads[j].read_func
75 || threads[j].write_func
76 || threads[j].error_func)
77 i++;
78 return i;
81 struct bottom_half {
82 LIST_HEAD(struct bottom_half);
84 select_handler_T fn;
85 void *data;
88 static INIT_LIST_OF(struct bottom_half, bottom_halves);
90 int
91 register_bottom_half_do(select_handler_T fn, void *data)
93 struct bottom_half *bh;
95 foreach (bh, bottom_halves)
96 if (bh->fn == fn && bh->data == data)
97 return 0;
99 bh = mem_alloc(sizeof(*bh));
100 if (!bh) return -1;
101 bh->fn = fn;
102 bh->data = data;
103 add_to_list(bottom_halves, bh);
105 return 0;
108 void
109 check_bottom_halves(void)
111 while (!list_empty(bottom_halves)) {
112 struct bottom_half *bh = bottom_halves.prev;
113 select_handler_T fn = bh->fn;
114 void *data = bh->data;
116 del_from_list(bh);
117 mem_free(bh);
118 fn(data);
122 select_handler_T
123 get_handler(int fd, enum select_handler_type tp)
125 #ifndef CONFIG_OS_WIN32
126 assertm(fd >= 0 && fd < FD_SETSIZE,
127 "get_handler: handle %d >= FD_SETSIZE %d",
128 fd, FD_SETSIZE);
129 if_assert_failed return NULL;
130 #endif
131 switch (tp) {
132 case SELECT_HANDLER_READ: return threads[fd].read_func;
133 case SELECT_HANDLER_WRITE: return threads[fd].write_func;
134 case SELECT_HANDLER_ERROR: return threads[fd].error_func;
135 case SELECT_HANDLER_DATA: return threads[fd].data;
138 INTERNAL("get_handler: bad type %d", tp);
139 return NULL;
142 void
143 set_handlers(int fd, select_handler_T read_func, select_handler_T write_func,
144 select_handler_T error_func, void *data)
146 #ifndef CONFIG_OS_WIN32
147 assertm(fd >= 0 && fd < FD_SETSIZE,
148 "set_handlers: handle %d >= FD_SETSIZE %d",
149 fd, FD_SETSIZE);
150 if_assert_failed return;
151 #endif
152 #ifdef __GNU__
153 /* GNU Hurd pflocal bug <http://savannah.gnu.org/bugs/?22861>:
154 * If ELinks does a select() where the initial exceptfds set
155 * includes a pipe that is not listed in the other fd_sets,
156 * then select() always reports an exception there. That
157 * makes Elinks think the pipe has failed and close it.
158 * To work around this bug, do not monitor exceptions for
159 * pipes on the Hurd. */
160 if (error_func) {
161 struct stat st;
163 if (fstat(fd, &st) == 0 && S_ISFIFO(st.st_mode))
164 error_func = NULL;
166 #endif /* __GNU__ */
167 threads[fd].read_func = read_func;
168 threads[fd].write_func = write_func;
169 threads[fd].error_func = error_func;
170 threads[fd].data = data;
172 if (read_func) {
173 FD_SET(fd, &w_read);
174 } else {
175 FD_CLR(fd, &w_read);
176 FD_CLR(fd, &x_read);
179 if (write_func) {
180 FD_SET(fd, &w_write);
181 } else {
182 FD_CLR(fd, &w_write);
183 FD_CLR(fd, &x_write);
186 if (error_func) {
187 FD_SET(fd, &w_error);
188 } else {
189 FD_CLR(fd, &w_error);
190 FD_CLR(fd, &x_error);
193 if (read_func || write_func || error_func) {
194 if (fd >= w_max) w_max = fd + 1;
195 } else if (fd == w_max - 1) {
196 int i;
198 for (i = fd - 1; i >= 0; i--)
199 if (FD_ISSET(i, &w_read)
200 || FD_ISSET(i, &w_write)
201 || FD_ISSET(i, &w_error))
202 break;
203 w_max = i + 1;
207 void
208 select_loop(void (*init)(void))
210 timeval_T last_time;
211 int select_errors = 0;
213 clear_signal_mask_and_handlers();
214 FD_ZERO(&w_read);
215 FD_ZERO(&w_write);
216 FD_ZERO(&w_error);
217 w_max = 0;
218 timeval_now(&last_time);
219 #ifdef SIGPIPE
220 signal(SIGPIPE, SIG_IGN);
221 #endif
222 init();
223 check_bottom_halves();
225 while (!program.terminate) {
226 struct timeval *timeout = NULL;
227 int n, i, has_timer;
228 timeval_T t;
230 check_signals();
231 check_timers(&last_time);
232 redraw_all_terminals();
234 memcpy(&x_read, &w_read, sizeof(fd_set));
235 memcpy(&x_write, &w_write, sizeof(fd_set));
236 memcpy(&x_error, &w_error, sizeof(fd_set));
238 if (program.terminate) break;
240 has_timer = get_next_timer_time(&t);
241 if (!w_max && !has_timer) break;
242 critical_section = 1;
244 if (check_signals()) {
245 critical_section = 0;
246 continue;
248 #if 0
250 int i;
252 printf("\nR:");
253 for (i = 0; i < 256; i++)
254 if (FD_ISSET(i, &x_read)) printf("%d,", i);
255 printf("\nW:");
256 for (i = 0; i < 256; i++)
257 if (FD_ISSET(i, &x_write)) printf("%d,", i);
258 printf("\nE:");
259 for (i = 0; i < 256; i++)
260 if (FD_ISSET(i, &x_error)) printf("%d,", i);
261 fflush(stdout);
263 #endif
264 if (has_timer) {
265 /* Be sure timeout is not negative. */
266 timeval_limit_to_zero_or_one(&t);
267 timeout = (struct timeval *) &t;
270 n = select(w_max, &x_read, &x_write, &x_error, timeout);
271 if (n < 0) {
272 /* The following calls (especially gettext)
273 * might change errno. */
274 const int errno_from_select = errno;
276 critical_section = 0;
277 uninstall_alarm();
278 if (errno_from_select != EINTR) {
279 ERROR(gettext("The call to %s failed: %d (%s)"),
280 "select()", errno_from_select, (unsigned char *) strerror(errno_from_select));
281 if (++select_errors > 10) /* Infinite loop prevention. */
282 INTERNAL(gettext("%d select() failures."),
283 select_errors);
285 continue;
288 select_errors = 0;
289 critical_section = 0;
290 uninstall_alarm();
291 check_signals();
292 /*printf("sel: %d\n", n);*/
293 check_timers(&last_time);
295 i = -1;
296 while (n > 0 && ++i < w_max) {
297 int k = 0;
299 #if 0
300 printf("C %d : %d,%d,%d\n", i, FD_ISSET(i, &w_read),
301 FD_ISSET(i, &w_write), FD_ISSET(i, &w_error));
302 printf("A %d : %d,%d,%d\n", i, FD_ISSET(i, &x_read),
303 FD_ISSET(i, &x_write), FD_ISSET(i, &x_error));
304 #endif
305 if (FD_ISSET(i, &x_read)) {
306 if (threads[i].read_func) {
307 threads[i].read_func(threads[i].data);
308 check_bottom_halves();
310 k = 1;
313 if (FD_ISSET(i, &x_write)) {
314 if (threads[i].write_func) {
315 threads[i].write_func(threads[i].data);
316 check_bottom_halves();
318 k = 1;
321 if (FD_ISSET(i, &x_error)) {
322 if (threads[i].error_func) {
323 threads[i].error_func(threads[i].data);
324 check_bottom_halves();
326 k = 1;
329 n -= k;
334 static int
335 can_read_or_write(int fd, int write)
337 struct timeval tv = {0, 0};
338 fd_set fds;
339 fd_set *rfds = NULL;
340 fd_set *wfds = NULL;
342 FD_ZERO(&fds);
343 FD_SET(fd, &fds);
345 if (write)
346 wfds = &fds;
347 else
348 rfds = &fds;
350 return select(fd + 1, rfds, wfds, NULL, &tv);
354 can_read(int fd)
356 return can_read_or_write(fd, 0);
360 can_write(int fd)
362 return can_read_or_write(fd, 1);