1 /* Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
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)
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.
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
15 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
16 * Boston, MA 02111-1307 USA
18 * As a special exception, the Free Software Foundation gives permission
19 * for additional uses of the text contained in its release of GUILE.
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.
27 * This exception does not however invalidate any other reasons why
28 * the executable file might be covered by the GNU General Public License.
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.
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.
40 * If you do not wish that, delete this exception notice. */
43 /* $Id: coop.c,v 1.15 1998-11-19 08:15:22 mdj Exp $ */
45 /* Cooperative thread library, based on QuickThreads */
54 \f/* #define COOP_STKSIZE (0x10000) */
55 #define COOP_STKSIZE (scm_eval_stack)
57 /* `alignment' must be a power of 2. */
58 #define COOP_STKALIGN(sp, alignment) \
59 ((void *)((((qt_word_t)(sp)) + (alignment) - 1) & ~((alignment)-1)))
63 /* Queue access functions. */
67 coop_qinit (coop_q_t
*q
)
74 q
->t
.next
= q
->tail
= &q
->t
;
82 q
->t
.exceptfds
= NULL
;
90 coop_qget (coop_q_t
*q
)
101 if (t
->next
== &q
->t
) {
102 if (t
== &q
->t
) { /* If it was already empty .. */
103 return (NULL
); /* .. say so. */
105 q
->tail
= &q
->t
; /* Else now it is empty. */
113 coop_qput (coop_q_t
*q
, coop_t
*t
)
128 coop_all_qput (coop_q_t
*q
, coop_t
*t
)
137 q
->t
.all_next
->all_prev
= t
;
139 t
->all_next
= q
->t
.all_next
;
145 coop_all_qremove (coop_q_t
*q
, coop_t
*t
)
148 coop_all_qremove (q
, t
)
154 t
->all_prev
->all_next
= t
->all_next
;
156 q
->t
.all_next
= t
->all_next
;
158 t
->all_next
->all_prev
= t
->all_prev
;
162 \f/* Thread routines. */
164 coop_q_t coop_global_runq
; /* A queue of runable threads. */
165 coop_q_t coop_global_sleepq
; /* A queue of sleeping threads. */
166 coop_q_t coop_tmp_queue
; /* A temp working queue */
167 coop_q_t coop_global_allq
; /* A queue of all threads. */
168 static coop_t coop_global_main
; /* Thread for the process. */
169 coop_t
*coop_global_curr
; /* Currently-executing thread. */
171 static void *coop_starthelp (qt_t
*old
, void *ignore0
, void *ignore1
);
172 static void coop_only (void *pu
, void *pt
, qt_userf_t
*f
);
173 static void *coop_aborthelp (qt_t
*sp
, void *old
, void *null
);
174 static void *coop_yieldhelp (qt_t
*sp
, void *old
, void *blockq
);
185 coop_qinit (&coop_global_runq
);
186 coop_qinit (&coop_global_sleepq
);
187 coop_qinit (&coop_tmp_queue
);
188 coop_qinit (&coop_global_allq
);
189 coop_global_curr
= &coop_global_main
;
193 /* Return the next runnable thread. If no threads are currently runnable,
194 and there are sleeping threads - wait until one wakes up. Otherwise,
197 #ifndef GUILE_ISELECT
200 coop_next_runnable_thread()
203 coop_next_runnable_thread()
214 /* Check the sleeping queue */
215 while ((t
= coop_qget(&coop_global_sleepq
)) != NULL
)
218 if (t
->wakeup_time
<= now
)
219 coop_qput(&coop_global_runq
, t
);
221 coop_qput(&coop_tmp_queue
, t
);
223 while ((t
= coop_qget(&coop_tmp_queue
)) != NULL
)
224 coop_qput(&coop_global_sleepq
, t
);
226 t
= coop_qget (&coop_global_runq
);
228 } while ((t
== NULL
) && (sleepers
> 0));
244 while ((next
= coop_qget (&coop_global_runq
)) != NULL
) {
245 coop_global_curr
= next
;
246 QT_BLOCK (coop_starthelp
, 0, 0, next
->sp
);
253 coop_starthelp (qt_t
*old
, void *ignore0
, void *ignore1
)
256 coop_starthelp (old
, ignore0
, ignore1
)
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 */
270 coop_mutex_init (coop_m
*m
)
278 coop_qinit(&(m
->waiting
));
284 coop_mutex_lock (coop_m
*m
)
291 if (m
->owner
== NULL
)
293 m
->owner
= coop_global_curr
;
297 coop_t
*old
, *newthread
;
299 /* Record the current top-of-stack before going to sleep */
300 coop_global_curr
->top
= &old
;
303 newthread
= coop_wait_for_runnable_thread();
304 if (newthread
== coop_global_curr
)
307 newthread
= coop_next_runnable_thread();
309 old
= coop_global_curr
;
310 coop_global_curr
= newthread
;
311 QT_BLOCK (coop_yieldhelp
, old
, &(m
->waiting
), newthread
->sp
);
319 coop_mutex_unlock (coop_m
*m
)
322 coop_mutex_unlock (m
)
326 coop_t
*old
, *newthread
;
328 newthread
= coop_qget (&(m
->waiting
));
329 if (newthread
!= NULL
)
331 /* Record the current top-of-stack before going to sleep */
332 coop_global_curr
->top
= &old
;
334 old
= coop_global_curr
;
335 coop_global_curr
= newthread
;
336 /* The new thread came into m->waiting through a lock operation.
337 It now owns this mutex. */
338 m
->owner
= coop_global_curr
;
339 QT_BLOCK (coop_yieldhelp
, old
, &coop_global_runq
, newthread
->sp
);
351 coop_mutex_destroy (coop_m
*m
)
354 coop_mutex_destroy (m
)
364 coop_condition_variable_init (coop_c
*c
)
367 coop_condition_variable_init (c
)
371 coop_qinit(&(c
->waiting
));
377 coop_condition_variable_wait_mutex (coop_c
*c
, coop_m
*m
)
380 coop_condition_variable_wait_mutex (c
, m
)
385 coop_t
*old
, *newthread
;
387 /* coop_mutex_unlock (m); */
388 newthread
= coop_qget (&(m
->waiting
));
389 if (newthread
!= NULL
)
391 m
->owner
= newthread
;
397 newthread
= coop_wait_for_runnable_thread();
398 if (newthread
== coop_global_curr
)
401 newthread
= coop_next_runnable_thread();
404 coop_global_curr
->top
= &old
;
405 old
= coop_global_curr
;
406 coop_global_curr
= newthread
;
407 QT_BLOCK (coop_yieldhelp
, old
, &(c
->waiting
), newthread
->sp
);
416 coop_condition_variable_signal (coop_c
*c
)
419 coop_condition_variable_signal (c
)
425 while ((newthread
= coop_qget (&(c
->waiting
))) != NULL
)
427 coop_qput (&coop_global_runq
, newthread
);
435 coop_condition_variable_destroy (coop_c
*c
)
438 coop_condition_variable_destroy (c
)
448 coop_create (coop_userf_t
*f
, void *pu
)
459 t
= malloc (sizeof(coop_t
));
462 t
->sto
= malloc (COOP_STKSIZE
);
463 sto
= COOP_STKALIGN (t
->sto
, QT_STKALIGN
);
464 t
->sp
= QT_SP (sto
, COOP_STKSIZE
- QT_STKALIGN
);
466 t
->sp
= QT_ARGS (t
->sp
, pu
, t
, (qt_userf_t
*)f
, coop_only
);
468 coop_qput (&coop_global_runq
, t
);
469 coop_all_qput (&coop_global_allq
, t
);
477 coop_only (void *pu
, void *pt
, qt_userf_t
*f
)
480 coop_only (pu
. pt
, f
)
486 coop_global_curr
= (coop_t
*)pt
;
487 (*(coop_userf_t
*)f
)(pu
);
501 coop_t
*old
, *newthread
;
503 /* Wake up any threads that are waiting to join this one */
504 if (coop_global_curr
->joining
)
506 while ((newthread
= coop_qget ((coop_q_t
*)(coop_global_curr
->joining
)))
509 coop_qput (&coop_global_runq
, newthread
);
511 free(coop_global_curr
->joining
);
517 newthread
= coop_wait_for_runnable_thread();
518 } while (newthread
== coop_global_curr
);
521 newthread
= coop_next_runnable_thread();
523 coop_all_qremove(&coop_global_allq
, coop_global_curr
);
524 old
= coop_global_curr
;
525 coop_global_curr
= newthread
;
526 QT_ABORT (coop_aborthelp
, old
, (void *)NULL
, newthread
->sp
);
532 coop_aborthelp (qt_t
*sp
, void *old
, void *null
)
535 coop_aborthelp (sp
, old
, null
)
541 coop_t
*oldthread
= (coop_t
*) old
;
543 free (oldthread
->sto
);
545 /* "old" is freed in scm_threads_thread_die().
546 Marking old->base NULL indicates that this thread is dead */
548 oldthread
->base
= NULL
;
563 coop_t
*old
, *newthread
;
565 /* Check if t is already finished */
569 /* Create a join list if necessary */
570 if (t
->joining
== NULL
)
572 t
->joining
= malloc(sizeof(coop_q_t
));
573 coop_qinit((coop_q_t
*) t
->joining
);
577 newthread
= coop_wait_for_runnable_thread();
578 if (newthread
== coop_global_curr
)
581 newthread
= coop_next_runnable_thread();
583 old
= coop_global_curr
;
584 coop_global_curr
= newthread
;
585 QT_BLOCK (coop_yieldhelp
, old
, (coop_q_t
*) t
->joining
, newthread
->sp
);
599 newthread
= coop_next_runnable_thread();
601 /* There may be no other runnable threads. Return if this is the
604 if (newthread
== coop_global_curr
)
607 if (newthread
== NULL
)
611 old
= coop_global_curr
;
613 coop_global_curr
= newthread
;
614 QT_BLOCK (coop_yieldhelp
, old
, &coop_global_runq
, newthread
->sp
);
620 coop_yieldhelp (qt_t
*sp
, void *old
, void *blockq
)
623 coop_yieldhelp (sp
, old
, blockq
)
629 ((coop_t
*)old
)->sp
= sp
;
630 coop_qput ((coop_q_t
*)blockq
, (coop_t
*)old
);
634 /* Replacement for the system's sleep() function. Does the right thing
635 for the process - but not for the system (it busy-waits) */
638 coop_sleephelp (qt_t
*sp
, void *old
, void *blockq
)
640 ((coop_t
*)old
)->sp
= sp
;
641 /* old is already on the sleep queue - so there's no need to
642 do anything extra here */
649 scm_thread_usleep (unsigned long usec
)
651 struct timeval timeout
;
653 timeout
.tv_usec
= usec
;
654 scm_internal_select (0, NULL
, NULL
, NULL
, &timeout
);
655 return 0; /* Maybe we should calculate actual time slept,
656 but this is faster... :) */
660 scm_thread_sleep (unsigned long sec
)
662 time_t now
= time (NULL
);
663 struct timeval timeout
;
665 timeout
.tv_sec
= sec
;
667 scm_internal_select (0, NULL
, NULL
, NULL
, &timeout
);
668 slept
= time (NULL
) - now
;
669 return slept
> sec
? 0 : sec
- slept
;
672 #else /* GUILE_ISELECT */
675 scm_thread_sleep (unsigned long s
)
677 coop_t
*newthread
, *old
;
678 time_t now
= time (NULL
);
679 coop_global_curr
->wakeup_time
= now
+ s
;
681 /* Put the current thread on the sleep queue */
682 coop_qput (&coop_global_sleepq
, coop_global_curr
);
684 newthread
= coop_next_runnable_thread();
686 /* If newthread is the same as the sleeping thread, do nothing */
687 if (newthread
== coop_global_curr
)
690 old
= coop_global_curr
;
692 coop_global_curr
= newthread
;
693 QT_BLOCK (coop_sleephelp
, old
, NULL
, newthread
->sp
);
699 scm_thread_usleep (unsigned long usec
)
701 /* We're so cheap. */
702 scm_thread_sleep (usec
/ 1000000);
703 struct timeval timeout
;
704 return 0; /* Maybe we should calculate actual time slept,
705 but this is faster... :) */
708 #endif /* GUILE_ISELECT */