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