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