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