X-Git-Url: http://git.hcoop.net/bpt/guile.git/blobdiff_plain/6aa9316dea8f1eb68a36c590ded0460ab9fcd28b..9bc6fb0a7d91ae9a6c57cedb76022043db413ba5:/libguile/coop.c diff --git a/libguile/coop.c b/libguile/coop.c index d69f32133..ca057b423 100644 --- a/libguile/coop.c +++ b/libguile/coop.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc. +/* Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -40,16 +40,20 @@ * If you do not wish that, delete this exception notice. */ -/* $Id: coop.c,v 1.14 1998-10-13 23:17:09 jimb Exp $ */ +/* $Id: coop.c,v 1.29 2001-11-04 15:52:29 ela Exp $ */ /* Cooperative thread library, based on QuickThreads */ +#include + #ifdef HAVE_UNISTD_H #include #endif -#include -#include "eval.h" +#include + +#include "qt/qt.h" +#include "libguile/eval.h" /* #define COOP_STKSIZE (0x10000) */ #define COOP_STKSIZE (scm_eval_stack) @@ -62,14 +66,8 @@ /* Queue access functions. */ -#ifdef __STDC__ static void coop_qinit (coop_q_t *q) -#else -static void -coop_qinit (q) - coop_q_t *q; -#endif { q->t.next = q->tail = &q->t; @@ -85,53 +83,35 @@ coop_qinit (q) } -#ifdef __STDC__ coop_t * coop_qget (coop_q_t *q) -#else -coop_t * -coop_qget (q) - coop_q_t *q; -#endif { coop_t *t; t = q->t.next; q->t.next = t->next; - if (t->next == &q->t) { - if (t == &q->t) { /* If it was already empty .. */ - return (NULL); /* .. say so. */ + if (t->next == &q->t) + { + if (t == &q->t) + { /* If it was already empty .. */ + return NULL; /* .. say so. */ + } + q->tail = &q->t; /* Else now it is empty. */ } - q->tail = &q->t; /* Else now it is empty. */ - } return (t); } -#ifdef __STDC__ void coop_qput (coop_q_t *q, coop_t *t) -#else -void -coop_qput (q, t) - coop_q_t *q; - coop_t *t; -#endif { q->tail->next = t; t->next = &q->t; q->tail = t; } -#ifdef __STDC__ static void coop_all_qput (coop_q_t *q, coop_t *t) -#else -static void -coop_all_qput (q, t) - coop_q_t *q; - coop_t *t; -#endif { if (q->t.all_next) q->t.all_next->all_prev = t; @@ -140,15 +120,8 @@ coop_all_qput (q, t) q->t.all_next = t; } -#ifdef __STDC__ static void coop_all_qremove (coop_q_t *q, coop_t *t) -#else -static void -coop_all_qremove (q, t) - coop_q_t *q; - coop_t *t; -#endif { if (t->all_prev) t->all_prev->all_next = t->all_next; @@ -158,6 +131,29 @@ coop_all_qremove (q, t) t->all_next->all_prev = t->all_prev; } +#ifdef GUILE_ISELECT +/* Insert thread t into the ordered queue q. + q is ordered after wakeup_time. Threads which aren't sleeping but + waiting for I/O go last into the queue. */ +void +coop_timeout_qinsert (coop_q_t *q, coop_t *t) +{ + coop_t *pred = &q->t; + int sec = t->wakeup_time.tv_sec; + int usec = t->wakeup_time.tv_usec; + while (pred->next != &q->t + && pred->next->timeoutp + && (pred->next->wakeup_time.tv_sec < sec + || (pred->next->wakeup_time.tv_sec == sec + && pred->next->wakeup_time.tv_usec < usec))) + pred = pred->next; + t->next = pred->next; + pred->next = t; + if (t->next == &q->t) + q->tail = t; +} +#endif + /* Thread routines. */ @@ -168,40 +164,75 @@ coop_q_t coop_global_allq; /* A queue of all threads. */ static coop_t coop_global_main; /* Thread for the process. */ coop_t *coop_global_curr; /* Currently-executing thread. */ +#ifdef GUILE_PTHREAD_COMPAT +static coop_q_t coop_deadq; +static int coop_quitting_p = -1; +static pthread_cond_t coop_cond_quit; +static pthread_cond_t coop_cond_create; +static pthread_mutex_t coop_mutex_create; +static pthread_t coop_mother; +static int mother_awake_p = 0; +static coop_t *coop_child; +#endif + static void *coop_starthelp (qt_t *old, void *ignore0, void *ignore1); static void coop_only (void *pu, void *pt, qt_userf_t *f); static void *coop_aborthelp (qt_t *sp, void *old, void *null); static void *coop_yieldhelp (qt_t *sp, void *old, void *blockq); -#ifdef __STDC__ -void -coop_init() +/* called on process termination. */ +#ifdef HAVE_ATEXIT +static void +coop_finish (void) #else -void -coop_init() +#ifdef HAVE_ON_EXIT +extern int on_exit (void (*procp) (), int arg); + +static void +coop_finish (int status, void *arg) +#else +#error Dont know how to setup a cleanup handler on your system. +#endif #endif +{ +#ifdef GUILE_PTHREAD_COMPAT + coop_quitting_p = 1; + pthread_cond_signal (&coop_cond_create); + pthread_cond_broadcast (&coop_cond_quit); +#endif +} + +void +coop_init () { coop_qinit (&coop_global_runq); coop_qinit (&coop_global_sleepq); coop_qinit (&coop_tmp_queue); coop_qinit (&coop_global_allq); coop_global_curr = &coop_global_main; +#ifdef GUILE_PTHREAD_COMPAT + coop_qinit (&coop_deadq); + pthread_cond_init (&coop_cond_quit, NULL); + pthread_cond_init (&coop_cond_create, NULL); + pthread_mutex_init (&coop_mutex_create, NULL); +#endif +#ifdef HAVE_ATEXIT + atexit (coop_finish); +#else +#ifdef HAVE_ON_EXIT + on_exit (coop_finish, 0); +#endif +#endif } - /* Return the next runnable thread. If no threads are currently runnable, and there are sleeping threads - wait until one wakes up. Otherwise, return NULL. */ #ifndef GUILE_ISELECT -#ifdef __STDC__ coop_t * coop_next_runnable_thread() -#else -coop_t * -coop_next_runnable_thread() -#endif { int sleepers; coop_t *t; @@ -231,13 +262,8 @@ coop_next_runnable_thread() } #endif -#ifdef __STDC__ -void -coop_start() -#else void coop_start() -#endif { coop_t *next; @@ -248,16 +274,8 @@ coop_start() } -#ifdef __STDC__ static void * coop_starthelp (qt_t *old, void *ignore0, void *ignore1) -#else -static void * -coop_starthelp (old, ignore0, ignore1) - qt_t *old; - void *ignore0; - void *ignore1; -#endif { coop_global_main.sp = old; coop_global_main.joining = NULL; @@ -265,28 +283,34 @@ coop_starthelp (old, ignore0, ignore1) return NULL; /* not used, but keeps compiler happy */ } -#ifdef __STDC__ int coop_mutex_init (coop_m *m) -#else +{ + return coop_new_mutex_init (m, NULL); +} + int -coop_mutex_init (m) - coop_m *m; -#endif +coop_new_mutex_init (coop_m *m, coop_mattr *attr) { m->owner = NULL; coop_qinit(&(m->waiting)); return 0; } -#ifdef __STDC__ +int +coop_mutex_trylock (coop_m *m) +{ + if (m->owner == NULL) + { + m->owner = coop_global_curr; + return 0; + } + else + return EBUSY; +} + int coop_mutex_lock (coop_m *m) -#else -int -coop_mutex_lock () - coop_m *m; -#endif { if (m->owner == NULL) { @@ -314,14 +338,8 @@ coop_mutex_lock () } -#ifdef __STDC__ int coop_mutex_unlock (coop_m *m) -#else -int -coop_mutex_unlock (m) - coop_m *m; -#endif { coop_t *old, *newthread; @@ -333,6 +351,8 @@ coop_mutex_unlock (m) old = coop_global_curr; coop_global_curr = newthread; + /* The new thread came into m->waiting through a lock operation. + It now owns this mutex. */ m->owner = coop_global_curr; QT_BLOCK (coop_yieldhelp, old, &coop_global_runq, newthread->sp); } @@ -344,82 +364,115 @@ coop_mutex_unlock (m) } -#ifdef __STDC__ int coop_mutex_destroy (coop_m *m) -#else -int -coop_mutex_destroy (m) - coop_m *m; -#endif { return 0; } -#ifdef __STDC__ int coop_condition_variable_init (coop_c *c) -#else -int -coop_condition_variable_init (c) - coop_c *c; -#endif +{ + return coop_new_condition_variable_init (c, NULL); +} + +int +coop_new_condition_variable_init (coop_c *c, coop_cattr *a) { coop_qinit(&(c->waiting)); return 0; } -#ifdef __STDC__ -static int -coop_condition_variable_wait (coop_c *c) -#else -static int -coop_condition_variable_wait (c) - coop_c *c; -#endif +int +coop_condition_variable_wait_mutex (coop_c *c, coop_m *m) { coop_t *old, *newthread; + /* coop_mutex_unlock (m); */ + newthread = coop_qget (&(m->waiting)); + if (newthread != NULL) + { + m->owner = newthread; + } + else + { + m->owner = NULL; + /*fixme* Should we really wait here? Isn't it OK just to proceed? */ #ifdef GUILE_ISELECT - newthread = coop_wait_for_runnable_thread(); - if (newthread == coop_global_curr) - coop_abort (); + newthread = coop_wait_for_runnable_thread(); + if (newthread == coop_global_curr) + coop_abort (); #else - newthread = coop_next_runnable_thread(); + newthread = coop_next_runnable_thread(); #endif + } + coop_global_curr->top = &old; old = coop_global_curr; coop_global_curr = newthread; QT_BLOCK (coop_yieldhelp, old, &(c->waiting), newthread->sp); + + coop_mutex_lock (m); return 0; } - -#ifdef __STDC__ int -coop_condition_variable_wait_mutex (coop_c *c, coop_m *m) +coop_condition_variable_timed_wait_mutex (coop_c *c, + coop_m *m, + const struct timespec *abstime) +{ + coop_t *old, *t; +#ifdef ETIMEDOUT + int res = ETIMEDOUT; +#elif defined (WSAETIMEDOUT) + int res = WSAETIMEDOUT; #else -int -coop_condition_variable_wait_mutex (c, m) - coop_c *c; - coop_m *m; + int res = 0; #endif -{ - coop_mutex_unlock (m); - coop_condition_variable_wait (c); + + /* coop_mutex_unlock (m); */ + t = coop_qget (&(m->waiting)); + if (t != NULL) + { + m->owner = t; + } + else + { + m->owner = NULL; +#ifdef GUILE_ISELECT + coop_global_curr->timeoutp = 1; + coop_global_curr->wakeup_time.tv_sec = abstime->tv_sec; + coop_global_curr->wakeup_time.tv_usec = abstime->tv_nsec / 1000; + coop_timeout_qinsert (&coop_global_sleepq, coop_global_curr); + t = coop_wait_for_runnable_thread(); +#else + /*fixme* Implement!*/ + t = coop_next_runnable_thread(); +#endif + } + if (t != coop_global_curr) + { + coop_global_curr->top = &old; + old = coop_global_curr; + coop_global_curr = t; + QT_BLOCK (coop_yieldhelp, old, &(c->waiting), t->sp); + + /* Are we still in the sleep queue? */ + old = &coop_global_sleepq.t; + for (t = old->next; t != &coop_global_sleepq.t; old = t, t = t->next) + if (t == coop_global_curr) + { + old->next = t->next; /* unlink */ + res = 0; + break; + } + } coop_mutex_lock (m); - return 0; + return res; } - -#ifdef __STDC__ int coop_condition_variable_signal (coop_c *c) -#else -int -coop_condition_variable_signal (c) - coop_c *c; -#endif { coop_t *newthread; @@ -430,40 +483,176 @@ coop_condition_variable_signal (c) return 0; } +/* {Keys} + */ + +static int n_keys = 0; +static int max_keys = 0; +static void (**destructors) (void *) = 0; + +int +coop_key_create (coop_k *keyp, void (*destructor) (void *value)) +{ + if (n_keys >= max_keys) + { + int i; + max_keys = max_keys ? max_keys * 3 / 2 : 10; + destructors = realloc (destructors, sizeof (void *) * max_keys); + if (destructors == 0) + { + fprintf (stderr, "Virtual memory exceeded in coop_key_create\n"); + exit (1); + } + for (i = n_keys; i < max_keys; ++i) + destructors[i] = NULL; + } + destructors[n_keys] = destructor; + *keyp = n_keys++; + return 0; +} + +int +coop_setspecific (coop_k key, const void *value) +{ + int n_keys = coop_global_curr->n_keys; + if (key >= n_keys) + { + int i; + coop_global_curr->n_keys = max_keys; + coop_global_curr->specific = realloc (n_keys + ? coop_global_curr->specific + : NULL, + sizeof (void *) * max_keys); + if (coop_global_curr->specific == 0) + { + fprintf (stderr, "Virtual memory exceeded in coop_setspecific\n"); + exit (1); + } + for (i = n_keys; i < max_keys; ++i) + coop_global_curr->specific[i] = NULL; + } + coop_global_curr->specific[key] = (void *) value; + return 0; +} + +void * +coop_getspecific (coop_k key) +{ + return (key < coop_global_curr->n_keys + ? coop_global_curr->specific[key] + : NULL); +} + +int +coop_key_delete (coop_k key) +{ + return 0; +} + -#ifdef __STDC__ int coop_condition_variable_destroy (coop_c *c) +{ + return 0; +} + +#ifdef GUILE_PTHREAD_COMPAT + +/* 1K room for the cond wait routine */ +#ifdef SCM_STACK_GROWS_UP +#define COOP_STACK_ROOM (256) #else -int -coop_condition_variable_destroy (c) - coop_c *c; +#define COOP_STACK_ROOM (-256) #endif + +static void * +dummy_start (void *coop_thread) { + coop_t *t = (coop_t *) coop_thread; + int res; + t->sp = (qt_t *) (&t + COOP_STACK_ROOM); + pthread_mutex_init (&t->dummy_mutex, NULL); + pthread_mutex_lock (&t->dummy_mutex); + coop_child = 0; + do + res = pthread_cond_wait (&coop_cond_quit, &t->dummy_mutex); + while (res == EINTR); return 0; } +static void * +mother (void *dummy) +{ + pthread_mutex_lock (&coop_mutex_create); + while (!coop_quitting_p) + { + int res; + pthread_create (&coop_child->dummy_thread, + NULL, + dummy_start, + coop_child); + mother_awake_p = 0; + do + res = pthread_cond_wait (&coop_cond_create, &coop_mutex_create); + while (res == EINTR); + } + return 0; +} + +#endif -#ifdef __STDC__ coop_t * coop_create (coop_userf_t *f, void *pu) -#else -coop_t * -coop_create (f, pu) - coop_userf_t *f; - void *pu; -#endif { coop_t *t; +#ifndef GUILE_PTHREAD_COMPAT void *sto; +#endif - t = malloc (sizeof(coop_t)); - - t->data = NULL; - t->sto = malloc (COOP_STKSIZE); - sto = COOP_STKALIGN (t->sto, QT_STKALIGN); - t->sp = QT_SP (sto, COOP_STKSIZE - QT_STKALIGN); - t->base = t->sp; +#ifdef GUILE_PTHREAD_COMPAT + t = coop_qget (&coop_deadq); + if (t) + { + t->sp = t->base; + t->specific = 0; + t->n_keys = 0; + } + else +#endif + { + t = malloc (sizeof (coop_t)); + + t->specific = NULL; + t->n_keys = 0; +#ifdef GUILE_PTHREAD_COMPAT + coop_child = t; + mother_awake_p = 1; + if (coop_quitting_p < 0) + { + coop_quitting_p = 0; + /* We can't create threads ourselves since the pthread + * corresponding to this stack might be sleeping. + */ + pthread_create (&coop_mother, NULL, mother, NULL); + } + else + { + pthread_cond_signal (&coop_cond_create); + } + /* We can't use a pthreads condition variable since "this" + * pthread could already be asleep. We can't use a COOP + * condition variable because they are not safe against + * pre-emptive switching. + */ + while (coop_child || mother_awake_p) + usleep (0); +#else + t->sto = malloc (COOP_STKSIZE); + sto = COOP_STKALIGN (t->sto, QT_STKALIGN); + t->sp = QT_SP (sto, COOP_STKSIZE - QT_STKALIGN); +#endif + t->base = t->sp; + } t->sp = QT_ARGS (t->sp, pu, t, (qt_userf_t *)f, coop_only); t->joining = NULL; coop_qput (&coop_global_runq, t); @@ -473,16 +662,8 @@ coop_create (f, pu) } -#ifdef __STDC__ static void coop_only (void *pu, void *pt, qt_userf_t *f) -#else -static void -coop_only (pu. pt, f) - void *pu, - void *pt, - qt_userf_t *f; -#endif { coop_global_curr = (coop_t *)pt; (*(coop_userf_t *)f)(pu); @@ -491,13 +672,8 @@ coop_only (pu. pt, f) } -#ifdef __STDC__ void coop_abort () -#else -void -coop_abort () -#endif { coop_t *old, *newthread; @@ -509,7 +685,7 @@ coop_abort () { coop_qput (&coop_global_runq, newthread); } - free(coop_global_curr->joining); + free (coop_global_curr->joining); } #ifdef GUILE_ISELECT @@ -521,52 +697,36 @@ coop_abort () #else newthread = coop_next_runnable_thread(); #endif - coop_all_qremove(&coop_global_allq, coop_global_curr); + coop_all_qremove (&coop_global_allq, coop_global_curr); old = coop_global_curr; coop_global_curr = newthread; - QT_ABORT (coop_aborthelp, old, (void *)NULL, newthread->sp); + QT_ABORT (coop_aborthelp, old, (void *) NULL, newthread->sp); } -#ifdef __STDC__ static void * coop_aborthelp (qt_t *sp, void *old, void *null) -#else -static void * -coop_aborthelp (sp, old, null) - qt_t *sp; - void *old; - void *null; -#endif { coop_t *oldthread = (coop_t *) old; + if (oldthread->specific) + free (oldthread->specific); +#ifndef GUILE_PTHREAD_COMPAT free (oldthread->sto); - - /* "old" is freed in scm_threads_thread_die(). - Marking old->base NULL indicates that this thread is dead */ - - oldthread->base = NULL; - + free (oldthread); +#else + coop_qput (&coop_deadq, oldthread); +#endif + return NULL; } -#ifdef __STDC__ void coop_join(coop_t *t) -#else -void -coop_join() - coop_t *t; -#endif { coop_t *old, *newthread; - /* Check if t is already finished */ - if (t->base == NULL) - return; - /* Create a join list if necessary */ if (t->joining == NULL) { @@ -586,13 +746,8 @@ coop_join() QT_BLOCK (coop_yieldhelp, old, (coop_q_t *) t->joining, newthread->sp); } -#ifdef __STDC__ -void -coop_yield() -#else void coop_yield() -#endif { coop_t *old = NULL; coop_t *newthread; @@ -616,16 +771,8 @@ coop_yield() } -#ifdef __STDC__ static void * coop_yieldhelp (qt_t *sp, void *old, void *blockq) -#else -static void * -coop_yieldhelp (sp, old, blockq) - qt_t *sp; - void *old; - void *blockq; -#endif { ((coop_t *)old)->sp = sp; coop_qput ((coop_q_t *)blockq, (coop_t *)old); @@ -701,9 +848,14 @@ scm_thread_usleep (unsigned long usec) { /* We're so cheap. */ scm_thread_sleep (usec / 1000000); - struct timeval timeout; return 0; /* Maybe we should calculate actual time slept, but this is faster... :) */ } #endif /* GUILE_ISELECT */ + +/* + Local Variables: + c-file-style: "gnu" + End: +*/