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