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