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