* *.[hc]: add Emacs magic at the end of file, to ensure GNU
[bpt/guile.git] / libguile / iselect.c
1 /* Copyright (C) 1997, 1998, 2000 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 #include "async.h"
48
49 #include "iselect.h"
50
51 #ifdef GUILE_ISELECT
52
53 #include "coop-threads.h"
54
55 #ifdef MISSING_BZERO_DECL
56 extern void bzero (void *, size_t);
57 #endif
58
59 \f
60
61 /* COOP queue macros */
62 #define QEMPTYP(q) (q.t.next == &q.t)
63 #define QFIRST(q) (q.t.next)
64
65 /* These macros count the number of bits in a word. */
66 #define SCM_BITS_PER_LONG (8 * sizeof (unsigned long))
67 /* Use LONG_MAX instead of ULONG_MAX here since not all systems define
68 ULONG_MAX */
69 #if LONG_MAX >> 16 == 0
70 #define SCM_NLONGBITS(p) (bc[((unsigned char *)(p))[0]]\
71 + bc[((unsigned char *)(p))[1]])
72 #elif LONG_MAX >> 32 == 0
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]])
77 #elif LONG_MAX >> 64 == 0
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
94 typedef unsigned long *ulongptr;
95
96 static char bc[256]; /* Bit counting array. bc[x] is the number of
97 bits in x. */
98
99 int scm_I_am_dead;
100
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. */
104 int collisionp;
105
106 /* These are the common fd sets. When new select calls are made,
107 those sets are merged into these. */
108 int gnfds;
109 SELECT_TYPE greadfds;
110 SELECT_TYPE gwritefds;
111 SELECT_TYPE gexceptfds;
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. */
116 SELECT_TYPE rreadfds;
117 SELECT_TYPE rwritefds;
118 SELECT_TYPE rexceptfds;
119
120 /* Constant timeval struct representing a zero timeout which we use
121 when polling. */
122 static struct timeval timeout0;
123
124 /* As select, but doesn't destroy the file descriptor sets passed as
125 arguments. The results are stored into the result sets. */
126 static int
127 safe_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
159 /* Merge new file descriptor sets into the common sets. */
160 static void
161 add_fd_sets (coop_t *t)
162 {
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;
195 while (i > 0)
196 {
197 --i;
198 if (t->readfds != NULL && ((ulongptr) t->readfds)[i] != 0)
199 {
200 ((ulongptr) &greadfds)[i] |= ((ulongptr) t->readfds)[i];
201 n = (i + 1) * SCM_BITS_PER_LONG;
202 t->nfds = n;
203 if (n > gnfds)
204 gnfds = n;
205 goto cont_read;
206 }
207 if (t->writefds != NULL && ((ulongptr) t->writefds)[i] != 0)
208 {
209 ((ulongptr) &gwritefds)[i] |= ((ulongptr) t->writefds)[i];
210 n = (i + 1) * SCM_BITS_PER_LONG;
211 t->nfds = n;
212 if (n > gnfds)
213 gnfds = n;
214 goto cont_write;
215 }
216 if (t->exceptfds != NULL && ((ulongptr) t->exceptfds)[i] != 0)
217 {
218 ((ulongptr) &gexceptfds)[i] |= ((ulongptr) t->exceptfds)[i];
219 n = (i + 1) * SCM_BITS_PER_LONG;
220 t->nfds = n;
221 if (n > gnfds)
222 gnfds = n;
223 goto cont_except;
224 }
225 }
226 return;
227
228 /* nfds is now determined. Just finish updating the common sets. */
229 while (i > 0)
230 {
231 --i;
232 if (t->readfds != NULL && ((ulongptr) t->readfds)[i] != 0)
233 ((ulongptr) &greadfds)[i] |= ((ulongptr) t->readfds)[i];
234 cont_read:
235 if (t->writefds != NULL && ((ulongptr) t->writefds)[i] != 0)
236 ((ulongptr) &gwritefds)[i] |= ((ulongptr) t->writefds)[i];
237 cont_write:
238 if (t->exceptfds != NULL && ((ulongptr) t->exceptfds)[i] != 0)
239 ((ulongptr) &gexceptfds)[i] |= ((ulongptr) t->exceptfds)[i];
240 cont_except:
241 }
242 }
243
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. */
248 static void
249 finalize_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;
254
255 if (t->nfds == gnfds)
256 {
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. */
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 }
301
302 /* Either this thread wasn't responsible for gnfds or gnfds has been
303 determined. */
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;
318 n_ones += SCM_NLONGBITS (&((ulongptr) t->writefds)[i]);
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;
325 n_ones += SCM_NLONGBITS (&((ulongptr) t->exceptfds)[i]);
326 }
327 cont_except:
328 ;
329 }
330 t->retval = n_ones;
331 }
332
333 /* Just like finalize_fd_sets except that we don't have to update the
334 global fd sets. Those will be recalulated elsewhere. */
335 static void
336 finalize_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;
340 while (i > 0)
341 {
342 --i;
343 if (t->readfds != NULL && ((ulongptr) t->readfds)[i] != 0)
344 {
345 ((ulongptr) t->readfds)[i] &= ((ulongptr) &rreadfds)[i];
346 n_ones += SCM_NLONGBITS (&((ulongptr) t->readfds)[i]);
347 }
348 if (t->writefds != NULL && ((ulongptr) t->writefds)[i] != 0)
349 {
350 ((ulongptr) t->writefds)[i] &= ((ulongptr) &rwritefds)[i];
351 n_ones += SCM_NLONGBITS (&((ulongptr) t->writefds)[i]);
352 }
353 if (t->exceptfds != NULL && ((ulongptr) t->exceptfds)[i] != 0)
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. */
363 static int
364 first_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
398 /* Revive all threads with an error status. */
399 void
400 scm_error_revive_threads (void)
401 {
402 coop_t *t;
403
404 while ((t = coop_qget (&coop_global_sleepq)) != NULL)
405 {
406 t->_errno = errno;
407 t->retval = -1;
408 if (t != coop_global_curr)
409 coop_qput (&coop_global_runq, t);
410 }
411 collisionp = 0;
412 gnfds = 0;
413 FD_ZERO (&greadfds);
414 FD_ZERO (&gwritefds);
415 FD_ZERO (&gexceptfds);
416 }
417
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. */
421 static coop_t *
422 find_thread (int n, struct timeval *now, int sleepingp)
423 {
424 coop_t *t;
425 int fd;
426
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)
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);
445 if (collisionp)
446 finalize_fd_sets_lazily (t);
447 else
448 finalize_fd_sets (t);
449 coop_qput (&coop_global_runq, t);
450 }
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 }
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 {
476 if (collisionp)
477 finalize_fd_sets_lazily (t);
478 else
479 finalize_fd_sets (t);
480 coop_qput (&coop_global_runq, t);
481 }
482 else
483 coop_qput(&coop_tmp_queue, t);
484 }
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 }
504 }
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 */
513 coop_t *
514 coop_next_runnable_thread ()
515 {
516 coop_t *t;
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))
522 {
523 if (QEMPTYP (coop_global_runq))
524 return coop_global_curr;
525 else
526 return coop_qget (&coop_global_runq);
527 }
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);
536 t = find_thread (n, &now, 0);
537 }
538 else
539 t = find_thread (n, 0, 0);
540 return t == NULL ? coop_global_curr : t;
541 }
542
543 coop_t *
544 coop_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? */
554 t = find_thread (n, now, 1);
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);
573 t = find_thread (n, now, 1);
574 }
575
576 return t;
577 }
578
579 coop_t *
580 coop_wait_for_runnable_thread ()
581 {
582 struct timeval now;
583
584 if (QEMPTYP (coop_global_sleepq))
585 {
586 if (QEMPTYP (coop_global_runq))
587 return coop_global_curr;
588 else
589 return coop_qget (&coop_global_runq);
590 }
591
592 if (QFIRST (coop_global_sleepq)->timeoutp)
593 gettimeofday (&now, NULL);
594
595 return coop_wait_for_runnable_thread_now (&now);
596 }
597
598 /* Initialize bit counting array */
599 static 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
610 void
611 scm_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);
623 #include "iselect.x"
624 }
625
626 #endif /* GUILE_ISELECT */
627
628 int
629 scm_internal_select (int nfds,
630 SELECT_TYPE *readfds,
631 SELECT_TYPE *writefds,
632 SELECT_TYPE *exceptfds,
633 struct timeval *timeout)
634 {
635 #ifndef GUILE_ISELECT
636 int res = select (nfds, readfds, writefds, exceptfds, timeout);
637 SCM_ASYNC_TICK;
638 return res;
639 #else /* GUILE_ISELECT */
640 struct timeval now;
641 coop_t *t, *curr = coop_global_curr;
642
643 /* If the timeout is 0, we're polling and can handle it quickly. */
644 if (timeout != NULL
645 && timeout->tv_sec == 0
646 && timeout->tv_usec == 0)
647 return select (nfds, readfds, writefds, exceptfds, timeout);
648
649 SCM_DEFER_INTS;
650
651 /* Add our file descriptor flags to the common set. */
652 curr->nfds = nfds;
653 curr->readfds = readfds;
654 curr->writefds = writefds;
655 curr->exceptfds = exceptfds;
656 add_fd_sets (curr);
657
658 /* Place ourselves on the sleep queue and get a new thread to run. */
659 if (timeout == NULL)
660 {
661 curr->timeoutp = 0;
662 coop_qput (&coop_global_sleepq, curr);
663 t = coop_wait_for_runnable_thread ();
664 }
665 else
666 {
667 gettimeofday (&now, NULL);
668 curr->timeoutp = 1;
669 curr->wakeup_time.tv_sec = now.tv_sec + timeout->tv_sec;
670 curr->wakeup_time.tv_usec = now.tv_usec + timeout->tv_usec;
671 if (curr->wakeup_time.tv_usec >= 1000000)
672 {
673 ++curr->wakeup_time.tv_sec;
674 curr->wakeup_time.tv_usec -= 1000000;
675 }
676 /* Insert the current thread at the right place in the sleep queue */
677 coop_timeout_qinsert (&coop_global_sleepq, curr);
678 t = coop_wait_for_runnable_thread_now (&now);
679 }
680
681 /* If the new thread is the same as the sleeping thread, do nothing */
682 if (t != coop_global_curr)
683 {
684 /* Do a context switch. */
685 coop_global_curr = t;
686 QT_BLOCK (coop_sleephelp, curr, NULL, t->sp);
687 }
688
689 if (coop_global_curr->retval == -1)
690 errno = coop_global_curr->_errno;
691 SCM_ALLOW_INTS;
692 SCM_ASYNC_TICK;
693 return coop_global_curr->retval;
694 #endif /* GUILE_ISELECT */
695 }
696
697 /*
698 Local Variables:
699 c-file-style: "gnu"
700 End:
701 */