* coop-defs.h (struct coop_t): Removed dummy_mutex.
[bpt/guile.git] / libguile / coop.c
CommitLineData
bc5329cc 1/* Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000 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
ac1aca35 43/* $Id: coop.c,v 1.21 2000-03-29 10:43:18 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;
f85a9bcf
MD
91 if (t->next == &q->t)
92 {
93 if (t == &q->t)
94 { /* If it was already empty .. */
95 return NULL; /* .. say so. */
96 }
97 q->tail = &q->t; /* Else now it is empty. */
7bfd3b9e 98 }
7bfd3b9e
JB
99 return (t);
100}
101
102
44e8413c 103void
7bfd3b9e 104coop_qput (coop_q_t *q, coop_t *t)
7bfd3b9e
JB
105{
106 q->tail->next = t;
107 t->next = &q->t;
108 q->tail = t;
109}
110
7bfd3b9e
JB
111static void
112coop_all_qput (coop_q_t *q, coop_t *t)
7bfd3b9e
JB
113{
114 if (q->t.all_next)
115 q->t.all_next->all_prev = t;
116 t->all_prev = NULL;
117 t->all_next = q->t.all_next;
118 q->t.all_next = t;
119}
120
7bfd3b9e
JB
121static void
122coop_all_qremove (coop_q_t *q, coop_t *t)
7bfd3b9e
JB
123{
124 if (t->all_prev)
125 t->all_prev->all_next = t->all_next;
126 else
127 q->t.all_next = t->all_next;
128 if (t->all_next)
129 t->all_next->all_prev = t->all_prev;
130}
131
df26ebfd
MD
132#ifdef GUILE_ISELECT
133/* Insert thread t into the ordered queue q.
134 q is ordered after wakeup_time. Threads which aren't sleeping but
135 waiting for I/O go last into the queue. */
136void
137coop_timeout_qinsert (coop_q_t *q, coop_t *t)
138{
139 coop_t *pred = &q->t;
140 int sec = t->wakeup_time.tv_sec;
141 int usec = t->wakeup_time.tv_usec;
142 while (pred->next != &q->t
143 && pred->next->timeoutp
144 && (pred->next->wakeup_time.tv_sec < sec
145 || (pred->next->wakeup_time.tv_sec == sec
146 && pred->next->wakeup_time.tv_usec < usec)))
147 pred = pred->next;
148 t->next = pred->next;
149 pred->next = t;
150 if (t->next == &q->t)
151 q->tail = t;
152}
153#endif
154
7bfd3b9e
JB
155
156\f/* Thread routines. */
157
44e8413c
MD
158coop_q_t coop_global_runq; /* A queue of runable threads. */
159coop_q_t coop_global_sleepq; /* A queue of sleeping threads. */
160coop_q_t coop_tmp_queue; /* A temp working queue */
161coop_q_t coop_global_allq; /* A queue of all threads. */
162static coop_t coop_global_main; /* Thread for the process. */
163coop_t *coop_global_curr; /* Currently-executing thread. */
7bfd3b9e 164
f85a9bcf
MD
165#ifdef GUILE_PTHREAD_COMPAT
166static coop_q_t coop_deadq;
167pthread_cond_t coop_cond_quit;
168#endif
169
7bfd3b9e
JB
170static void *coop_starthelp (qt_t *old, void *ignore0, void *ignore1);
171static void coop_only (void *pu, void *pt, qt_userf_t *f);
172static void *coop_aborthelp (qt_t *sp, void *old, void *null);
173static void *coop_yieldhelp (qt_t *sp, void *old, void *blockq);
174
175
f85a9bcf
MD
176/* called on process termination. */
177#ifdef HAVE_ATEXIT
178static void
179coop_finish (void)
180#else
181#ifdef HAVE_ON_EXIT
182extern int on_exit (void (*procp) (), int arg);
183
184static void
185coop_finish (int status, void *arg)
186#else
187#error Dont know how to setup a cleanup handler on your system.
188#endif
189#endif
190{
191#ifdef GUILE_PTHREAD_COMPAT
192 pthread_cond_broadcast (&coop_cond_quit);
193#endif
194}
195
7bfd3b9e 196void
f85a9bcf 197coop_init ()
7bfd3b9e
JB
198{
199 coop_qinit (&coop_global_runq);
200 coop_qinit (&coop_global_sleepq);
44e8413c 201 coop_qinit (&coop_tmp_queue);
7bfd3b9e
JB
202 coop_qinit (&coop_global_allq);
203 coop_global_curr = &coop_global_main;
f85a9bcf
MD
204#ifdef GUILE_PTHREAD_COMPAT
205 coop_qinit (&coop_deadq);
206 pthread_cond_init (&coop_cond_quit, NULL);
207#endif
208#ifdef HAVE_ATEXIT
209 atexit (coop_finish);
210#else
211#ifdef HAVE_ON_EXIT
212 on_exit (coop_finish, 0);
213#endif
214#endif
7bfd3b9e
JB
215}
216
7bfd3b9e
JB
217/* Return the next runnable thread. If no threads are currently runnable,
218 and there are sleeping threads - wait until one wakes up. Otherwise,
219 return NULL. */
220
6d71500e 221#ifndef GUILE_ISELECT
7bfd3b9e
JB
222coop_t *
223coop_next_runnable_thread()
7bfd3b9e
JB
224{
225 int sleepers;
226 coop_t *t;
227 time_t now;
228
229 do {
230 sleepers = 0;
231 now = time(NULL);
232
233 /* Check the sleeping queue */
234 while ((t = coop_qget(&coop_global_sleepq)) != NULL)
235 {
236 sleepers++;
237 if (t->wakeup_time <= now)
238 coop_qput(&coop_global_runq, t);
239 else
44e8413c 240 coop_qput(&coop_tmp_queue, t);
7bfd3b9e 241 }
44e8413c 242 while ((t = coop_qget(&coop_tmp_queue)) != NULL)
7bfd3b9e
JB
243 coop_qput(&coop_global_sleepq, t);
244
245 t = coop_qget (&coop_global_runq);
246
247 } while ((t == NULL) && (sleepers > 0));
248
249 return t;
250}
44e8413c 251#endif
7bfd3b9e 252
7bfd3b9e
JB
253void
254coop_start()
7bfd3b9e
JB
255{
256 coop_t *next;
257
258 while ((next = coop_qget (&coop_global_runq)) != NULL) {
259 coop_global_curr = next;
260 QT_BLOCK (coop_starthelp, 0, 0, next->sp);
261 }
262}
263
264
7bfd3b9e
JB
265static void *
266coop_starthelp (qt_t *old, void *ignore0, void *ignore1)
7bfd3b9e
JB
267{
268 coop_global_main.sp = old;
269 coop_global_main.joining = NULL;
270 coop_qput (&coop_global_runq, &coop_global_main);
271 return NULL; /* not used, but keeps compiler happy */
272}
273
c8bf4ecd 274int
7bfd3b9e 275coop_mutex_init (coop_m *m)
df26ebfd
MD
276{
277 return coop_new_mutex_init (m, NULL);
278}
279
280int
281coop_new_mutex_init (coop_m *m, coop_mattr *attr)
7bfd3b9e
JB
282{
283 m->owner = NULL;
284 coop_qinit(&(m->waiting));
c8bf4ecd 285 return 0;
7bfd3b9e
JB
286}
287
df26ebfd
MD
288int
289coop_mutex_trylock (coop_m *m)
290{
291 if (m->owner == NULL)
292 {
293 m->owner = coop_global_curr;
294 return 0;
295 }
296 else
297 return EBUSY;
298}
299
c8bf4ecd 300int
7bfd3b9e 301coop_mutex_lock (coop_m *m)
7bfd3b9e
JB
302{
303 if (m->owner == NULL)
304 {
305 m->owner = coop_global_curr;
306 }
307 else
308 {
309 coop_t *old, *newthread;
310
311 /* Record the current top-of-stack before going to sleep */
312 coop_global_curr->top = &old;
313
44e8413c
MD
314#ifdef GUILE_ISELECT
315 newthread = coop_wait_for_runnable_thread();
d186aac6
MD
316 if (newthread == coop_global_curr)
317 coop_abort ();
44e8413c 318#else
7bfd3b9e 319 newthread = coop_next_runnable_thread();
44e8413c 320#endif
7bfd3b9e
JB
321 old = coop_global_curr;
322 coop_global_curr = newthread;
323 QT_BLOCK (coop_yieldhelp, old, &(m->waiting), newthread->sp);
324 }
c8bf4ecd 325 return 0;
7bfd3b9e
JB
326}
327
328
c8bf4ecd 329int
7bfd3b9e 330coop_mutex_unlock (coop_m *m)
7bfd3b9e
JB
331{
332 coop_t *old, *newthread;
333
334 newthread = coop_qget (&(m->waiting));
335 if (newthread != NULL)
336 {
337 /* Record the current top-of-stack before going to sleep */
338 coop_global_curr->top = &old;
339
340 old = coop_global_curr;
341 coop_global_curr = newthread;
b322f09a
MD
342 /* The new thread came into m->waiting through a lock operation.
343 It now owns this mutex. */
7bfd3b9e
JB
344 m->owner = coop_global_curr;
345 QT_BLOCK (coop_yieldhelp, old, &coop_global_runq, newthread->sp);
346 }
347 else
348 {
349 m->owner = NULL;
350 }
c8bf4ecd 351 return 0;
7bfd3b9e
JB
352}
353
354
c8bf4ecd
MD
355int
356coop_mutex_destroy (coop_m *m)
c8bf4ecd
MD
357{
358 return 0;
359}
360
361
c8bf4ecd 362int
7bfd3b9e 363coop_condition_variable_init (coop_c *c)
df26ebfd
MD
364{
365 return coop_new_condition_variable_init (c, NULL);
366}
367
368int
369coop_new_condition_variable_init (coop_c *c, coop_cattr *a)
7bfd3b9e
JB
370{
371 coop_qinit(&(c->waiting));
c8bf4ecd 372 return 0;
7bfd3b9e
JB
373}
374
b322f09a
MD
375int
376coop_condition_variable_wait_mutex (coop_c *c, coop_m *m)
7bfd3b9e
JB
377{
378 coop_t *old, *newthread;
379
b322f09a
MD
380 /* coop_mutex_unlock (m); */
381 newthread = coop_qget (&(m->waiting));
382 if (newthread != NULL)
383 {
384 m->owner = newthread;
385 }
386 else
387 {
388 m->owner = NULL;
df26ebfd 389 /*fixme* Should we really wait here? Isn't it OK just to proceed? */
44e8413c 390#ifdef GUILE_ISELECT
b322f09a
MD
391 newthread = coop_wait_for_runnable_thread();
392 if (newthread == coop_global_curr)
393 coop_abort ();
44e8413c 394#else
b322f09a 395 newthread = coop_next_runnable_thread();
44e8413c 396#endif
b322f09a
MD
397 }
398 coop_global_curr->top = &old;
7bfd3b9e
JB
399 old = coop_global_curr;
400 coop_global_curr = newthread;
401 QT_BLOCK (coop_yieldhelp, old, &(c->waiting), newthread->sp);
c8bf4ecd 402
c8bf4ecd
MD
403 coop_mutex_lock (m);
404 return 0;
7bfd3b9e
JB
405}
406
df26ebfd
MD
407int
408coop_condition_variable_timed_wait_mutex (coop_c *c,
409 coop_m *m,
410 const struct timespec *abstime)
411{
412 coop_t *old, *t;
bc5329cc 413 int res = ETIMEDOUT;
df26ebfd
MD
414
415 /* coop_mutex_unlock (m); */
416 t = coop_qget (&(m->waiting));
417 if (t != NULL)
418 {
419 m->owner = t;
420 }
421 else
422 {
423 m->owner = NULL;
424#ifdef GUILE_ISELECT
425 coop_global_curr->timeoutp = 1;
426 coop_global_curr->wakeup_time.tv_sec = abstime->tv_sec;
427 coop_global_curr->wakeup_time.tv_usec = abstime->tv_nsec / 1000;
428 coop_timeout_qinsert (&coop_global_sleepq, coop_global_curr);
429 t = coop_wait_for_runnable_thread();
430#else
431 /*fixme* Implement!*/
432 t = coop_next_runnable_thread();
433#endif
434 }
435 if (t != coop_global_curr)
436 {
437 coop_global_curr->top = &old;
438 old = coop_global_curr;
439 coop_global_curr = t;
440 QT_BLOCK (coop_yieldhelp, old, &(c->waiting), t->sp);
441
442 /* Are we still in the sleep queue? */
443 old = &coop_global_sleepq.t;
444 for (t = old->next; t != &coop_global_sleepq.t; old = t, t = t->next)
445 if (t == coop_global_curr)
446 {
447 old->next = t->next; /* unlink */
448 res = 0;
449 break;
450 }
451 }
452 coop_mutex_lock (m);
453 return res;
454}
c8bf4ecd 455
c8bf4ecd 456int
7bfd3b9e 457coop_condition_variable_signal (coop_c *c)
7bfd3b9e
JB
458{
459 coop_t *newthread;
460
461 while ((newthread = coop_qget (&(c->waiting))) != NULL)
462 {
463 coop_qput (&coop_global_runq, newthread);
464 }
c8bf4ecd
MD
465 return 0;
466}
467
df26ebfd
MD
468/* {Keys}
469 */
470
471static int n_keys = 0;
472static int max_keys = 0;
473static void (**destructors) (void *) = 0;
474
475int
476coop_key_create (coop_k *keyp, void (*destructor) (void *value))
477{
478 if (n_keys >= max_keys)
479 {
480 int i;
481 max_keys = max_keys ? max_keys * 3 / 2 : 10;
482 destructors = realloc (destructors, sizeof (void *) * max_keys);
483 if (destructors == 0)
484 {
485 fprintf (stderr, "Virtual memory exceeded in coop_key_create\n");
486 exit (1);
487 }
488 for (i = n_keys; i < max_keys; ++i)
489 destructors[i] = NULL;
490 }
491 destructors[n_keys] = destructor;
492 *keyp = n_keys++;
493 return 0;
494}
495
496int
497coop_setspecific (coop_k key, const void *value)
498{
499 int n_keys = coop_global_curr->n_keys;
500 if (key >= n_keys)
501 {
502 int i;
503 coop_global_curr->n_keys = max_keys;
504 coop_global_curr->specific = realloc (n_keys
505 ? coop_global_curr->specific
506 : NULL,
507 sizeof (void *) * max_keys);
508 if (coop_global_curr->specific == 0)
509 {
510 fprintf (stderr, "Virtual memory exceeded in coop_setspecific\n");
511 exit (1);
512 }
513 for (i = n_keys; i < max_keys; ++i)
514 coop_global_curr->specific[i] = NULL;
515 }
516 coop_global_curr->specific[key] = (void *) value;
517 return 0;
518}
519
520void *
521coop_getspecific (coop_k key)
522{
523 return (key < coop_global_curr->n_keys
524 ? coop_global_curr->specific[key]
525 : NULL);
526}
527
528int
529coop_key_delete (coop_k key)
530{
531 return 0;
532}
533
c8bf4ecd 534
c8bf4ecd
MD
535int
536coop_condition_variable_destroy (coop_c *c)
c8bf4ecd
MD
537{
538 return 0;
7bfd3b9e
JB
539}
540
df26ebfd 541#ifdef GUILE_PTHREAD_COMPAT
f85a9bcf
MD
542
543/* 1K room for the cond wait routine */
544#ifdef SCM_STACK_GROWS_UP
ac1aca35 545#define COOP_STACK_ROOM (256)
f85a9bcf 546#else
ac1aca35 547#define COOP_STACK_ROOM (-256)
f85a9bcf
MD
548#endif
549
df26ebfd
MD
550static void *
551dummy_start (void *coop_thread)
552{
553 coop_t *t = (coop_t *) coop_thread;
f85a9bcf 554 t->sp = (qt_t *) (&t + COOP_STACK_ROOM);
df26ebfd 555 pthread_mutex_lock (&t->dummy_mutex);
f85a9bcf
MD
556 pthread_cond_signal (&t->dummy_cond);
557 pthread_cond_wait (&coop_cond_quit, &t->dummy_mutex);
df26ebfd
MD
558 return 0;
559}
560#endif
7bfd3b9e 561
7bfd3b9e
JB
562coop_t *
563coop_create (coop_userf_t *f, void *pu)
7bfd3b9e
JB
564{
565 coop_t *t;
f85a9bcf 566#ifndef GUILE_PTHREAD_COMPAT
7bfd3b9e 567 void *sto;
f85a9bcf 568#endif
7bfd3b9e 569
f85a9bcf
MD
570#ifdef GUILE_PTHREAD_COMPAT
571 t = coop_qget (&coop_deadq);
572 if (t)
ac1aca35
MD
573 {
574 t->sp = t->base;
575 t->specific = 0;
576 t->n_keys = 0;
577 }
f85a9bcf
MD
578 else
579#endif
580 {
581 t = malloc (sizeof (coop_t));
7bfd3b9e 582
ac1aca35 583 t->specific = NULL;
f85a9bcf 584 t->n_keys = 0;
df26ebfd 585#ifdef GUILE_PTHREAD_COMPAT
f85a9bcf
MD
586 pthread_cond_init (&t->dummy_cond, NULL);
587 pthread_mutex_init (&t->dummy_mutex, NULL);
588 pthread_mutex_lock (&t->dummy_mutex);
589 pthread_create (&t->dummy_thread, NULL, dummy_start, t);
590 pthread_cond_wait (&t->dummy_cond, &t->dummy_mutex);
591 pthread_mutex_unlock (&t->dummy_mutex);
df26ebfd 592#else
f85a9bcf
MD
593 t->sto = malloc (COOP_STKSIZE);
594 sto = COOP_STKALIGN (t->sto, QT_STKALIGN);
595 t->sp = QT_SP (sto, COOP_STKSIZE - QT_STKALIGN);
df26ebfd 596#endif
f85a9bcf
MD
597 t->base = t->sp;
598 }
7bfd3b9e
JB
599 t->sp = QT_ARGS (t->sp, pu, t, (qt_userf_t *)f, coop_only);
600 t->joining = NULL;
601 coop_qput (&coop_global_runq, t);
602 coop_all_qput (&coop_global_allq, t);
603
604 return t;
605}
606
607
7bfd3b9e
JB
608static void
609coop_only (void *pu, void *pt, qt_userf_t *f)
7bfd3b9e
JB
610{
611 coop_global_curr = (coop_t *)pt;
612 (*(coop_userf_t *)f)(pu);
613 coop_abort();
614 /* NOTREACHED */
615}
616
617
7bfd3b9e
JB
618void
619coop_abort ()
7bfd3b9e
JB
620{
621 coop_t *old, *newthread;
622
623 /* Wake up any threads that are waiting to join this one */
624 if (coop_global_curr->joining)
625 {
626 while ((newthread = coop_qget ((coop_q_t *)(coop_global_curr->joining)))
627 != NULL)
628 {
629 coop_qput (&coop_global_runq, newthread);
630 }
f85a9bcf 631 free (coop_global_curr->joining);
7bfd3b9e
JB
632 }
633
44e8413c 634#ifdef GUILE_ISELECT
d186aac6
MD
635 scm_I_am_dead = 1;
636 do {
637 newthread = coop_wait_for_runnable_thread();
638 } while (newthread == coop_global_curr);
639 scm_I_am_dead = 0;
44e8413c 640#else
7bfd3b9e 641 newthread = coop_next_runnable_thread();
44e8413c 642#endif
f85a9bcf 643 coop_all_qremove (&coop_global_allq, coop_global_curr);
7bfd3b9e
JB
644 old = coop_global_curr;
645 coop_global_curr = newthread;
f85a9bcf 646 QT_ABORT (coop_aborthelp, old, (void *) NULL, newthread->sp);
7bfd3b9e
JB
647}
648
649
7bfd3b9e
JB
650static void *
651coop_aborthelp (qt_t *sp, void *old, void *null)
7bfd3b9e
JB
652{
653 coop_t *oldthread = (coop_t *) old;
654
f85a9bcf
MD
655#if 0
656 /* Marking old->base NULL indicates that this thread is dead */
7bfd3b9e 657 oldthread->base = NULL;
f85a9bcf 658#endif
7bfd3b9e 659
ac1aca35
MD
660 if (oldthread->specific)
661 free (oldthread->specific);
f85a9bcf
MD
662#ifndef GUILE_PTHREAD_COMPAT
663 free (oldthread->sto);
664 free (oldthread);
665#else
f85a9bcf
MD
666 coop_qput (&coop_deadq, oldthread);
667#endif
668
7bfd3b9e
JB
669 return NULL;
670}
671
672
7bfd3b9e
JB
673void
674coop_join(coop_t *t)
7bfd3b9e
JB
675{
676 coop_t *old, *newthread;
677
678 /* Check if t is already finished */
679 if (t->base == NULL)
680 return;
681
682 /* Create a join list if necessary */
683 if (t->joining == NULL)
684 {
685 t->joining = malloc(sizeof(coop_q_t));
686 coop_qinit((coop_q_t *) t->joining);
687 }
688
44e8413c
MD
689#ifdef GUILE_ISELECT
690 newthread = coop_wait_for_runnable_thread();
d186aac6
MD
691 if (newthread == coop_global_curr)
692 return;
44e8413c 693#else
7bfd3b9e 694 newthread = coop_next_runnable_thread();
44e8413c 695#endif
7bfd3b9e
JB
696 old = coop_global_curr;
697 coop_global_curr = newthread;
698 QT_BLOCK (coop_yieldhelp, old, (coop_q_t *) t->joining, newthread->sp);
699}
700
7bfd3b9e
JB
701void
702coop_yield()
7bfd3b9e
JB
703{
704 coop_t *old = NULL;
705 coop_t *newthread;
706
707 newthread = coop_next_runnable_thread();
708
709 /* There may be no other runnable threads. Return if this is the
710 case. */
d186aac6
MD
711#if GUILE_ISELECT
712 if (newthread == coop_global_curr)
713 return;
714#else
7bfd3b9e
JB
715 if (newthread == NULL)
716 return;
d186aac6 717#endif
7bfd3b9e
JB
718
719 old = coop_global_curr;
720
721 coop_global_curr = newthread;
722 QT_BLOCK (coop_yieldhelp, old, &coop_global_runq, newthread->sp);
723}
724
725
7bfd3b9e
JB
726static void *
727coop_yieldhelp (qt_t *sp, void *old, void *blockq)
7bfd3b9e
JB
728{
729 ((coop_t *)old)->sp = sp;
730 coop_qput ((coop_q_t *)blockq, (coop_t *)old);
731 return NULL;
732}
733
734/* Replacement for the system's sleep() function. Does the right thing
735 for the process - but not for the system (it busy-waits) */
736
44e8413c 737void *
7bfd3b9e 738coop_sleephelp (qt_t *sp, void *old, void *blockq)
7bfd3b9e
JB
739{
740 ((coop_t *)old)->sp = sp;
741 /* old is already on the sleep queue - so there's no need to
742 do anything extra here */
743 return NULL;
744}
745
44e8413c
MD
746#ifdef GUILE_ISELECT
747
6aa9316d
JB
748unsigned long
749scm_thread_usleep (unsigned long usec)
44e8413c
MD
750{
751 struct timeval timeout;
752 timeout.tv_sec = 0;
753 timeout.tv_usec = usec;
754 scm_internal_select (0, NULL, NULL, NULL, &timeout);
2c4e1a34
MD
755 return 0; /* Maybe we should calculate actual time slept,
756 but this is faster... :) */
44e8413c
MD
757}
758
6aa9316d
JB
759unsigned long
760scm_thread_sleep (unsigned long sec)
44e8413c
MD
761{
762 time_t now = time (NULL);
763 struct timeval timeout;
6aa9316d 764 unsigned long slept;
44e8413c
MD
765 timeout.tv_sec = sec;
766 timeout.tv_usec = 0;
767 scm_internal_select (0, NULL, NULL, NULL, &timeout);
768 slept = time (NULL) - now;
769 return slept > sec ? 0 : sec - slept;
770}
771
772#else /* GUILE_ISELECT */
773
6aa9316d
JB
774unsigned long
775scm_thread_sleep (unsigned long s)
7bfd3b9e
JB
776{
777 coop_t *newthread, *old;
44e8413c 778 time_t now = time (NULL);
7bfd3b9e
JB
779 coop_global_curr->wakeup_time = now + s;
780
781 /* Put the current thread on the sleep queue */
782 coop_qput (&coop_global_sleepq, coop_global_curr);
783
784 newthread = coop_next_runnable_thread();
785
786 /* If newthread is the same as the sleeping thread, do nothing */
787 if (newthread == coop_global_curr)
788 return s;
789
790 old = coop_global_curr;
791
792 coop_global_curr = newthread;
793 QT_BLOCK (coop_sleephelp, old, NULL, newthread->sp);
794
795 return s;
796}
44e8413c 797
6aa9316d
JB
798unsigned long
799scm_thread_usleep (unsigned long usec)
800{
801 /* We're so cheap. */
802 scm_thread_sleep (usec / 1000000);
803 struct timeval timeout;
804 return 0; /* Maybe we should calculate actual time slept,
805 but this is faster... :) */
806}
807
44e8413c 808#endif /* GUILE_ISELECT */
89e00824
ML
809
810/*
811 Local Variables:
812 c-file-style: "gnu"
813 End:
814*/