*** empty log message ***
[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
46#include "_scm.h"
47
48#include "iselect.h"
1cbf4dea
MD
49
50#ifdef GUILE_ISELECT
51
3666451e
MD
52#include "coop-threads.h"
53
4d3bacdd 54#ifdef MISSING_BZERO_DECL
b8ff5fe9
MD
55extern void bzero (void *, size_t);
56#endif
57
3666451e
MD
58\f
59
a48b6916 60/* COOP queue macros */
3666451e
MD
61#define QEMPTYP(q) (q.t.next == &q.t)
62#define QFIRST(q) (q.t.next)
63
a48b6916 64/* These macros count the number of bits in a word. */
3666451e 65#define SCM_BITS_PER_LONG (8 * sizeof (unsigned long))
95f44da9
MD
66/* Use LONG_MAX instead of ULONG_MAX here since not all systems define
67 ULONG_MAX */
68#if LONG_MAX >> 16 == 0
3666451e
MD
69#define SCM_NLONGBITS(p) (bc[((unsigned char *)(p))[0]]\
70 + bc[((unsigned char *)(p))[1]])
95f44da9 71#elif LONG_MAX >> 32 == 0
3666451e
MD
72#define SCM_NLONGBITS(p) (bc[((unsigned char *)(p))[0]]\
73 + bc[((unsigned char *)(p))[1]]\
74 + bc[((unsigned char *)(p))[2]]\
75 + bc[((unsigned char *)(p))[3]])
95f44da9 76#elif LONG_MAX >> 64 == 0
3666451e
MD
77#define SCM_NLONGBITS(p) (bc[((unsigned char *)(p))[0]]\
78 + bc[((unsigned char *)(p))[1]]\
79 + bc[((unsigned char *)(p))[2]]\
80 + bc[((unsigned char *)(p))[3]]\
81 + bc[((unsigned char *)(p))[4]]\
82 + bc[((unsigned char *)(p))[5]]\
83 + bc[((unsigned char *)(p))[6]]\
84 + bc[((unsigned char *)(p))[7]])
85#endif
86
87#ifdef HAVE_BZERO
88#define FD_ZERO_N(pos, n) bzero ((pos), (n))
89#else
90#define FD_ZERO_N(pos, n) memset ((void *) (pos), 0, (n))
91#endif
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
MD
239 cont_except:
240 }
3666451e
MD
241}
242
a48b6916
MD
243/* Update the fd sets pointed to by the thread so that they reflect
244 the status of the file descriptors which the thread was interested
245 in. Also clear those bits in the common sets. This function is
246 only called when there are no bit collisions. */
3666451e
MD
247static void
248finalize_fd_sets (coop_t *t)
249{
250 int i = (t->nfds + SCM_BITS_PER_LONG - 1) / SCM_BITS_PER_LONG;
251 int n_ones = 0;
252 register unsigned long s;
a48b6916 253
3666451e
MD
254 if (t->nfds == gnfds)
255 {
a48b6916
MD
256 /* This thread is the one responsible for the current high value
257 of gnfds. First do our other jobs while at the same time
258 trying to decrease gnfds. */
3666451e
MD
259 while (i > 0)
260 {
261 --i;
262 if (t->readfds != NULL && (s = ((ulongptr) t->readfds)[i]) != 0)
263 {
264 ((ulongptr) t->readfds)[i] &= ((ulongptr) &rreadfds)[i];
265 ((ulongptr) &greadfds)[i] &= ~s;
266 n_ones += SCM_NLONGBITS (&((ulongptr) t->readfds)[i]);
267 }
268 if (((ulongptr) &greadfds)[i] != 0)
269 {
270 gnfds = (i + 1) * SCM_BITS_PER_LONG;
271 goto cont_read;
272 }
273 if (t->writefds != NULL && (s = ((ulongptr) t->writefds)[i]) != 0)
274 {
275 ((ulongptr) t->writefds)[i] &= ((ulongptr) &rwritefds)[i];
276 ((ulongptr) &gwritefds)[i] &= ~s;
277 n_ones += SCM_NLONGBITS (&((ulongptr) t->writefds)[i]);
278 }
279 if (((ulongptr) &gwritefds)[i] != 0)
280 {
281 gnfds = (i + 1) * SCM_BITS_PER_LONG;
282 goto cont_write;
283 }
284 if (t->exceptfds != NULL && (s = ((ulongptr) t->exceptfds)[i]) != 0)
285 {
286 ((ulongptr) t->exceptfds)[i] &= ((ulongptr) &rexceptfds)[i];
287 ((ulongptr) &gexceptfds)[i] &= ~s;
288 n_ones += SCM_NLONGBITS (&((ulongptr) t->exceptfds)[i]);
289 }
290 if (((ulongptr) &gexceptfds)[i] != 0)
291 {
292 gnfds = (i + 1) * SCM_BITS_PER_LONG;
293 goto cont_except;
294 }
295 }
296 gnfds = 0;
297 t->retval = n_ones;
298 return;
299 }
a48b6916
MD
300
301 /* Either this thread wasn't responsible for gnfds or gnfds has been
302 determined. */
3666451e
MD
303 while (i > 0)
304 {
305 --i;
306 if (t->readfds != NULL && (s = ((ulongptr) t->readfds)[i]) != 0)
307 {
308 ((ulongptr) t->readfds)[i] &= ((ulongptr) &rreadfds)[i];
309 ((ulongptr) &greadfds)[i] &= ~s;
310 n_ones += SCM_NLONGBITS (&((ulongptr) t->readfds)[i]);
311 }
312 cont_read:
313 if (t->writefds != NULL && (s = ((ulongptr) t->writefds)[i]) != 0)
314 {
315 ((ulongptr) t->writefds)[i] &= ((ulongptr) &rwritefds)[i];
316 ((ulongptr) &gwritefds)[i] &= ~s;
cafc12ff 317 n_ones += SCM_NLONGBITS (&((ulongptr) t->writefds)[i]);
3666451e
MD
318 }
319 cont_write:
320 if (t->exceptfds != NULL && (s = ((ulongptr) t->exceptfds)[i]) != 0)
321 {
322 ((ulongptr) t->exceptfds)[i] &= ((ulongptr) &rexceptfds)[i];
323 ((ulongptr) &gexceptfds)[i] &= ~s;
cafc12ff 324 n_ones += SCM_NLONGBITS (&((ulongptr) t->exceptfds)[i]);
3666451e
MD
325 }
326 cont_except:
95f44da9 327 ;
3666451e
MD
328 }
329 t->retval = n_ones;
330}
331
a48b6916
MD
332/* Just like finalize_fd_sets except that we don't have to update the
333 global fd sets. Those will be recalulated elsewhere. */
334static void
335finalize_fd_sets_lazily (coop_t *t)
336{
337 int i = (t->nfds + SCM_BITS_PER_LONG - 1) / SCM_BITS_PER_LONG;
338 int n_ones = 0;
a48b6916
MD
339 while (i > 0)
340 {
341 --i;
3237b129 342 if (t->readfds != NULL && ((ulongptr) t->readfds)[i] != 0)
a48b6916
MD
343 {
344 ((ulongptr) t->readfds)[i] &= ((ulongptr) &rreadfds)[i];
345 n_ones += SCM_NLONGBITS (&((ulongptr) t->readfds)[i]);
346 }
3237b129 347 if (t->writefds != NULL && ((ulongptr) t->writefds)[i] != 0)
a48b6916
MD
348 {
349 ((ulongptr) t->writefds)[i] &= ((ulongptr) &rwritefds)[i];
350 n_ones += SCM_NLONGBITS (&((ulongptr) t->writefds)[i]);
351 }
3237b129 352 if (t->exceptfds != NULL && ((ulongptr) t->exceptfds)[i] != 0)
a48b6916
MD
353 {
354 ((ulongptr) t->exceptfds)[i] &= ((ulongptr) &rexceptfds)[i];
355 n_ones += SCM_NLONGBITS (&((ulongptr) t->exceptfds)[i]);
356 }
357 }
358 t->retval = n_ones;
359}
360
361/* Return first fd with a non-zero bit in any of the result sets. */
3666451e
MD
362static int
363first_interesting_fd (void)
364{
365 int i = 0;
366 SELECT_TYPE *s;
367 while (1)
368 {
369 if (((ulongptr) &rreadfds)[i] != 0)
370 {
371 s = &rreadfds;
372 break;
373 }
374 if (((ulongptr) &rwritefds)[i] != 0)
375 {
376 s = &rwritefds;
377 break;
378 }
379 if (((ulongptr) &rexceptfds)[i] != 0)
380 {
381 s = &rexceptfds;
382 break;
383 }
384 ++i;
385 }
386 i *= SCM_BITS_PER_LONG;
387 while (i < gnfds)
388 {
389 if (FD_ISSET (i, s))
390 return i;
391 ++i;
392 }
393 fprintf (stderr, "first_interesting_fd: internal error\n");
394 exit (1);
395}
396
a48b6916 397/* Revive all threads with an error status. */
c69dfa65
MD
398void
399scm_error_revive_threads (void)
3666451e
MD
400{
401 coop_t *t;
402
403 while ((t = coop_qget (&coop_global_sleepq)) != NULL)
404 {
c44bfbc9 405 t->_errno = errno;
3666451e 406 t->retval = -1;
c69dfa65
MD
407 if (t != coop_global_curr)
408 coop_qput (&coop_global_runq, t);
3666451e 409 }
c69dfa65 410 collisionp = 0;
3666451e
MD
411 gnfds = 0;
412 FD_ZERO (&greadfds);
413 FD_ZERO (&gwritefds);
414 FD_ZERO (&gexceptfds);
415}
416
a48b6916
MD
417/* Given the result of a call to safe_select and the current time,
418 try to wake up some threads and return the first one. Return NULL
419 if we couldn't find any. */
3666451e 420static coop_t *
c69dfa65 421find_thread (int n, struct timeval *now, int sleepingp)
3666451e
MD
422{
423 coop_t *t;
424 int fd;
425
c69dfa65
MD
426 if (n < 0)
427 /* An error or a signal has occured. Wake all threads. Since we
428 don't care to calculate if there is a sinner we report the
429 error to all of them. */
430 {
431 scm_error_revive_threads ();
432 if (!scm_I_am_dead)
433 return coop_global_curr;
434 }
435 else if (n == 0)
3666451e
MD
436 {
437 while (!QEMPTYP (coop_global_sleepq)
438 && (t = QFIRST (coop_global_sleepq))->timeoutp
439 && (t->wakeup_time.tv_sec < now->tv_sec
440 || (t->wakeup_time.tv_sec == now->tv_sec
441 && t->wakeup_time.tv_usec <= now->tv_usec)))
442 {
443 coop_qget (&coop_global_sleepq);
a48b6916
MD
444 if (collisionp)
445 finalize_fd_sets_lazily (t);
446 else
447 finalize_fd_sets (t);
3666451e
MD
448 coop_qput (&coop_global_runq, t);
449 }
a48b6916
MD
450 if (collisionp)
451 {
452 while ((t = coop_qget (&coop_global_sleepq)) != NULL)
453 coop_qput (&coop_tmp_queue, t);
454 goto rebuild_global_fd_sets;
455 }
3666451e
MD
456 }
457 else if (n > 0)
458 {
459 /* Find the first interesting file descriptor */
460 fd = first_interesting_fd ();
461 /* Check the sleeping queue for this file descriptor.
462 Other file descriptors will be handled next time
463 coop_next_runnable_thread is called. */
464 /* This code is inefficient. We'll improve it later. */
465 while ((t = coop_qget (&coop_global_sleepq)) != NULL)
466 {
467 if ((t->readfds && FD_ISSET (fd, t->readfds))
468 || (t->writefds && FD_ISSET (fd, t->writefds))
469 || (t->exceptfds && FD_ISSET (fd, t->exceptfds))
470 || (t->timeoutp
471 && (t->wakeup_time.tv_sec < now->tv_sec
472 || (t->wakeup_time.tv_sec == now->tv_sec
473 && t->wakeup_time.tv_usec <= now->tv_usec))))
474 {
a48b6916
MD
475 if (collisionp)
476 finalize_fd_sets_lazily (t);
477 else
478 finalize_fd_sets (t);
3666451e
MD
479 coop_qput (&coop_global_runq, t);
480 }
481 else
482 coop_qput(&coop_tmp_queue, t);
483 }
a48b6916
MD
484 if (collisionp)
485 {
486 rebuild_global_fd_sets:
487 collisionp = 0;
488 gnfds = 0;
489 FD_ZERO (&greadfds);
490 FD_ZERO (&gwritefds);
491 FD_ZERO (&gexceptfds);
492 while ((t = coop_qget (&coop_tmp_queue)) != NULL)
493 {
494 add_fd_sets (t);
495 coop_qput (&coop_global_sleepq, t);
496 }
497 }
498 else
499 {
500 while ((t = coop_qget (&coop_tmp_queue)) != NULL)
501 coop_qput (&coop_global_sleepq, t);
502 }
3666451e 503 }
3666451e
MD
504
505 return coop_qget (&coop_global_runq);
506}
507
508/* Return next runnable thread on the run queue.
509 * First update the queue with possible I/O or timeouts.
510 * If no thread is found, return NULL.
511 */
512coop_t *
513coop_next_runnable_thread ()
514{
81e81a5c 515 coop_t *t;
3666451e
MD
516 struct timeval now;
517 int n;
518
519 /* Just return next thread on the runq if the sleepq is empty. */
520 if (QEMPTYP (coop_global_sleepq))
c69dfa65
MD
521 {
522 if (QEMPTYP (coop_global_runq))
523 return coop_global_curr;
524 else
525 return coop_qget (&coop_global_runq);
526 }
3666451e
MD
527
528 if (gnfds > 0)
529 n = safe_select (gnfds, &greadfds, &gwritefds, &gexceptfds, &timeout0);
530 else
531 n = 0;
532 if (QFIRST (coop_global_sleepq)->timeoutp)
533 {
534 gettimeofday (&now, NULL);
c69dfa65 535 t = find_thread (n, &now, 0);
3666451e 536 }
81e81a5c 537 else
c69dfa65
MD
538 t = find_thread (n, 0, 0);
539 return t == NULL ? coop_global_curr : t;
3666451e
MD
540}
541
542coop_t *
543coop_wait_for_runnable_thread_now (struct timeval *now)
544{
545 int n;
546 coop_t *t;
547
548 if (gnfds > 0)
549 n = safe_select (gnfds, &greadfds, &gwritefds, &gexceptfds, &timeout0);
550 else
551 n = 0;
552 /* Is there any other runnable thread? */
c69dfa65 553 t = find_thread (n, now, 1);
3666451e
MD
554 while (t == NULL)
555 {
556 /* No. Let the process go to sleep. */
557 if ((t = QFIRST (coop_global_sleepq))->timeoutp)
558 {
559 now->tv_sec = t->wakeup_time.tv_sec - now->tv_sec;
560 if (now->tv_usec > t->wakeup_time.tv_usec)
561 {
562 --now->tv_sec;
563 now->tv_usec = 1000000 + t->wakeup_time.tv_usec - now->tv_usec;
564 }
565 else
566 now->tv_usec = t->wakeup_time.tv_usec - now->tv_usec;
567 n = safe_select (gnfds, &greadfds, &gwritefds, &gexceptfds, now);
568 }
569 else
570 n = safe_select (gnfds, &greadfds, &gwritefds, &gexceptfds, NULL);
571 gettimeofday (now, NULL);
c69dfa65 572 t = find_thread (n, now, 1);
3666451e
MD
573 }
574
575 return t;
576}
577
578coop_t *
579coop_wait_for_runnable_thread ()
580{
581 struct timeval now;
582
583 if (QEMPTYP (coop_global_sleepq))
c69dfa65
MD
584 {
585 if (QEMPTYP (coop_global_runq))
586 return coop_global_curr;
587 else
588 return coop_qget (&coop_global_runq);
589 }
3666451e
MD
590
591 if (QFIRST (coop_global_sleepq)->timeoutp)
592 gettimeofday (&now, NULL);
593
594 return coop_wait_for_runnable_thread_now (&now);
595}
596
1cbf4dea
MD
597/* Initialize bit counting array */
598static void init_bc (int bit, int i, int n)
599{
600 if (bit == 0)
601 bc[i] = n;
602 else
603 {
604 init_bc (bit >> 1, i, n);
605 init_bc (bit >> 1, i | bit, n + 1);
606 }
607}
608
609void
610scm_init_iselect ()
611{
612#if 0 /* This is just symbolic */
613 collisionp = 0;
614 gnfds = 0;
615 FD_ZERO (&greadfds);
616 FD_ZERO (&gwritefds);
617 FD_ZERO (&gexceptfds);
618 timeout0.tv_sec = 0;
619 timeout0.tv_usec = 0;
620#endif
621 init_bc (0x80, 0, 0);
622#include "iselect.x"
623}
624
625#endif /* GUILE_ISELECT */
626
3666451e
MD
627int
628scm_internal_select (int nfds,
629 SELECT_TYPE *readfds,
630 SELECT_TYPE *writefds,
631 SELECT_TYPE *exceptfds,
632 struct timeval *timeout)
633{
1cbf4dea
MD
634#ifndef GUILE_ISELECT
635 int res = select (nfds, readfds, writefds, exceptfds, timeout);
636 SCM_ASYNC_TICK;
637 return res;
638#else /* GUILE_ISELECT */
3666451e
MD
639 struct timeval now;
640 coop_t *t, *curr = coop_global_curr;
c69dfa65 641
3666451e
MD
642 /* If the timeout is 0, we're polling and can handle it quickly. */
643 if (timeout != NULL
644 && timeout->tv_sec == 0
645 && timeout->tv_usec == 0)
646 return select (nfds, readfds, writefds, exceptfds, timeout);
647
c69dfa65 648 SCM_DEFER_INTS;
c718cb07 649
3666451e 650 /* Add our file descriptor flags to the common set. */
3237b129
MD
651 curr->nfds = nfds;
652 curr->readfds = readfds;
653 curr->writefds = writefds;
654 curr->exceptfds = exceptfds;
655 add_fd_sets (curr);
3666451e
MD
656
657 /* Place ourselves on the sleep queue and get a new thread to run. */
658 if (timeout == NULL)
659 {
660 curr->timeoutp = 0;
661 coop_qput (&coop_global_sleepq, curr);
662 t = coop_wait_for_runnable_thread ();
663 }
664 else
665 {
666 gettimeofday (&now, NULL);
667 curr->timeoutp = 1;
668 curr->wakeup_time.tv_sec = now.tv_sec + timeout->tv_sec;
669 curr->wakeup_time.tv_usec = now.tv_usec + timeout->tv_usec;
670 if (curr->wakeup_time.tv_usec >= 1000000)
671 {
672 ++curr->wakeup_time.tv_sec;
673 curr->wakeup_time.tv_usec -= 1000000;
674 }
675 /* Insert the current thread at the right place in the sleep queue */
676 coop_timeout_qinsert (&coop_global_sleepq, curr);
677 t = coop_wait_for_runnable_thread_now (&now);
678 }
679
680 /* If the new thread is the same as the sleeping thread, do nothing */
c69dfa65 681 if (t != coop_global_curr)
3666451e
MD
682 {
683 /* Do a context switch. */
684 coop_global_curr = t;
685 QT_BLOCK (coop_sleephelp, curr, NULL, t->sp);
686 }
687
c69dfa65
MD
688 if (coop_global_curr->retval == -1)
689 errno = coop_global_curr->_errno;
690 SCM_ALLOW_INTS;
1cbf4dea 691 SCM_ASYNC_TICK;
c69dfa65 692 return coop_global_curr->retval;
1cbf4dea 693#endif /* GUILE_ISELECT */
3666451e 694}