* coop-defs.h (struct timespec): Conditionally defined.
[bpt/guile.git] / libguile / coop.c
CommitLineData
df26ebfd 1/* Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
7bfd3b9e
JB
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
82892bed
JB
15 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
16 * Boston, MA 02111-1307 USA
7bfd3b9e
JB
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.
82892bed 40 * If you do not wish that, delete this exception notice. */
7bfd3b9e
JB
41\f
42
df26ebfd 43/* $Id: coop.c,v 1.17 2000-03-12 00:33:56 mdj Exp $ */
7bfd3b9e
JB
44
45/* Cooperative thread library, based on QuickThreads */
46
6d71500e
JB
47#ifdef HAVE_UNISTD_H
48#include <unistd.h>
49#endif
50
df26ebfd
MD
51#include <errno.h>
52
7bfd3b9e 53#include <qt.h>
51d394a1 54#include "eval.h"
7bfd3b9e 55
6d71500e 56\f/* #define COOP_STKSIZE (0x10000) */
a74145b8 57#define COOP_STKSIZE (scm_eval_stack)
7bfd3b9e
JB
58
59/* `alignment' must be a power of 2. */
60#define COOP_STKALIGN(sp, alignment) \
61((void *)((((qt_word_t)(sp)) + (alignment) - 1) & ~((alignment)-1)))
62
63\f
64
65/* Queue access functions. */
66
7bfd3b9e
JB
67static void
68coop_qinit (coop_q_t *q)
7bfd3b9e
JB
69{
70 q->t.next = q->tail = &q->t;
71
72 q->t.all_prev = NULL;
73 q->t.all_next = NULL;
44e8413c
MD
74#ifdef GUILE_ISELECT
75 q->t.nfds = 0;
76 q->t.readfds = NULL;
77 q->t.writefds = NULL;
78 q->t.exceptfds = NULL;
79 q->t.timeoutp = 0;
80#endif
7bfd3b9e
JB
81}
82
83
44e8413c 84coop_t *
7bfd3b9e 85coop_qget (coop_q_t *q)
7bfd3b9e
JB
86{
87 coop_t *t;
88
89 t = q->t.next;
90 q->t.next = t->next;
91 if (t->next == &q->t) {
92 if (t == &q->t) { /* If it was already empty .. */
93 return (NULL); /* .. say so. */
94 }
95 q->tail = &q->t; /* Else now it is empty. */
96 }
97 return (t);
98}
99
100
44e8413c 101void
7bfd3b9e 102coop_qput (coop_q_t *q, coop_t *t)
7bfd3b9e
JB
103{
104 q->tail->next = t;
105 t->next = &q->t;
106 q->tail = t;
107}
108
7bfd3b9e
JB
109static void
110coop_all_qput (coop_q_t *q, coop_t *t)
7bfd3b9e
JB
111{
112 if (q->t.all_next)
113 q->t.all_next->all_prev = t;
114 t->all_prev = NULL;
115 t->all_next = q->t.all_next;
116 q->t.all_next = t;
117}
118
7bfd3b9e
JB
119static void
120coop_all_qremove (coop_q_t *q, coop_t *t)
7bfd3b9e
JB
121{
122 if (t->all_prev)
123 t->all_prev->all_next = t->all_next;
124 else
125 q->t.all_next = t->all_next;
126 if (t->all_next)
127 t->all_next->all_prev = t->all_prev;
128}
129
df26ebfd
MD
130#ifdef GUILE_ISELECT
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. */
134void
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#endif
152
7bfd3b9e
JB
153
154\f/* Thread routines. */
155
44e8413c
MD
156coop_q_t coop_global_runq; /* A queue of runable threads. */
157coop_q_t coop_global_sleepq; /* A queue of sleeping threads. */
158coop_q_t coop_tmp_queue; /* A temp working queue */
159coop_q_t coop_global_allq; /* A queue of all threads. */
160static coop_t coop_global_main; /* Thread for the process. */
161coop_t *coop_global_curr; /* Currently-executing thread. */
7bfd3b9e
JB
162
163static void *coop_starthelp (qt_t *old, void *ignore0, void *ignore1);
164static void coop_only (void *pu, void *pt, qt_userf_t *f);
165static void *coop_aborthelp (qt_t *sp, void *old, void *null);
166static void *coop_yieldhelp (qt_t *sp, void *old, void *blockq);
167
168
7bfd3b9e
JB
169void
170coop_init()
7bfd3b9e
JB
171{
172 coop_qinit (&coop_global_runq);
173 coop_qinit (&coop_global_sleepq);
44e8413c 174 coop_qinit (&coop_tmp_queue);
7bfd3b9e
JB
175 coop_qinit (&coop_global_allq);
176 coop_global_curr = &coop_global_main;
177}
178
179
180/* Return the next runnable thread. If no threads are currently runnable,
181 and there are sleeping threads - wait until one wakes up. Otherwise,
182 return NULL. */
183
6d71500e 184#ifndef GUILE_ISELECT
7bfd3b9e
JB
185coop_t *
186coop_next_runnable_thread()
7bfd3b9e
JB
187{
188 int sleepers;
189 coop_t *t;
190 time_t now;
191
192 do {
193 sleepers = 0;
194 now = time(NULL);
195
196 /* Check the sleeping queue */
197 while ((t = coop_qget(&coop_global_sleepq)) != NULL)
198 {
199 sleepers++;
200 if (t->wakeup_time <= now)
201 coop_qput(&coop_global_runq, t);
202 else
44e8413c 203 coop_qput(&coop_tmp_queue, t);
7bfd3b9e 204 }
44e8413c 205 while ((t = coop_qget(&coop_tmp_queue)) != NULL)
7bfd3b9e
JB
206 coop_qput(&coop_global_sleepq, t);
207
208 t = coop_qget (&coop_global_runq);
209
210 } while ((t == NULL) && (sleepers > 0));
211
212 return t;
213}
44e8413c 214#endif
7bfd3b9e 215
7bfd3b9e
JB
216void
217coop_start()
7bfd3b9e
JB
218{
219 coop_t *next;
220
221 while ((next = coop_qget (&coop_global_runq)) != NULL) {
222 coop_global_curr = next;
223 QT_BLOCK (coop_starthelp, 0, 0, next->sp);
224 }
225}
226
227
7bfd3b9e
JB
228static void *
229coop_starthelp (qt_t *old, void *ignore0, void *ignore1)
7bfd3b9e
JB
230{
231 coop_global_main.sp = old;
232 coop_global_main.joining = NULL;
233 coop_qput (&coop_global_runq, &coop_global_main);
234 return NULL; /* not used, but keeps compiler happy */
235}
236
c8bf4ecd 237int
7bfd3b9e 238coop_mutex_init (coop_m *m)
df26ebfd
MD
239{
240 return coop_new_mutex_init (m, NULL);
241}
242
243int
244coop_new_mutex_init (coop_m *m, coop_mattr *attr)
7bfd3b9e
JB
245{
246 m->owner = NULL;
247 coop_qinit(&(m->waiting));
c8bf4ecd 248 return 0;
7bfd3b9e
JB
249}
250
df26ebfd
MD
251int
252coop_mutex_trylock (coop_m *m)
253{
254 if (m->owner == NULL)
255 {
256 m->owner = coop_global_curr;
257 return 0;
258 }
259 else
260 return EBUSY;
261}
262
c8bf4ecd 263int
7bfd3b9e 264coop_mutex_lock (coop_m *m)
7bfd3b9e
JB
265{
266 if (m->owner == NULL)
267 {
268 m->owner = coop_global_curr;
269 }
270 else
271 {
272 coop_t *old, *newthread;
273
274 /* Record the current top-of-stack before going to sleep */
275 coop_global_curr->top = &old;
276
44e8413c
MD
277#ifdef GUILE_ISELECT
278 newthread = coop_wait_for_runnable_thread();
d186aac6
MD
279 if (newthread == coop_global_curr)
280 coop_abort ();
44e8413c 281#else
7bfd3b9e 282 newthread = coop_next_runnable_thread();
44e8413c 283#endif
7bfd3b9e
JB
284 old = coop_global_curr;
285 coop_global_curr = newthread;
286 QT_BLOCK (coop_yieldhelp, old, &(m->waiting), newthread->sp);
287 }
c8bf4ecd 288 return 0;
7bfd3b9e
JB
289}
290
291
c8bf4ecd 292int
7bfd3b9e 293coop_mutex_unlock (coop_m *m)
7bfd3b9e
JB
294{
295 coop_t *old, *newthread;
296
297 newthread = coop_qget (&(m->waiting));
298 if (newthread != NULL)
299 {
300 /* Record the current top-of-stack before going to sleep */
301 coop_global_curr->top = &old;
302
303 old = coop_global_curr;
304 coop_global_curr = newthread;
b322f09a
MD
305 /* The new thread came into m->waiting through a lock operation.
306 It now owns this mutex. */
7bfd3b9e
JB
307 m->owner = coop_global_curr;
308 QT_BLOCK (coop_yieldhelp, old, &coop_global_runq, newthread->sp);
309 }
310 else
311 {
312 m->owner = NULL;
313 }
c8bf4ecd 314 return 0;
7bfd3b9e
JB
315}
316
317
c8bf4ecd
MD
318int
319coop_mutex_destroy (coop_m *m)
c8bf4ecd
MD
320{
321 return 0;
322}
323
324
c8bf4ecd 325int
7bfd3b9e 326coop_condition_variable_init (coop_c *c)
df26ebfd
MD
327{
328 return coop_new_condition_variable_init (c, NULL);
329}
330
331int
332coop_new_condition_variable_init (coop_c *c, coop_cattr *a)
7bfd3b9e
JB
333{
334 coop_qinit(&(c->waiting));
c8bf4ecd 335 return 0;
7bfd3b9e
JB
336}
337
b322f09a
MD
338int
339coop_condition_variable_wait_mutex (coop_c *c, coop_m *m)
7bfd3b9e
JB
340{
341 coop_t *old, *newthread;
342
b322f09a
MD
343 /* coop_mutex_unlock (m); */
344 newthread = coop_qget (&(m->waiting));
345 if (newthread != NULL)
346 {
347 m->owner = newthread;
348 }
349 else
350 {
351 m->owner = NULL;
df26ebfd 352 /*fixme* Should we really wait here? Isn't it OK just to proceed? */
44e8413c 353#ifdef GUILE_ISELECT
b322f09a
MD
354 newthread = coop_wait_for_runnable_thread();
355 if (newthread == coop_global_curr)
356 coop_abort ();
44e8413c 357#else
b322f09a 358 newthread = coop_next_runnable_thread();
44e8413c 359#endif
b322f09a
MD
360 }
361 coop_global_curr->top = &old;
7bfd3b9e
JB
362 old = coop_global_curr;
363 coop_global_curr = newthread;
364 QT_BLOCK (coop_yieldhelp, old, &(c->waiting), newthread->sp);
c8bf4ecd 365
c8bf4ecd
MD
366 coop_mutex_lock (m);
367 return 0;
7bfd3b9e
JB
368}
369
df26ebfd
MD
370int
371coop_condition_variable_timed_wait_mutex (coop_c *c,
372 coop_m *m,
373 const struct timespec *abstime)
374{
375 coop_t *old, *t;
376 int res = ETIME;
377
378 /* coop_mutex_unlock (m); */
379 t = coop_qget (&(m->waiting));
380 if (t != NULL)
381 {
382 m->owner = t;
383 }
384 else
385 {
386 m->owner = NULL;
387#ifdef GUILE_ISELECT
388 coop_global_curr->timeoutp = 1;
389 coop_global_curr->wakeup_time.tv_sec = abstime->tv_sec;
390 coop_global_curr->wakeup_time.tv_usec = abstime->tv_nsec / 1000;
391 coop_timeout_qinsert (&coop_global_sleepq, coop_global_curr);
392 t = coop_wait_for_runnable_thread();
393#else
394 /*fixme* Implement!*/
395 t = coop_next_runnable_thread();
396#endif
397 }
398 if (t != coop_global_curr)
399 {
400 coop_global_curr->top = &old;
401 old = coop_global_curr;
402 coop_global_curr = t;
403 QT_BLOCK (coop_yieldhelp, old, &(c->waiting), t->sp);
404
405 /* Are we still in the sleep queue? */
406 old = &coop_global_sleepq.t;
407 for (t = old->next; t != &coop_global_sleepq.t; old = t, t = t->next)
408 if (t == coop_global_curr)
409 {
410 old->next = t->next; /* unlink */
411 res = 0;
412 break;
413 }
414 }
415 coop_mutex_lock (m);
416 return res;
417}
c8bf4ecd 418
c8bf4ecd 419int
7bfd3b9e 420coop_condition_variable_signal (coop_c *c)
7bfd3b9e
JB
421{
422 coop_t *newthread;
423
424 while ((newthread = coop_qget (&(c->waiting))) != NULL)
425 {
426 coop_qput (&coop_global_runq, newthread);
427 }
c8bf4ecd
MD
428 return 0;
429}
430
df26ebfd
MD
431/* {Keys}
432 */
433
434static int n_keys = 0;
435static int max_keys = 0;
436static void (**destructors) (void *) = 0;
437
438int
439coop_key_create (coop_k *keyp, void (*destructor) (void *value))
440{
441 if (n_keys >= max_keys)
442 {
443 int i;
444 max_keys = max_keys ? max_keys * 3 / 2 : 10;
445 destructors = realloc (destructors, sizeof (void *) * max_keys);
446 if (destructors == 0)
447 {
448 fprintf (stderr, "Virtual memory exceeded in coop_key_create\n");
449 exit (1);
450 }
451 for (i = n_keys; i < max_keys; ++i)
452 destructors[i] = NULL;
453 }
454 destructors[n_keys] = destructor;
455 *keyp = n_keys++;
456 return 0;
457}
458
459int
460coop_setspecific (coop_k key, const void *value)
461{
462 int n_keys = coop_global_curr->n_keys;
463 if (key >= n_keys)
464 {
465 int i;
466 coop_global_curr->n_keys = max_keys;
467 coop_global_curr->specific = realloc (n_keys
468 ? coop_global_curr->specific
469 : NULL,
470 sizeof (void *) * max_keys);
471 if (coop_global_curr->specific == 0)
472 {
473 fprintf (stderr, "Virtual memory exceeded in coop_setspecific\n");
474 exit (1);
475 }
476 for (i = n_keys; i < max_keys; ++i)
477 coop_global_curr->specific[i] = NULL;
478 }
479 coop_global_curr->specific[key] = (void *) value;
480 return 0;
481}
482
483void *
484coop_getspecific (coop_k key)
485{
486 return (key < coop_global_curr->n_keys
487 ? coop_global_curr->specific[key]
488 : NULL);
489}
490
491int
492coop_key_delete (coop_k key)
493{
494 return 0;
495}
496
c8bf4ecd 497
c8bf4ecd
MD
498int
499coop_condition_variable_destroy (coop_c *c)
c8bf4ecd
MD
500{
501 return 0;
7bfd3b9e
JB
502}
503
df26ebfd
MD
504#ifdef GUILE_PTHREAD_COMPAT
505static void *
506dummy_start (void *coop_thread)
507{
508 coop_t *t = (coop_t *) coop_thread;
509 t->sto = &t + 1;
510 pthread_mutex_init (&t->dummy_mutex, NULL);
511 pthread_mutex_lock (&t->dummy_mutex);
512 pthread_cond_init (&t->dummy_cond, NULL);
513 pthread_cond_wait (&t->dummy_cond, &t->dummy_mutex);
514 return 0;
515}
516#endif
7bfd3b9e 517
7bfd3b9e
JB
518coop_t *
519coop_create (coop_userf_t *f, void *pu)
7bfd3b9e
JB
520{
521 coop_t *t;
522 void *sto;
523
524 t = malloc (sizeof(coop_t));
525
526 t->data = NULL;
df26ebfd
MD
527 t->n_keys = 0;
528#ifdef GUILE_PTHREAD_COMPAT
529 pthread_create (&t->dummy_thread, NULL, dummy_start, t);
530#else
7bfd3b9e 531 t->sto = malloc (COOP_STKSIZE);
df26ebfd 532#endif
7bfd3b9e
JB
533 sto = COOP_STKALIGN (t->sto, QT_STKALIGN);
534 t->sp = QT_SP (sto, COOP_STKSIZE - QT_STKALIGN);
535 t->base = t->sp;
536 t->sp = QT_ARGS (t->sp, pu, t, (qt_userf_t *)f, coop_only);
537 t->joining = NULL;
538 coop_qput (&coop_global_runq, t);
539 coop_all_qput (&coop_global_allq, t);
540
541 return t;
542}
543
544
7bfd3b9e
JB
545static void
546coop_only (void *pu, void *pt, qt_userf_t *f)
7bfd3b9e
JB
547{
548 coop_global_curr = (coop_t *)pt;
549 (*(coop_userf_t *)f)(pu);
550 coop_abort();
551 /* NOTREACHED */
552}
553
554
7bfd3b9e
JB
555void
556coop_abort ()
7bfd3b9e
JB
557{
558 coop_t *old, *newthread;
559
560 /* Wake up any threads that are waiting to join this one */
561 if (coop_global_curr->joining)
562 {
563 while ((newthread = coop_qget ((coop_q_t *)(coop_global_curr->joining)))
564 != NULL)
565 {
566 coop_qput (&coop_global_runq, newthread);
567 }
568 free(coop_global_curr->joining);
569 }
570
44e8413c 571#ifdef GUILE_ISELECT
d186aac6
MD
572 scm_I_am_dead = 1;
573 do {
574 newthread = coop_wait_for_runnable_thread();
575 } while (newthread == coop_global_curr);
576 scm_I_am_dead = 0;
44e8413c 577#else
7bfd3b9e 578 newthread = coop_next_runnable_thread();
44e8413c 579#endif
7bfd3b9e
JB
580 coop_all_qremove(&coop_global_allq, coop_global_curr);
581 old = coop_global_curr;
582 coop_global_curr = newthread;
df26ebfd
MD
583#ifdef GUILE_PTHREAD_COMPAT
584 pthread_cond_signal (&old->dummy_cond);
585#endif
7bfd3b9e
JB
586 QT_ABORT (coop_aborthelp, old, (void *)NULL, newthread->sp);
587}
588
589
7bfd3b9e
JB
590static void *
591coop_aborthelp (qt_t *sp, void *old, void *null)
7bfd3b9e
JB
592{
593 coop_t *oldthread = (coop_t *) old;
594
595 free (oldthread->sto);
596
597 /* "old" is freed in scm_threads_thread_die().
598 Marking old->base NULL indicates that this thread is dead */
599
600 oldthread->base = NULL;
601
602 return NULL;
603}
604
605
7bfd3b9e
JB
606void
607coop_join(coop_t *t)
7bfd3b9e
JB
608{
609 coop_t *old, *newthread;
610
611 /* Check if t is already finished */
612 if (t->base == NULL)
613 return;
614
615 /* Create a join list if necessary */
616 if (t->joining == NULL)
617 {
618 t->joining = malloc(sizeof(coop_q_t));
619 coop_qinit((coop_q_t *) t->joining);
620 }
621
44e8413c
MD
622#ifdef GUILE_ISELECT
623 newthread = coop_wait_for_runnable_thread();
d186aac6
MD
624 if (newthread == coop_global_curr)
625 return;
44e8413c 626#else
7bfd3b9e 627 newthread = coop_next_runnable_thread();
44e8413c 628#endif
7bfd3b9e
JB
629 old = coop_global_curr;
630 coop_global_curr = newthread;
631 QT_BLOCK (coop_yieldhelp, old, (coop_q_t *) t->joining, newthread->sp);
632}
633
7bfd3b9e
JB
634void
635coop_yield()
7bfd3b9e
JB
636{
637 coop_t *old = NULL;
638 coop_t *newthread;
639
640 newthread = coop_next_runnable_thread();
641
642 /* There may be no other runnable threads. Return if this is the
643 case. */
d186aac6
MD
644#if GUILE_ISELECT
645 if (newthread == coop_global_curr)
646 return;
647#else
7bfd3b9e
JB
648 if (newthread == NULL)
649 return;
d186aac6 650#endif
7bfd3b9e
JB
651
652 old = coop_global_curr;
653
654 coop_global_curr = newthread;
655 QT_BLOCK (coop_yieldhelp, old, &coop_global_runq, newthread->sp);
656}
657
658
7bfd3b9e
JB
659static void *
660coop_yieldhelp (qt_t *sp, void *old, void *blockq)
7bfd3b9e
JB
661{
662 ((coop_t *)old)->sp = sp;
663 coop_qput ((coop_q_t *)blockq, (coop_t *)old);
664 return NULL;
665}
666
667/* Replacement for the system's sleep() function. Does the right thing
668 for the process - but not for the system (it busy-waits) */
669
44e8413c 670void *
7bfd3b9e 671coop_sleephelp (qt_t *sp, void *old, void *blockq)
7bfd3b9e
JB
672{
673 ((coop_t *)old)->sp = sp;
674 /* old is already on the sleep queue - so there's no need to
675 do anything extra here */
676 return NULL;
677}
678
44e8413c
MD
679#ifdef GUILE_ISELECT
680
6aa9316d
JB
681unsigned long
682scm_thread_usleep (unsigned long usec)
44e8413c
MD
683{
684 struct timeval timeout;
685 timeout.tv_sec = 0;
686 timeout.tv_usec = usec;
687 scm_internal_select (0, NULL, NULL, NULL, &timeout);
2c4e1a34
MD
688 return 0; /* Maybe we should calculate actual time slept,
689 but this is faster... :) */
44e8413c
MD
690}
691
6aa9316d
JB
692unsigned long
693scm_thread_sleep (unsigned long sec)
44e8413c
MD
694{
695 time_t now = time (NULL);
696 struct timeval timeout;
6aa9316d 697 unsigned long slept;
44e8413c
MD
698 timeout.tv_sec = sec;
699 timeout.tv_usec = 0;
700 scm_internal_select (0, NULL, NULL, NULL, &timeout);
701 slept = time (NULL) - now;
702 return slept > sec ? 0 : sec - slept;
703}
704
705#else /* GUILE_ISELECT */
706
6aa9316d
JB
707unsigned long
708scm_thread_sleep (unsigned long s)
7bfd3b9e
JB
709{
710 coop_t *newthread, *old;
44e8413c 711 time_t now = time (NULL);
7bfd3b9e
JB
712 coop_global_curr->wakeup_time = now + s;
713
714 /* Put the current thread on the sleep queue */
715 coop_qput (&coop_global_sleepq, coop_global_curr);
716
717 newthread = coop_next_runnable_thread();
718
719 /* If newthread is the same as the sleeping thread, do nothing */
720 if (newthread == coop_global_curr)
721 return s;
722
723 old = coop_global_curr;
724
725 coop_global_curr = newthread;
726 QT_BLOCK (coop_sleephelp, old, NULL, newthread->sp);
727
728 return s;
729}
44e8413c 730
6aa9316d
JB
731unsigned long
732scm_thread_usleep (unsigned long usec)
733{
734 /* We're so cheap. */
735 scm_thread_sleep (usec / 1000000);
736 struct timeval timeout;
737 return 0; /* Maybe we should calculate actual time slept,
738 but this is faster... :) */
739}
740
44e8413c 741#endif /* GUILE_ISELECT */