Access smob data with SCM_{SET_}?CELL_TYPE or SCM_{SET_}?WORD_[1-3].
[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
f85a9bcf 43/* $Id: coop.c,v 1.20 2000-03-29 01:57:40 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
545#define COOP_STACK_ROOM (512)
546#else
547#define COOP_STACK_ROOM (-512)
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)
573 t->sp = t->base;
574 else
575#endif
576 {
577 t = malloc (sizeof (coop_t));
7bfd3b9e 578
f85a9bcf
MD
579 t->data = NULL;
580 t->n_keys = 0;
df26ebfd 581#ifdef GUILE_PTHREAD_COMPAT
f85a9bcf
MD
582 pthread_cond_init (&t->dummy_cond, NULL);
583 pthread_mutex_init (&t->dummy_mutex, NULL);
584 pthread_mutex_lock (&t->dummy_mutex);
585 pthread_create (&t->dummy_thread, NULL, dummy_start, t);
586 pthread_cond_wait (&t->dummy_cond, &t->dummy_mutex);
587 pthread_mutex_unlock (&t->dummy_mutex);
df26ebfd 588#else
f85a9bcf
MD
589 t->sto = malloc (COOP_STKSIZE);
590 sto = COOP_STKALIGN (t->sto, QT_STKALIGN);
591 t->sp = QT_SP (sto, COOP_STKSIZE - QT_STKALIGN);
df26ebfd 592#endif
f85a9bcf
MD
593 t->base = t->sp;
594 }
7bfd3b9e
JB
595 t->sp = QT_ARGS (t->sp, pu, t, (qt_userf_t *)f, coop_only);
596 t->joining = NULL;
597 coop_qput (&coop_global_runq, t);
598 coop_all_qput (&coop_global_allq, t);
599
600 return t;
601}
602
603
7bfd3b9e
JB
604static void
605coop_only (void *pu, void *pt, qt_userf_t *f)
7bfd3b9e
JB
606{
607 coop_global_curr = (coop_t *)pt;
608 (*(coop_userf_t *)f)(pu);
609 coop_abort();
610 /* NOTREACHED */
611}
612
613
7bfd3b9e
JB
614void
615coop_abort ()
7bfd3b9e
JB
616{
617 coop_t *old, *newthread;
618
619 /* Wake up any threads that are waiting to join this one */
620 if (coop_global_curr->joining)
621 {
622 while ((newthread = coop_qget ((coop_q_t *)(coop_global_curr->joining)))
623 != NULL)
624 {
625 coop_qput (&coop_global_runq, newthread);
626 }
f85a9bcf 627 free (coop_global_curr->joining);
7bfd3b9e
JB
628 }
629
44e8413c 630#ifdef GUILE_ISELECT
d186aac6
MD
631 scm_I_am_dead = 1;
632 do {
633 newthread = coop_wait_for_runnable_thread();
634 } while (newthread == coop_global_curr);
635 scm_I_am_dead = 0;
44e8413c 636#else
7bfd3b9e 637 newthread = coop_next_runnable_thread();
44e8413c 638#endif
f85a9bcf 639 coop_all_qremove (&coop_global_allq, coop_global_curr);
7bfd3b9e
JB
640 old = coop_global_curr;
641 coop_global_curr = newthread;
f85a9bcf 642 QT_ABORT (coop_aborthelp, old, (void *) NULL, newthread->sp);
7bfd3b9e
JB
643}
644
645
7bfd3b9e
JB
646static void *
647coop_aborthelp (qt_t *sp, void *old, void *null)
7bfd3b9e
JB
648{
649 coop_t *oldthread = (coop_t *) old;
650
f85a9bcf
MD
651#if 0
652 /* Marking old->base NULL indicates that this thread is dead */
7bfd3b9e 653 oldthread->base = NULL;
f85a9bcf 654#endif
7bfd3b9e 655
f85a9bcf
MD
656 if (oldthread->data)
657 free (oldthread->data);
658#ifndef GUILE_PTHREAD_COMPAT
659 free (oldthread->sto);
660 free (oldthread);
661#else
662 oldthread->n_keys = 0;
663 coop_qput (&coop_deadq, oldthread);
664#endif
665
7bfd3b9e
JB
666 return NULL;
667}
668
669
7bfd3b9e
JB
670void
671coop_join(coop_t *t)
7bfd3b9e
JB
672{
673 coop_t *old, *newthread;
674
675 /* Check if t is already finished */
676 if (t->base == NULL)
677 return;
678
679 /* Create a join list if necessary */
680 if (t->joining == NULL)
681 {
682 t->joining = malloc(sizeof(coop_q_t));
683 coop_qinit((coop_q_t *) t->joining);
684 }
685
44e8413c
MD
686#ifdef GUILE_ISELECT
687 newthread = coop_wait_for_runnable_thread();
d186aac6
MD
688 if (newthread == coop_global_curr)
689 return;
44e8413c 690#else
7bfd3b9e 691 newthread = coop_next_runnable_thread();
44e8413c 692#endif
7bfd3b9e
JB
693 old = coop_global_curr;
694 coop_global_curr = newthread;
695 QT_BLOCK (coop_yieldhelp, old, (coop_q_t *) t->joining, newthread->sp);
696}
697
7bfd3b9e
JB
698void
699coop_yield()
7bfd3b9e
JB
700{
701 coop_t *old = NULL;
702 coop_t *newthread;
703
704 newthread = coop_next_runnable_thread();
705
706 /* There may be no other runnable threads. Return if this is the
707 case. */
d186aac6
MD
708#if GUILE_ISELECT
709 if (newthread == coop_global_curr)
710 return;
711#else
7bfd3b9e
JB
712 if (newthread == NULL)
713 return;
d186aac6 714#endif
7bfd3b9e
JB
715
716 old = coop_global_curr;
717
718 coop_global_curr = newthread;
719 QT_BLOCK (coop_yieldhelp, old, &coop_global_runq, newthread->sp);
720}
721
722
7bfd3b9e
JB
723static void *
724coop_yieldhelp (qt_t *sp, void *old, void *blockq)
7bfd3b9e
JB
725{
726 ((coop_t *)old)->sp = sp;
727 coop_qput ((coop_q_t *)blockq, (coop_t *)old);
728 return NULL;
729}
730
731/* Replacement for the system's sleep() function. Does the right thing
732 for the process - but not for the system (it busy-waits) */
733
44e8413c 734void *
7bfd3b9e 735coop_sleephelp (qt_t *sp, void *old, void *blockq)
7bfd3b9e
JB
736{
737 ((coop_t *)old)->sp = sp;
738 /* old is already on the sleep queue - so there's no need to
739 do anything extra here */
740 return NULL;
741}
742
44e8413c
MD
743#ifdef GUILE_ISELECT
744
6aa9316d
JB
745unsigned long
746scm_thread_usleep (unsigned long usec)
44e8413c
MD
747{
748 struct timeval timeout;
749 timeout.tv_sec = 0;
750 timeout.tv_usec = usec;
751 scm_internal_select (0, NULL, NULL, NULL, &timeout);
2c4e1a34
MD
752 return 0; /* Maybe we should calculate actual time slept,
753 but this is faster... :) */
44e8413c
MD
754}
755
6aa9316d
JB
756unsigned long
757scm_thread_sleep (unsigned long sec)
44e8413c
MD
758{
759 time_t now = time (NULL);
760 struct timeval timeout;
6aa9316d 761 unsigned long slept;
44e8413c
MD
762 timeout.tv_sec = sec;
763 timeout.tv_usec = 0;
764 scm_internal_select (0, NULL, NULL, NULL, &timeout);
765 slept = time (NULL) - now;
766 return slept > sec ? 0 : sec - slept;
767}
768
769#else /* GUILE_ISELECT */
770
6aa9316d
JB
771unsigned long
772scm_thread_sleep (unsigned long s)
7bfd3b9e
JB
773{
774 coop_t *newthread, *old;
44e8413c 775 time_t now = time (NULL);
7bfd3b9e
JB
776 coop_global_curr->wakeup_time = now + s;
777
778 /* Put the current thread on the sleep queue */
779 coop_qput (&coop_global_sleepq, coop_global_curr);
780
781 newthread = coop_next_runnable_thread();
782
783 /* If newthread is the same as the sleeping thread, do nothing */
784 if (newthread == coop_global_curr)
785 return s;
786
787 old = coop_global_curr;
788
789 coop_global_curr = newthread;
790 QT_BLOCK (coop_sleephelp, old, NULL, newthread->sp);
791
792 return s;
793}
44e8413c 794
6aa9316d
JB
795unsigned long
796scm_thread_usleep (unsigned long usec)
797{
798 /* We're so cheap. */
799 scm_thread_sleep (usec / 1000000);
800 struct timeval timeout;
801 return 0; /* Maybe we should calculate actual time slept,
802 but this is faster... :) */
803}
804
44e8413c 805#endif /* GUILE_ISELECT */
89e00824
ML
806
807/*
808 Local Variables:
809 c-file-style: "gnu"
810 End:
811*/