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