* coop-threads.c (scheme_launch_data, scheme_body_bootstrip,
[bpt/guile.git] / libguile / coop.c
CommitLineData
7bfd3b9e
JB
1/* Copyright (C) 1995, 1996 Free Software Foundation, Inc.
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
c8bf4ecd 43/* $Id: coop.c,v 1.4 1998-01-26 01:43:16 mdj Exp $ */
7bfd3b9e
JB
44
45/* Cooperative thread library, based on QuickThreads */
46
47#include <qt.h>
48
49#define COOP_STKSIZE (0x10000)
50
51/* `alignment' must be a power of 2. */
52#define COOP_STKALIGN(sp, alignment) \
53((void *)((((qt_word_t)(sp)) + (alignment) - 1) & ~((alignment)-1)))
54
55\f
56
57/* Queue access functions. */
58
59#ifdef __STDC__
60static void
61coop_qinit (coop_q_t *q)
62#else
63static void
64coop_qinit (q)
65 coop_q_t *q;
66#endif
67{
68 q->t.next = q->tail = &q->t;
69
70 q->t.all_prev = NULL;
71 q->t.all_next = NULL;
44e8413c
MD
72#ifdef GUILE_ISELECT
73 q->t.nfds = 0;
74 q->t.readfds = NULL;
75 q->t.writefds = NULL;
76 q->t.exceptfds = NULL;
77 q->t.timeoutp = 0;
78#endif
7bfd3b9e
JB
79}
80
81
82#ifdef __STDC__
44e8413c 83coop_t *
7bfd3b9e
JB
84coop_qget (coop_q_t *q)
85#else
44e8413c 86coop_t *
7bfd3b9e
JB
87coop_qget (q)
88 coop_q_t *q;
89#endif
90{
91 coop_t *t;
92
93 t = q->t.next;
94 q->t.next = t->next;
95 if (t->next == &q->t) {
96 if (t == &q->t) { /* If it was already empty .. */
97 return (NULL); /* .. say so. */
98 }
99 q->tail = &q->t; /* Else now it is empty. */
100 }
101 return (t);
102}
103
104
105#ifdef __STDC__
44e8413c 106void
7bfd3b9e
JB
107coop_qput (coop_q_t *q, coop_t *t)
108#else
44e8413c 109void
7bfd3b9e
JB
110coop_qput (q, t)
111 coop_q_t *q;
112 coop_t *t;
113#endif
114{
115 q->tail->next = t;
116 t->next = &q->t;
117 q->tail = t;
118}
119
120#ifdef __STDC__
121static void
122coop_all_qput (coop_q_t *q, coop_t *t)
123#else
124static void
125coop_all_qput (q, t)
126 coop_q_t *q;
127 coop_t *t;
128#endif
129{
130 if (q->t.all_next)
131 q->t.all_next->all_prev = t;
132 t->all_prev = NULL;
133 t->all_next = q->t.all_next;
134 q->t.all_next = t;
135}
136
137#ifdef __STDC__
138static void
139coop_all_qremove (coop_q_t *q, coop_t *t)
140#else
141static void
142coop_all_qremove (q, t)
143 coop_q_t *q;
144 coop_t *t;
145#endif
146{
147 if (t->all_prev)
148 t->all_prev->all_next = t->all_next;
149 else
150 q->t.all_next = t->all_next;
151 if (t->all_next)
152 t->all_next->all_prev = t->all_prev;
153}
154
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
JB
164
165static void *coop_starthelp (qt_t *old, void *ignore0, void *ignore1);
166static void coop_only (void *pu, void *pt, qt_userf_t *f);
167static void *coop_aborthelp (qt_t *sp, void *old, void *null);
168static void *coop_yieldhelp (qt_t *sp, void *old, void *blockq);
169
170
171#ifdef __STDC__
172void
173coop_init()
174#else
175void
176coop_init()
177#endif
178{
179 coop_qinit (&coop_global_runq);
180 coop_qinit (&coop_global_sleepq);
44e8413c 181 coop_qinit (&coop_tmp_queue);
7bfd3b9e
JB
182 coop_qinit (&coop_global_allq);
183 coop_global_curr = &coop_global_main;
184}
185
186
187/* Return the next runnable thread. If no threads are currently runnable,
188 and there are sleeping threads - wait until one wakes up. Otherwise,
189 return NULL. */
190
44e8413c
MD
191#ifdef GUILE_ISELECT
192extern coop_t *coop_next_runnable_thread ();
193#else
7bfd3b9e
JB
194#ifdef __STDC__
195coop_t *
196coop_next_runnable_thread()
197#else
198coop_t *
199coop_next_runnable_thread()
200#endif
201{
202 int sleepers;
203 coop_t *t;
204 time_t now;
205
206 do {
207 sleepers = 0;
208 now = time(NULL);
209
210 /* Check the sleeping queue */
211 while ((t = coop_qget(&coop_global_sleepq)) != NULL)
212 {
213 sleepers++;
214 if (t->wakeup_time <= now)
215 coop_qput(&coop_global_runq, t);
216 else
44e8413c 217 coop_qput(&coop_tmp_queue, t);
7bfd3b9e 218 }
44e8413c 219 while ((t = coop_qget(&coop_tmp_queue)) != NULL)
7bfd3b9e
JB
220 coop_qput(&coop_global_sleepq, t);
221
222 t = coop_qget (&coop_global_runq);
223
224 } while ((t == NULL) && (sleepers > 0));
225
226 return t;
227}
44e8413c 228#endif
7bfd3b9e
JB
229
230#ifdef __STDC__
231void
232coop_start()
233#else
234void
235coop_start()
236#endif
237{
238 coop_t *next;
239
240 while ((next = coop_qget (&coop_global_runq)) != NULL) {
241 coop_global_curr = next;
242 QT_BLOCK (coop_starthelp, 0, 0, next->sp);
243 }
244}
245
246
247#ifdef __STDC__
248static void *
249coop_starthelp (qt_t *old, void *ignore0, void *ignore1)
250#else
251static void *
252coop_starthelp (old, ignore0, ignore1)
253 qt_t *old;
254 void *ignore0;
255 void *ignore1;
256#endif
257{
258 coop_global_main.sp = old;
259 coop_global_main.joining = NULL;
260 coop_qput (&coop_global_runq, &coop_global_main);
261 return NULL; /* not used, but keeps compiler happy */
262}
263
264#ifdef __STDC__
c8bf4ecd 265int
7bfd3b9e
JB
266coop_mutex_init (coop_m *m)
267#else
c8bf4ecd 268int
7bfd3b9e
JB
269coop_mutex_init (m)
270 coop_m *m;
271#endif
272{
273 m->owner = NULL;
274 coop_qinit(&(m->waiting));
c8bf4ecd 275 return 0;
7bfd3b9e
JB
276}
277
278#ifdef __STDC__
c8bf4ecd 279int
7bfd3b9e
JB
280coop_mutex_lock (coop_m *m)
281#else
c8bf4ecd 282int
7bfd3b9e
JB
283coop_mutex_lock ()
284 coop_m *m;
285#endif
286{
287 if (m->owner == NULL)
288 {
289 m->owner = coop_global_curr;
290 }
291 else
292 {
293 coop_t *old, *newthread;
294
295 /* Record the current top-of-stack before going to sleep */
296 coop_global_curr->top = &old;
297
44e8413c
MD
298#ifdef GUILE_ISELECT
299 newthread = coop_wait_for_runnable_thread();
300#else
7bfd3b9e 301 newthread = coop_next_runnable_thread();
44e8413c 302#endif
7bfd3b9e
JB
303 old = coop_global_curr;
304 coop_global_curr = newthread;
305 QT_BLOCK (coop_yieldhelp, old, &(m->waiting), newthread->sp);
306 }
c8bf4ecd 307 return 0;
7bfd3b9e
JB
308}
309
310
311#ifdef __STDC__
c8bf4ecd 312int
7bfd3b9e
JB
313coop_mutex_unlock (coop_m *m)
314#else
c8bf4ecd 315int
7bfd3b9e
JB
316coop_mutex_unlock (m)
317 coop_m *m;
318#endif
319{
320 coop_t *old, *newthread;
321
322 newthread = coop_qget (&(m->waiting));
323 if (newthread != NULL)
324 {
325 /* Record the current top-of-stack before going to sleep */
326 coop_global_curr->top = &old;
327
328 old = coop_global_curr;
329 coop_global_curr = newthread;
330 m->owner = coop_global_curr;
331 QT_BLOCK (coop_yieldhelp, old, &coop_global_runq, newthread->sp);
332 }
333 else
334 {
335 m->owner = NULL;
336 }
c8bf4ecd 337 return 0;
7bfd3b9e
JB
338}
339
340
341#ifdef __STDC__
c8bf4ecd
MD
342int
343coop_mutex_destroy (coop_m *m)
344#else
345int
346coop_mutex_destroy (m)
347 coop_m *m;
348#endif
349{
350 return 0;
351}
352
353
354#ifdef __STDC__
355int
7bfd3b9e
JB
356coop_condition_variable_init (coop_c *c)
357#else
c8bf4ecd 358int
7bfd3b9e
JB
359coop_condition_variable_init (c)
360 coop_c *c;
361#endif
362{
363 coop_qinit(&(c->waiting));
c8bf4ecd 364 return 0;
7bfd3b9e
JB
365}
366
367#ifdef __STDC__
c8bf4ecd 368int
7bfd3b9e
JB
369coop_condition_variable_wait (coop_c *c)
370#else
c8bf4ecd 371int
7bfd3b9e
JB
372coop_condition_variable_wait (c)
373 coop_c *c;
374#endif
375{
376 coop_t *old, *newthread;
377
44e8413c
MD
378#ifdef GUILE_ISELECT
379 newthread = coop_wait_for_runnable_thread();
380#else
7bfd3b9e 381 newthread = coop_next_runnable_thread();
44e8413c 382#endif
7bfd3b9e
JB
383 old = coop_global_curr;
384 coop_global_curr = newthread;
385 QT_BLOCK (coop_yieldhelp, old, &(c->waiting), newthread->sp);
c8bf4ecd
MD
386 return 0;
387}
388
389
390#ifdef __STDC__
391int
392coop_condition_variable_wait_mutex (coop_c *c, coop_m *m)
393#else
394int
395coop_condition_variable_wait_mutex (c, m)
396 coop_c *c;
397 coop_m *m;
398#endif
399{
400 coop_mutex_unlock (m);
401 coop_condition_variable_wait (c);
402 coop_mutex_lock (m);
403 return 0;
7bfd3b9e
JB
404}
405
c8bf4ecd 406
7bfd3b9e 407#ifdef __STDC__
c8bf4ecd 408int
7bfd3b9e
JB
409coop_condition_variable_signal (coop_c *c)
410#else
c8bf4ecd 411int
7bfd3b9e
JB
412coop_condition_variable_signal (c)
413 coop_c *c;
414#endif
415{
416 coop_t *newthread;
417
418 while ((newthread = coop_qget (&(c->waiting))) != NULL)
419 {
420 coop_qput (&coop_global_runq, newthread);
421 }
c8bf4ecd
MD
422 return 0;
423}
424
425
426#ifdef __STDC__
427int
428coop_condition_variable_destroy (coop_c *c)
429#else
430int
431coop_condition_variable_destroy (c)
432 coop_c *c;
433#endif
434{
435 return 0;
7bfd3b9e
JB
436}
437
438
439#ifdef __STDC__
440coop_t *
441coop_create (coop_userf_t *f, void *pu)
442#else
443coop_t *
444coop_create (f, pu)
445 coop_userf_t *f;
446 void *pu;
447#endif
448{
449 coop_t *t;
450 void *sto;
451
452 t = malloc (sizeof(coop_t));
453
454 t->data = NULL;
455 t->sto = malloc (COOP_STKSIZE);
456 sto = COOP_STKALIGN (t->sto, QT_STKALIGN);
457 t->sp = QT_SP (sto, COOP_STKSIZE - QT_STKALIGN);
458 t->base = t->sp;
459 t->sp = QT_ARGS (t->sp, pu, t, (qt_userf_t *)f, coop_only);
460 t->joining = NULL;
461 coop_qput (&coop_global_runq, t);
462 coop_all_qput (&coop_global_allq, t);
463
464 return t;
465}
466
467
468#ifdef __STDC__
469static void
470coop_only (void *pu, void *pt, qt_userf_t *f)
471#else
472static void
473coop_only (pu. pt, f)
474 void *pu,
475 void *pt,
476 qt_userf_t *f;
477#endif
478{
479 coop_global_curr = (coop_t *)pt;
480 (*(coop_userf_t *)f)(pu);
481 coop_abort();
482 /* NOTREACHED */
483}
484
485
486#ifdef __STDC__
487void
488coop_abort ()
489#else
490void
491coop_abort ()
492#endif
493{
494 coop_t *old, *newthread;
495
496 /* Wake up any threads that are waiting to join this one */
497 if (coop_global_curr->joining)
498 {
499 while ((newthread = coop_qget ((coop_q_t *)(coop_global_curr->joining)))
500 != NULL)
501 {
502 coop_qput (&coop_global_runq, newthread);
503 }
504 free(coop_global_curr->joining);
505 }
506
44e8413c
MD
507#ifdef GUILE_ISELECT
508 newthread = coop_wait_for_runnable_thread();
509#else
7bfd3b9e 510 newthread = coop_next_runnable_thread();
44e8413c 511#endif
7bfd3b9e
JB
512 coop_all_qremove(&coop_global_allq, coop_global_curr);
513 old = coop_global_curr;
514 coop_global_curr = newthread;
515 QT_ABORT (coop_aborthelp, old, (void *)NULL, newthread->sp);
516}
517
518
519#ifdef __STDC__
520static void *
521coop_aborthelp (qt_t *sp, void *old, void *null)
522#else
523static void *
524coop_aborthelp (sp, old, null)
525 qt_t *sp;
526 void *old;
527 void *null;
528#endif
529{
530 coop_t *oldthread = (coop_t *) old;
531
532 free (oldthread->sto);
533
534 /* "old" is freed in scm_threads_thread_die().
535 Marking old->base NULL indicates that this thread is dead */
536
537 oldthread->base = NULL;
538
539 return NULL;
540}
541
542
543#ifdef __STDC__
544void
545coop_join(coop_t *t)
546#else
547void
548coop_join()
549 coop_t *t;
550#endif
551{
552 coop_t *old, *newthread;
553
554 /* Check if t is already finished */
555 if (t->base == NULL)
556 return;
557
558 /* Create a join list if necessary */
559 if (t->joining == NULL)
560 {
561 t->joining = malloc(sizeof(coop_q_t));
562 coop_qinit((coop_q_t *) t->joining);
563 }
564
44e8413c
MD
565#ifdef GUILE_ISELECT
566 newthread = coop_wait_for_runnable_thread();
567#else
7bfd3b9e 568 newthread = coop_next_runnable_thread();
44e8413c 569#endif
7bfd3b9e
JB
570 old = coop_global_curr;
571 coop_global_curr = newthread;
572 QT_BLOCK (coop_yieldhelp, old, (coop_q_t *) t->joining, newthread->sp);
573}
574
575#ifdef __STDC__
576void
577coop_yield()
578#else
579void
580coop_yield()
581#endif
582{
583 coop_t *old = NULL;
584 coop_t *newthread;
585
586 newthread = coop_next_runnable_thread();
587
588 /* There may be no other runnable threads. Return if this is the
589 case. */
590 if (newthread == NULL)
591 return;
592
593 old = coop_global_curr;
594
595 coop_global_curr = newthread;
596 QT_BLOCK (coop_yieldhelp, old, &coop_global_runq, newthread->sp);
597}
598
599
600#ifdef __STDC__
601static void *
602coop_yieldhelp (qt_t *sp, void *old, void *blockq)
603#else
604static void *
605coop_yieldhelp (sp, old, blockq)
606 qt_t *sp;
607 void *old;
608 void *blockq;
609#endif
610{
611 ((coop_t *)old)->sp = sp;
612 coop_qput ((coop_q_t *)blockq, (coop_t *)old);
613 return NULL;
614}
615
616/* Replacement for the system's sleep() function. Does the right thing
617 for the process - but not for the system (it busy-waits) */
618
619#ifdef __STDC__
44e8413c 620void *
7bfd3b9e
JB
621coop_sleephelp (qt_t *sp, void *old, void *blockq)
622#else
44e8413c 623void *
7bfd3b9e
JB
624coop_sleephelp (sp, old, bolckq)
625 qt_t *sp;
626 void *old;
627 void *blockq;
628#endif
629{
630 ((coop_t *)old)->sp = sp;
631 /* old is already on the sleep queue - so there's no need to
632 do anything extra here */
633 return NULL;
634}
635
44e8413c
MD
636#ifdef GUILE_ISELECT
637
638void
639usleep (unsigned usec)
640{
641 struct timeval timeout;
642 timeout.tv_sec = 0;
643 timeout.tv_usec = usec;
644 scm_internal_select (0, NULL, NULL, NULL, &timeout);
645}
646
647unsigned
648sleep (unsigned sec)
649{
650 time_t now = time (NULL);
651 struct timeval timeout;
652 int slept;
653 timeout.tv_sec = sec;
654 timeout.tv_usec = 0;
655 scm_internal_select (0, NULL, NULL, NULL, &timeout);
656 slept = time (NULL) - now;
657 return slept > sec ? 0 : sec - slept;
658}
659
660#else /* GUILE_ISELECT */
661
7bfd3b9e
JB
662#ifdef __STDC__
663unsigned
664sleep (unsigned s)
665#else
666unsigned
667sleep (s)
668 unsigned s;
669#endif
670{
671 coop_t *newthread, *old;
44e8413c 672 time_t now = time (NULL);
7bfd3b9e
JB
673 coop_global_curr->wakeup_time = now + s;
674
675 /* Put the current thread on the sleep queue */
676 coop_qput (&coop_global_sleepq, coop_global_curr);
677
678 newthread = coop_next_runnable_thread();
679
680 /* If newthread is the same as the sleeping thread, do nothing */
681 if (newthread == coop_global_curr)
682 return s;
683
684 old = coop_global_curr;
685
686 coop_global_curr = newthread;
687 QT_BLOCK (coop_sleephelp, old, NULL, newthread->sp);
688
689 return s;
690}
44e8413c
MD
691
692#endif /* GUILE_ISELECT */