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