1 /* File descriptors managment and switching */
9 #include <string.h> /* FreeBSD FD_ZERO() macro calls bzero() */
10 #ifdef HAVE_SYS_SIGNAL_H
11 #include <sys/signal.h>
13 #ifdef __GNU__ /* For GNU Hurd bug workaround in set_handlers() */
14 #include <sys/stat.h> /* OS/2 needs this after sys/types.h */
16 #include <sys/types.h>
17 #ifdef HAVE_SYS_WAIT_H
24 /* This must be here, thanks to BSD. */
25 #ifdef HAVE_INTTYPES_H
26 #include <inttypes.h> /* OMG */
28 #ifdef HAVE_SYS_SELECT_H
29 #include <sys/select.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"
46 #define FD_SETSIZE 1024
50 select_handler_T read_func
;
51 select_handler_T write_func
;
52 select_handler_T error_func
;
56 static struct thread threads
[FD_SETSIZE
];
59 static fd_set w_write
;
60 static fd_set w_error
;
63 static fd_set x_write
;
64 static fd_set x_error
;
69 get_file_handles_count(void)
73 for (j
= 0; j
< FD_SETSIZE
; j
++)
74 if (threads
[j
].read_func
75 || threads
[j
].write_func
76 || threads
[j
].error_func
)
82 LIST_HEAD(struct bottom_half
);
88 static INIT_LIST_OF(struct bottom_half
, bottom_halves
);
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
)
99 bh
= mem_alloc(sizeof(*bh
));
103 add_to_list(bottom_halves
, bh
);
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
;
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",
129 if_assert_failed
return NULL
;
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
);
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",
150 if_assert_failed
return;
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. */
163 if (fstat(fd
, &st
) == 0 && S_ISFIFO(st
.st_mode
))
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
;
180 FD_SET(fd
, &w_write
);
182 FD_CLR(fd
, &w_write
);
183 FD_CLR(fd
, &x_write
);
187 FD_SET(fd
, &w_error
);
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) {
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
))
208 select_loop(void (*init
)(void))
211 int select_errors
= 0;
213 clear_signal_mask_and_handlers();
218 timeval_now(&last_time
);
220 signal(SIGPIPE
, SIG_IGN
);
223 check_bottom_halves();
225 while (!program
.terminate
) {
226 struct timeval
*timeout
= NULL
;
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;
253 for (i
= 0; i
< 256; i
++)
254 if (FD_ISSET(i
, &x_read
)) printf("%d,", i
);
256 for (i
= 0; i
< 256; i
++)
257 if (FD_ISSET(i
, &x_write
)) printf("%d,", i
);
259 for (i
= 0; i
< 256; i
++)
260 if (FD_ISSET(i
, &x_error
)) printf("%d,", i
);
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
);
272 /* The following calls (especially gettext)
273 * might change errno. */
274 const int errno_from_select
= errno
;
276 critical_section
= 0;
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."),
289 critical_section
= 0;
292 /*printf("sel: %d\n", n);*/
293 check_timers(&last_time
);
296 while (n
> 0 && ++i
< w_max
) {
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
));
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();
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();
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();
335 can_read_or_write(int fd
, int write
)
337 struct timeval tv
= {0, 0};
350 return select(fd
+ 1, rfds
, wfds
, NULL
, &tv
);
356 return can_read_or_write(fd
, 0);
362 return can_read_or_write(fd
, 1);