* _scm.h: Removed #include <errno.h>.
[bpt/guile.git] / libguile / iselect.c
CommitLineData
e75341b3 1/* Copyright (C) 1997, 1998, 2000, 2001 Free Software Foundation, Inc.
3666451e
MD
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2, or (at your option)
6 * any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; see the file COPYING. If not, write to
15 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
16 * Boston, MA 02111-1307 USA
17 *
18 * As a special exception, the Free Software Foundation gives permission
19 * for additional uses of the text contained in its release of GUILE.
20 *
21 * The exception is that, if you link the GUILE library with other files
22 * to produce an executable, this does not by itself cause the
23 * resulting executable to be covered by the GNU General Public License.
24 * Your use of that executable is in no way restricted on account of
25 * linking the GUILE library code into it.
26 *
27 * This exception does not however invalidate any other reasons why
28 * the executable file might be covered by the GNU General Public License.
29 *
30 * This exception applies only to the code released by the
31 * Free Software Foundation under the name GUILE. If you copy
32 * code from other Free Software Foundation releases into a copy of
33 * GUILE, as the General Public License permits, the exception does
34 * not apply to the code that you add in this way. To avoid misleading
35 * anyone as to the status of such modified files, you must delete
36 * this exception notice from them.
37 *
38 * If you write modifications of your own for GUILE, it is your choice
39 * whether to permit this exception to apply to your modifications.
40 * If you do not wish that, delete this exception notice. */
41\f
e75341b3 42#include <stdio.h>
e6e2e95a 43#include <errno.h>
3666451e
MD
44#include <limits.h>
45#include <string.h>
46
a26f1191
MV
47#ifdef HAVE_UNISTD_H
48#include <unistd.h>
49#endif
50
a0599745
MD
51#include "libguile/_scm.h"
52#include "libguile/async.h"
3666451e 53
a0599745 54#include "libguile/iselect.h"
1cbf4dea
MD
55
56#ifdef GUILE_ISELECT
57
a0599745 58#include "libguile/coop-threads.h"
3666451e 59
4d3bacdd 60#ifdef MISSING_BZERO_DECL
b8ff5fe9
MD
61extern void bzero (void *, size_t);
62#endif
63
3666451e
MD
64\f
65
a48b6916 66/* COOP queue macros */
3666451e
MD
67#define QEMPTYP(q) (q.t.next == &q.t)
68#define QFIRST(q) (q.t.next)
69
a48b6916 70/* These macros count the number of bits in a word. */
3666451e 71#define SCM_BITS_PER_LONG (8 * sizeof (unsigned long))
95f44da9
MD
72/* Use LONG_MAX instead of ULONG_MAX here since not all systems define
73 ULONG_MAX */
74#if LONG_MAX >> 16 == 0
3666451e
MD
75#define SCM_NLONGBITS(p) (bc[((unsigned char *)(p))[0]]\
76 + bc[((unsigned char *)(p))[1]])
c5070877 77#elif LONG_MAX >> 32 == 0 || LONG_MAX == 2147483647L /* bug in Sun CC 4.2 */
3666451e
MD
78#define SCM_NLONGBITS(p) (bc[((unsigned char *)(p))[0]]\
79 + bc[((unsigned char *)(p))[1]]\
80 + bc[((unsigned char *)(p))[2]]\
81 + bc[((unsigned char *)(p))[3]])
95f44da9 82#elif LONG_MAX >> 64 == 0
3666451e
MD
83#define SCM_NLONGBITS(p) (bc[((unsigned char *)(p))[0]]\
84 + bc[((unsigned char *)(p))[1]]\
85 + bc[((unsigned char *)(p))[2]]\
86 + bc[((unsigned char *)(p))[3]]\
87 + bc[((unsigned char *)(p))[4]]\
88 + bc[((unsigned char *)(p))[5]]\
89 + bc[((unsigned char *)(p))[6]]\
90 + bc[((unsigned char *)(p))[7]])
c5070877
MD
91#else
92#error Could not determine suitable definition for SCM_NLONGBITS
3666451e
MD
93#endif
94
95#ifdef HAVE_BZERO
96#define FD_ZERO_N(pos, n) bzero ((pos), (n))
97#else
98#define FD_ZERO_N(pos, n) memset ((void *) (pos), 0, (n))
99#endif
100
101typedef unsigned long *ulongptr;
102
103static char bc[256]; /* Bit counting array. bc[x] is the number of
104 bits in x. */
105
c69dfa65
MD
106int scm_I_am_dead;
107
a48b6916
MD
108/* This flag indicates that several threads are waiting on the same
109 file descriptor. When this is the case, the common fd sets are
110 updated in a more inefficient way. */
111int collisionp;
112
113/* These are the common fd sets. When new select calls are made,
114 those sets are merged into these. */
3666451e
MD
115int gnfds;
116SELECT_TYPE greadfds;
117SELECT_TYPE gwritefds;
118SELECT_TYPE gexceptfds;
a48b6916
MD
119
120/* These are the result sets. They are used when we call OS select.
121 We couldn't use the common fd sets above, since that would destroy
122 them. */
3666451e
MD
123SELECT_TYPE rreadfds;
124SELECT_TYPE rwritefds;
125SELECT_TYPE rexceptfds;
a48b6916
MD
126
127/* Constant timeval struct representing a zero timeout which we use
128 when polling. */
3666451e
MD
129static struct timeval timeout0;
130
3666451e 131/* As select, but doesn't destroy the file descriptor sets passed as
a48b6916 132 arguments. The results are stored into the result sets. */
3666451e
MD
133static int
134safe_select (int nfds,
135 SELECT_TYPE *readfds,
136 SELECT_TYPE *writefds,
137 SELECT_TYPE *exceptfds,
138 struct timeval *timeout)
139{
140 int n = (nfds + 7) / 8;
141 /* Copy file descriptor sets to result area */
142 if (readfds == NULL)
143 FD_ZERO (&rreadfds);
144 else
145 {
146 memcpy (&rreadfds, readfds, n);
147 FD_ZERO_N ((char *) &rreadfds + n, SELECT_SET_SIZE / 8 - n);
148 }
149 if (writefds == NULL)
150 FD_ZERO (&rwritefds);
151 else
152 {
153 memcpy (&rwritefds, writefds, n);
154 FD_ZERO_N ((char *) &rwritefds + n, SELECT_SET_SIZE / 8 - n);
155 }
156 if (exceptfds == NULL)
157 FD_ZERO (&rexceptfds);
158 else
159 {
160 memcpy (&rexceptfds, exceptfds, n);
161 FD_ZERO_N ((char *) &rexceptfds + n, SELECT_SET_SIZE / 8 - n);
162 }
163 return select (nfds, &rreadfds, &rwritefds, &rexceptfds, timeout);
164}
165
a48b6916
MD
166/* Merge new file descriptor sets into the common sets. */
167static void
168add_fd_sets (coop_t *t)
3666451e 169{
a48b6916
MD
170 int n = (t->nfds + SCM_BITS_PER_LONG - 1) / SCM_BITS_PER_LONG;
171 int i;
172
173 /* Detect if the fd sets of the thread have any bits in common with
174 the rest of the waiting threads. If that is so, set the
175 collision flag. This causes a more time consuming handling of
176 the common fd sets---they need to recalculated every time a
177 thread wakes up. */
178 if (!collisionp)
179 for (i = 0; i < n; ++i)
180 if ((t->readfds != NULL
181 && (((ulongptr) t->readfds)[i] & ((ulongptr) &greadfds)[i]) != 0)
182 || (t->writefds != NULL
183 && ((((ulongptr) t->writefds)[i] & ((ulongptr) &gwritefds)[i])
184 != 0))
185 || (t->exceptfds != NULL
186 && ((((ulongptr) t->exceptfds)[i] & ((ulongptr) &gexceptfds)[i])
187 != 0)))
188 {
189 collisionp = 1;
190 break;
191 }
192
193 /* We recalculate nfds below. The cost for this can be paid back
194 with a great bonus since many programs are lazy with the nfds
195 arg. Many even pass 1024 when using one of the lowest fd:s!
196
197 We approach from above, checking for non-zero bits. As soon as
198 we have determined the value of nfds, we jump down to code below
199 which concludes the updating of the common sets. */
200 t->nfds = 0;
201 i = n;
3666451e
MD
202 while (i > 0)
203 {
204 --i;
a48b6916 205 if (t->readfds != NULL && ((ulongptr) t->readfds)[i] != 0)
3666451e 206 {
a48b6916 207 ((ulongptr) &greadfds)[i] |= ((ulongptr) t->readfds)[i];
3666451e 208 n = (i + 1) * SCM_BITS_PER_LONG;
a48b6916 209 t->nfds = n;
3666451e
MD
210 if (n > gnfds)
211 gnfds = n;
212 goto cont_read;
213 }
a48b6916 214 if (t->writefds != NULL && ((ulongptr) t->writefds)[i] != 0)
3666451e 215 {
a48b6916 216 ((ulongptr) &gwritefds)[i] |= ((ulongptr) t->writefds)[i];
3666451e 217 n = (i + 1) * SCM_BITS_PER_LONG;
a48b6916 218 t->nfds = n;
3666451e
MD
219 if (n > gnfds)
220 gnfds = n;
221 goto cont_write;
222 }
a48b6916 223 if (t->exceptfds != NULL && ((ulongptr) t->exceptfds)[i] != 0)
3666451e 224 {
a48b6916 225 ((ulongptr) &gexceptfds)[i] |= ((ulongptr) t->exceptfds)[i];
3666451e 226 n = (i + 1) * SCM_BITS_PER_LONG;
a48b6916 227 t->nfds = n;
3666451e
MD
228 if (n > gnfds)
229 gnfds = n;
230 goto cont_except;
231 }
232 }
a48b6916
MD
233 return;
234
235 /* nfds is now determined. Just finish updating the common sets. */
3666451e
MD
236 while (i > 0)
237 {
238 --i;
a48b6916
MD
239 if (t->readfds != NULL && ((ulongptr) t->readfds)[i] != 0)
240 ((ulongptr) &greadfds)[i] |= ((ulongptr) t->readfds)[i];
3666451e 241 cont_read:
a48b6916
MD
242 if (t->writefds != NULL && ((ulongptr) t->writefds)[i] != 0)
243 ((ulongptr) &gwritefds)[i] |= ((ulongptr) t->writefds)[i];
3666451e 244 cont_write:
a48b6916
MD
245 if (t->exceptfds != NULL && ((ulongptr) t->exceptfds)[i] != 0)
246 ((ulongptr) &gexceptfds)[i] |= ((ulongptr) t->exceptfds)[i];
3666451e 247 cont_except:
26cbcbf9 248 ;
3666451e 249 }
3666451e
MD
250}
251
a48b6916
MD
252/* Update the fd sets pointed to by the thread so that they reflect
253 the status of the file descriptors which the thread was interested
254 in. Also clear those bits in the common sets. This function is
255 only called when there are no bit collisions. */
3666451e
MD
256static void
257finalize_fd_sets (coop_t *t)
258{
259 int i = (t->nfds + SCM_BITS_PER_LONG - 1) / SCM_BITS_PER_LONG;
260 int n_ones = 0;
261 register unsigned long s;
a48b6916 262
3666451e
MD
263 if (t->nfds == gnfds)
264 {
a48b6916
MD
265 /* This thread is the one responsible for the current high value
266 of gnfds. First do our other jobs while at the same time
267 trying to decrease gnfds. */
3666451e
MD
268 while (i > 0)
269 {
270 --i;
271 if (t->readfds != NULL && (s = ((ulongptr) t->readfds)[i]) != 0)
272 {
273 ((ulongptr) t->readfds)[i] &= ((ulongptr) &rreadfds)[i];
274 ((ulongptr) &greadfds)[i] &= ~s;
275 n_ones += SCM_NLONGBITS (&((ulongptr) t->readfds)[i]);
276 }
277 if (((ulongptr) &greadfds)[i] != 0)
278 {
279 gnfds = (i + 1) * SCM_BITS_PER_LONG;
280 goto cont_read;
281 }
282 if (t->writefds != NULL && (s = ((ulongptr) t->writefds)[i]) != 0)
283 {
284 ((ulongptr) t->writefds)[i] &= ((ulongptr) &rwritefds)[i];
285 ((ulongptr) &gwritefds)[i] &= ~s;
286 n_ones += SCM_NLONGBITS (&((ulongptr) t->writefds)[i]);
287 }
288 if (((ulongptr) &gwritefds)[i] != 0)
289 {
290 gnfds = (i + 1) * SCM_BITS_PER_LONG;
291 goto cont_write;
292 }
293 if (t->exceptfds != NULL && (s = ((ulongptr) t->exceptfds)[i]) != 0)
294 {
295 ((ulongptr) t->exceptfds)[i] &= ((ulongptr) &rexceptfds)[i];
296 ((ulongptr) &gexceptfds)[i] &= ~s;
297 n_ones += SCM_NLONGBITS (&((ulongptr) t->exceptfds)[i]);
298 }
299 if (((ulongptr) &gexceptfds)[i] != 0)
300 {
301 gnfds = (i + 1) * SCM_BITS_PER_LONG;
302 goto cont_except;
303 }
304 }
305 gnfds = 0;
306 t->retval = n_ones;
307 return;
308 }
a48b6916
MD
309
310 /* Either this thread wasn't responsible for gnfds or gnfds has been
311 determined. */
3666451e
MD
312 while (i > 0)
313 {
314 --i;
315 if (t->readfds != NULL && (s = ((ulongptr) t->readfds)[i]) != 0)
316 {
317 ((ulongptr) t->readfds)[i] &= ((ulongptr) &rreadfds)[i];
318 ((ulongptr) &greadfds)[i] &= ~s;
319 n_ones += SCM_NLONGBITS (&((ulongptr) t->readfds)[i]);
320 }
321 cont_read:
322 if (t->writefds != NULL && (s = ((ulongptr) t->writefds)[i]) != 0)
323 {
324 ((ulongptr) t->writefds)[i] &= ((ulongptr) &rwritefds)[i];
325 ((ulongptr) &gwritefds)[i] &= ~s;
cafc12ff 326 n_ones += SCM_NLONGBITS (&((ulongptr) t->writefds)[i]);
3666451e
MD
327 }
328 cont_write:
329 if (t->exceptfds != NULL && (s = ((ulongptr) t->exceptfds)[i]) != 0)
330 {
331 ((ulongptr) t->exceptfds)[i] &= ((ulongptr) &rexceptfds)[i];
332 ((ulongptr) &gexceptfds)[i] &= ~s;
cafc12ff 333 n_ones += SCM_NLONGBITS (&((ulongptr) t->exceptfds)[i]);
3666451e
MD
334 }
335 cont_except:
95f44da9 336 ;
3666451e
MD
337 }
338 t->retval = n_ones;
339}
340
a48b6916
MD
341/* Just like finalize_fd_sets except that we don't have to update the
342 global fd sets. Those will be recalulated elsewhere. */
343static void
344finalize_fd_sets_lazily (coop_t *t)
345{
346 int i = (t->nfds + SCM_BITS_PER_LONG - 1) / SCM_BITS_PER_LONG;
347 int n_ones = 0;
a48b6916
MD
348 while (i > 0)
349 {
350 --i;
3237b129 351 if (t->readfds != NULL && ((ulongptr) t->readfds)[i] != 0)
a48b6916
MD
352 {
353 ((ulongptr) t->readfds)[i] &= ((ulongptr) &rreadfds)[i];
354 n_ones += SCM_NLONGBITS (&((ulongptr) t->readfds)[i]);
355 }
3237b129 356 if (t->writefds != NULL && ((ulongptr) t->writefds)[i] != 0)
a48b6916
MD
357 {
358 ((ulongptr) t->writefds)[i] &= ((ulongptr) &rwritefds)[i];
359 n_ones += SCM_NLONGBITS (&((ulongptr) t->writefds)[i]);
360 }
3237b129 361 if (t->exceptfds != NULL && ((ulongptr) t->exceptfds)[i] != 0)
a48b6916
MD
362 {
363 ((ulongptr) t->exceptfds)[i] &= ((ulongptr) &rexceptfds)[i];
364 n_ones += SCM_NLONGBITS (&((ulongptr) t->exceptfds)[i]);
365 }
366 }
367 t->retval = n_ones;
368}
369
370/* Return first fd with a non-zero bit in any of the result sets. */
3666451e
MD
371static int
372first_interesting_fd (void)
373{
374 int i = 0;
375 SELECT_TYPE *s;
376 while (1)
377 {
378 if (((ulongptr) &rreadfds)[i] != 0)
379 {
380 s = &rreadfds;
381 break;
382 }
383 if (((ulongptr) &rwritefds)[i] != 0)
384 {
385 s = &rwritefds;
386 break;
387 }
388 if (((ulongptr) &rexceptfds)[i] != 0)
389 {
390 s = &rexceptfds;
391 break;
392 }
393 ++i;
394 }
395 i *= SCM_BITS_PER_LONG;
396 while (i < gnfds)
397 {
398 if (FD_ISSET (i, s))
399 return i;
400 ++i;
401 }
402 fprintf (stderr, "first_interesting_fd: internal error\n");
403 exit (1);
404}
405
a48b6916 406/* Revive all threads with an error status. */
c69dfa65
MD
407void
408scm_error_revive_threads (void)
3666451e
MD
409{
410 coop_t *t;
411
412 while ((t = coop_qget (&coop_global_sleepq)) != NULL)
413 {
c44bfbc9 414 t->_errno = errno;
3666451e 415 t->retval = -1;
c69dfa65
MD
416 if (t != coop_global_curr)
417 coop_qput (&coop_global_runq, t);
3666451e 418 }
c69dfa65 419 collisionp = 0;
3666451e
MD
420 gnfds = 0;
421 FD_ZERO (&greadfds);
422 FD_ZERO (&gwritefds);
423 FD_ZERO (&gexceptfds);
424}
425
a48b6916
MD
426/* Given the result of a call to safe_select and the current time,
427 try to wake up some threads and return the first one. Return NULL
428 if we couldn't find any. */
3666451e 429static coop_t *
c69dfa65 430find_thread (int n, struct timeval *now, int sleepingp)
3666451e
MD
431{
432 coop_t *t;
433 int fd;
434
c69dfa65
MD
435 if (n < 0)
436 /* An error or a signal has occured. Wake all threads. Since we
437 don't care to calculate if there is a sinner we report the
438 error to all of them. */
439 {
440 scm_error_revive_threads ();
441 if (!scm_I_am_dead)
442 return coop_global_curr;
443 }
444 else if (n == 0)
3666451e
MD
445 {
446 while (!QEMPTYP (coop_global_sleepq)
447 && (t = QFIRST (coop_global_sleepq))->timeoutp
448 && (t->wakeup_time.tv_sec < now->tv_sec
449 || (t->wakeup_time.tv_sec == now->tv_sec
450 && t->wakeup_time.tv_usec <= now->tv_usec)))
451 {
452 coop_qget (&coop_global_sleepq);
a48b6916
MD
453 if (collisionp)
454 finalize_fd_sets_lazily (t);
455 else
456 finalize_fd_sets (t);
3666451e
MD
457 coop_qput (&coop_global_runq, t);
458 }
a48b6916
MD
459 if (collisionp)
460 {
461 while ((t = coop_qget (&coop_global_sleepq)) != NULL)
462 coop_qput (&coop_tmp_queue, t);
463 goto rebuild_global_fd_sets;
464 }
3666451e
MD
465 }
466 else if (n > 0)
467 {
468 /* Find the first interesting file descriptor */
469 fd = first_interesting_fd ();
470 /* Check the sleeping queue for this file descriptor.
471 Other file descriptors will be handled next time
472 coop_next_runnable_thread is called. */
473 /* This code is inefficient. We'll improve it later. */
474 while ((t = coop_qget (&coop_global_sleepq)) != NULL)
475 {
476 if ((t->readfds && FD_ISSET (fd, t->readfds))
477 || (t->writefds && FD_ISSET (fd, t->writefds))
478 || (t->exceptfds && FD_ISSET (fd, t->exceptfds))
479 || (t->timeoutp
480 && (t->wakeup_time.tv_sec < now->tv_sec
481 || (t->wakeup_time.tv_sec == now->tv_sec
482 && t->wakeup_time.tv_usec <= now->tv_usec))))
483 {
a48b6916
MD
484 if (collisionp)
485 finalize_fd_sets_lazily (t);
486 else
487 finalize_fd_sets (t);
3666451e
MD
488 coop_qput (&coop_global_runq, t);
489 }
490 else
491 coop_qput(&coop_tmp_queue, t);
492 }
a48b6916
MD
493 if (collisionp)
494 {
495 rebuild_global_fd_sets:
496 collisionp = 0;
497 gnfds = 0;
498 FD_ZERO (&greadfds);
499 FD_ZERO (&gwritefds);
500 FD_ZERO (&gexceptfds);
501 while ((t = coop_qget (&coop_tmp_queue)) != NULL)
502 {
503 add_fd_sets (t);
504 coop_qput (&coop_global_sleepq, t);
505 }
506 }
507 else
508 {
509 while ((t = coop_qget (&coop_tmp_queue)) != NULL)
510 coop_qput (&coop_global_sleepq, t);
511 }
3666451e 512 }
3666451e
MD
513
514 return coop_qget (&coop_global_runq);
515}
516
517/* Return next runnable thread on the run queue.
518 * First update the queue with possible I/O or timeouts.
519 * If no thread is found, return NULL.
520 */
521coop_t *
522coop_next_runnable_thread ()
523{
81e81a5c 524 coop_t *t;
3666451e
MD
525 struct timeval now;
526 int n;
527
528 /* Just return next thread on the runq if the sleepq is empty. */
529 if (QEMPTYP (coop_global_sleepq))
c69dfa65
MD
530 {
531 if (QEMPTYP (coop_global_runq))
532 return coop_global_curr;
533 else
534 return coop_qget (&coop_global_runq);
535 }
3666451e
MD
536
537 if (gnfds > 0)
538 n = safe_select (gnfds, &greadfds, &gwritefds, &gexceptfds, &timeout0);
539 else
540 n = 0;
541 if (QFIRST (coop_global_sleepq)->timeoutp)
542 {
543 gettimeofday (&now, NULL);
c69dfa65 544 t = find_thread (n, &now, 0);
3666451e 545 }
81e81a5c 546 else
c69dfa65
MD
547 t = find_thread (n, 0, 0);
548 return t == NULL ? coop_global_curr : t;
3666451e
MD
549}
550
551coop_t *
552coop_wait_for_runnable_thread_now (struct timeval *now)
553{
554 int n;
555 coop_t *t;
556
557 if (gnfds > 0)
558 n = safe_select (gnfds, &greadfds, &gwritefds, &gexceptfds, &timeout0);
559 else
560 n = 0;
561 /* Is there any other runnable thread? */
c69dfa65 562 t = find_thread (n, now, 1);
3666451e
MD
563 while (t == NULL)
564 {
565 /* No. Let the process go to sleep. */
566 if ((t = QFIRST (coop_global_sleepq))->timeoutp)
567 {
568 now->tv_sec = t->wakeup_time.tv_sec - now->tv_sec;
569 if (now->tv_usec > t->wakeup_time.tv_usec)
570 {
571 --now->tv_sec;
572 now->tv_usec = 1000000 + t->wakeup_time.tv_usec - now->tv_usec;
573 }
574 else
575 now->tv_usec = t->wakeup_time.tv_usec - now->tv_usec;
576 n = safe_select (gnfds, &greadfds, &gwritefds, &gexceptfds, now);
577 }
578 else
579 n = safe_select (gnfds, &greadfds, &gwritefds, &gexceptfds, NULL);
580 gettimeofday (now, NULL);
c69dfa65 581 t = find_thread (n, now, 1);
3666451e
MD
582 }
583
584 return t;
585}
586
587coop_t *
588coop_wait_for_runnable_thread ()
589{
590 struct timeval now;
591
592 if (QEMPTYP (coop_global_sleepq))
c69dfa65
MD
593 {
594 if (QEMPTYP (coop_global_runq))
595 return coop_global_curr;
596 else
597 return coop_qget (&coop_global_runq);
598 }
3666451e
MD
599
600 if (QFIRST (coop_global_sleepq)->timeoutp)
601 gettimeofday (&now, NULL);
602
603 return coop_wait_for_runnable_thread_now (&now);
604}
605
1cbf4dea
MD
606/* Initialize bit counting array */
607static void init_bc (int bit, int i, int n)
608{
609 if (bit == 0)
610 bc[i] = n;
611 else
612 {
613 init_bc (bit >> 1, i, n);
614 init_bc (bit >> 1, i | bit, n + 1);
615 }
616}
617
618void
619scm_init_iselect ()
620{
621#if 0 /* This is just symbolic */
622 collisionp = 0;
623 gnfds = 0;
624 FD_ZERO (&greadfds);
625 FD_ZERO (&gwritefds);
626 FD_ZERO (&gexceptfds);
627 timeout0.tv_sec = 0;
628 timeout0.tv_usec = 0;
629#endif
630 init_bc (0x80, 0, 0);
8dc9439f 631#ifndef SCM_MAGIC_SNARFER
a0599745 632#include "libguile/iselect.x"
8dc9439f 633#endif
1cbf4dea
MD
634}
635
636#endif /* GUILE_ISELECT */
637
3666451e
MD
638int
639scm_internal_select (int nfds,
640 SELECT_TYPE *readfds,
641 SELECT_TYPE *writefds,
642 SELECT_TYPE *exceptfds,
643 struct timeval *timeout)
644{
1cbf4dea
MD
645#ifndef GUILE_ISELECT
646 int res = select (nfds, readfds, writefds, exceptfds, timeout);
647 SCM_ASYNC_TICK;
648 return res;
649#else /* GUILE_ISELECT */
3666451e
MD
650 struct timeval now;
651 coop_t *t, *curr = coop_global_curr;
c69dfa65 652
3666451e
MD
653 /* If the timeout is 0, we're polling and can handle it quickly. */
654 if (timeout != NULL
655 && timeout->tv_sec == 0
656 && timeout->tv_usec == 0)
657 return select (nfds, readfds, writefds, exceptfds, timeout);
658
c69dfa65 659 SCM_DEFER_INTS;
c718cb07 660
3666451e 661 /* Add our file descriptor flags to the common set. */
3237b129
MD
662 curr->nfds = nfds;
663 curr->readfds = readfds;
664 curr->writefds = writefds;
665 curr->exceptfds = exceptfds;
666 add_fd_sets (curr);
3666451e
MD
667
668 /* Place ourselves on the sleep queue and get a new thread to run. */
669 if (timeout == NULL)
670 {
671 curr->timeoutp = 0;
672 coop_qput (&coop_global_sleepq, curr);
673 t = coop_wait_for_runnable_thread ();
674 }
675 else
676 {
677 gettimeofday (&now, NULL);
678 curr->timeoutp = 1;
679 curr->wakeup_time.tv_sec = now.tv_sec + timeout->tv_sec;
680 curr->wakeup_time.tv_usec = now.tv_usec + timeout->tv_usec;
681 if (curr->wakeup_time.tv_usec >= 1000000)
682 {
683 ++curr->wakeup_time.tv_sec;
684 curr->wakeup_time.tv_usec -= 1000000;
685 }
686 /* Insert the current thread at the right place in the sleep queue */
687 coop_timeout_qinsert (&coop_global_sleepq, curr);
688 t = coop_wait_for_runnable_thread_now (&now);
689 }
690
691 /* If the new thread is the same as the sleeping thread, do nothing */
c69dfa65 692 if (t != coop_global_curr)
3666451e
MD
693 {
694 /* Do a context switch. */
695 coop_global_curr = t;
696 QT_BLOCK (coop_sleephelp, curr, NULL, t->sp);
697 }
698
c69dfa65
MD
699 if (coop_global_curr->retval == -1)
700 errno = coop_global_curr->_errno;
701 SCM_ALLOW_INTS;
1cbf4dea 702 SCM_ASYNC_TICK;
c69dfa65 703 return coop_global_curr->retval;
1cbf4dea 704#endif /* GUILE_ISELECT */
3666451e 705}
89e00824
ML
706
707/*
708 Local Variables:
709 c-file-style: "gnu"
710 End:
711*/