1008: Added upload_progress to the connection.
[elinks.git] / src / main / select.c
blobaab5349de8b2241472b2c540fe717e55910f0311
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_OF(struct bottom_half, 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 #ifndef CONFIG_OS_WIN32
123 assertm(fd >= 0 && fd < FD_SETSIZE,
124 "get_handler: handle %d >= FD_SETSIZE %d",
125 fd, FD_SETSIZE);
126 if_assert_failed return NULL;
127 #endif
128 switch (tp) {
129 case SELECT_HANDLER_READ: return threads[fd].read_func;
130 case SELECT_HANDLER_WRITE: return threads[fd].write_func;
131 case SELECT_HANDLER_ERROR: return threads[fd].error_func;
132 case SELECT_HANDLER_DATA: return threads[fd].data;
135 INTERNAL("get_handler: bad type %d", tp);
136 return NULL;
139 void
140 set_handlers(int fd, select_handler_T read_func, select_handler_T write_func,
141 select_handler_T error_func, void *data)
143 #ifndef CONFIG_OS_WIN32
144 assertm(fd >= 0 && fd < FD_SETSIZE,
145 "set_handlers: handle %d >= FD_SETSIZE %d",
146 fd, FD_SETSIZE);
147 if_assert_failed return;
148 #endif
149 threads[fd].read_func = read_func;
150 threads[fd].write_func = write_func;
151 threads[fd].error_func = error_func;
152 threads[fd].data = data;
154 if (read_func) {
155 FD_SET(fd, &w_read);
156 } else {
157 FD_CLR(fd, &w_read);
158 FD_CLR(fd, &x_read);
161 if (write_func) {
162 FD_SET(fd, &w_write);
163 } else {
164 FD_CLR(fd, &w_write);
165 FD_CLR(fd, &x_write);
168 if (error_func) {
169 FD_SET(fd, &w_error);
170 } else {
171 FD_CLR(fd, &w_error);
172 FD_CLR(fd, &x_error);
175 if (read_func || write_func || error_func) {
176 if (fd >= w_max) w_max = fd + 1;
177 } else if (fd == w_max - 1) {
178 int i;
180 for (i = fd - 1; i >= 0; i--)
181 if (FD_ISSET(i, &w_read)
182 || FD_ISSET(i, &w_write)
183 || FD_ISSET(i, &w_error))
184 break;
185 w_max = i + 1;
189 void
190 select_loop(void (*init)(void))
192 timeval_T last_time;
193 int select_errors = 0;
195 clear_signal_mask_and_handlers();
196 FD_ZERO(&w_read);
197 FD_ZERO(&w_write);
198 FD_ZERO(&w_error);
199 w_max = 0;
200 timeval_now(&last_time);
201 #ifdef SIGPIPE
202 signal(SIGPIPE, SIG_IGN);
203 #endif
204 init();
205 check_bottom_halves();
207 while (!program.terminate) {
208 struct timeval *timeout = NULL;
209 int n, i, has_timer;
210 timeval_T t;
212 check_signals();
213 check_timers(&last_time);
214 redraw_all_terminals();
216 memcpy(&x_read, &w_read, sizeof(fd_set));
217 memcpy(&x_write, &w_write, sizeof(fd_set));
218 memcpy(&x_error, &w_error, sizeof(fd_set));
220 if (program.terminate) break;
222 has_timer = get_next_timer_time(&t);
223 if (!w_max && !has_timer) break;
224 critical_section = 1;
226 if (check_signals()) {
227 critical_section = 0;
228 continue;
230 #if 0
232 int i;
234 printf("\nR:");
235 for (i = 0; i < 256; i++)
236 if (FD_ISSET(i, &x_read)) printf("%d,", i);
237 printf("\nW:");
238 for (i = 0; i < 256; i++)
239 if (FD_ISSET(i, &x_write)) printf("%d,", i);
240 printf("\nE:");
241 for (i = 0; i < 256; i++)
242 if (FD_ISSET(i, &x_error)) printf("%d,", i);
243 fflush(stdout);
245 #endif
246 if (has_timer) {
247 /* Be sure timeout is not negative. */
248 timeval_limit_to_zero_or_one(&t);
249 timeout = (struct timeval *) &t;
252 n = select(w_max, &x_read, &x_write, &x_error, timeout);
253 if (n < 0) {
254 /* The following calls (especially gettext)
255 * might change errno. */
256 const int errno_from_select = errno;
258 critical_section = 0;
259 uninstall_alarm();
260 if (errno_from_select != EINTR) {
261 ERROR(gettext("The call to %s failed: %d (%s)"),
262 "select()", errno_from_select, (unsigned char *) strerror(errno_from_select));
263 if (++select_errors > 10) /* Infinite loop prevention. */
264 INTERNAL(gettext("%d select() failures."),
265 select_errors);
267 continue;
270 select_errors = 0;
271 critical_section = 0;
272 uninstall_alarm();
273 check_signals();
274 /*printf("sel: %d\n", n);*/
275 check_timers(&last_time);
277 i = -1;
278 while (n > 0 && ++i < w_max) {
279 int k = 0;
281 #if 0
282 printf("C %d : %d,%d,%d\n", i, FD_ISSET(i, &w_read),
283 FD_ISSET(i, &w_write), FD_ISSET(i, &w_error));
284 printf("A %d : %d,%d,%d\n", i, FD_ISSET(i, &x_read),
285 FD_ISSET(i, &x_write), FD_ISSET(i, &x_error));
286 #endif
287 if (FD_ISSET(i, &x_read)) {
288 if (threads[i].read_func) {
289 threads[i].read_func(threads[i].data);
290 check_bottom_halves();
292 k = 1;
295 if (FD_ISSET(i, &x_write)) {
296 if (threads[i].write_func) {
297 threads[i].write_func(threads[i].data);
298 check_bottom_halves();
300 k = 1;
303 if (FD_ISSET(i, &x_error)) {
304 if (threads[i].error_func) {
305 threads[i].error_func(threads[i].data);
306 check_bottom_halves();
308 k = 1;
311 n -= k;
316 static int
317 can_read_or_write(int fd, int write)
319 struct timeval tv = {0, 0};
320 fd_set fds;
321 fd_set *rfds = NULL;
322 fd_set *wfds = NULL;
324 FD_ZERO(&fds);
325 FD_SET(fd, &fds);
327 if (write)
328 wfds = &fds;
329 else
330 rfds = &fds;
332 return select(fd + 1, rfds, wfds, NULL, &tv);
336 can_read(int fd)
338 return can_read_or_write(fd, 0);
342 can_write(int fd)
344 return can_read_or_write(fd, 1);