summaryrefslogtreecommitdiff
path: root/libguile/coop.c
diff options
context:
space:
mode:
authorJim Blandy <jimb@red-bean.com>1997-04-15 01:34:36 +0000
committerJim Blandy <jimb@red-bean.com>1997-04-15 01:34:36 +0000
commit7bfd3b9e94a3317ad49d5dfab0bf1fcf5d33d82b (patch)
tree29e1e7daa526d13b63071dd89974e5f8b4899ce5 /libguile/coop.c
parentc520b64ca6b111c598cff2237bfc41dc68a9e59a (diff)
downloadguile-7bfd3b9e94a3317ad49d5dfab0bf1fcf5d33d82b.tar.gz
Merge threads directory into libguile.
* coop-defs.h, coop-threads.c, coop-threads.h, coop.c, threads.c, threads.h: New source files. * Makefile.am (EXTRA_libguile_la_SOURCES): Add threads.c. (noinst_HEADERS): Add coop-threads.c, coop-threads.h, coop.c here; see comment. (modinclude_HEADERS): Add threads.h, coop-defs.h. (EXTRA_DIST): Add fsu-pthreads.h, mit-pthreads.c, mit-pthreads.h, coop-threads.c.cygnus, coop-threads.h.cygnus. * configure.in: If we're using threads, include threads.o in LIBOBJS. * _scm.h, libguile.h: threads.h lives in this directory now. * fsu-pthreads.h, mit-pthreads.c, mit-pthreads.h, coop-threads.c.cygnus, coop-threads.h.cygnus: New files, not currently used, but brought along for information's sake. * ChangeLog-threads: log from old 'threads' directory. * Makefile.in, configure: Rebuilt.
Diffstat (limited to 'libguile/coop.c')
-rw-r--r--libguile/coop.c588
1 files changed, 588 insertions, 0 deletions
diff --git a/libguile/coop.c b/libguile/coop.c
new file mode 100644
index 000000000..56f7175d2
--- /dev/null
+++ b/libguile/coop.c
@@ -0,0 +1,588 @@
+/* Copyright (C) 1995, 1996 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
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * As a special exception, the Free Software Foundation gives permission
+ * for additional uses of the text contained in its release of GUILE.
+ *
+ * The exception is that, if you link the GUILE library with other files
+ * to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public License.
+ * Your use of that executable is in no way restricted on account of
+ * linking the GUILE library code into it.
+ *
+ * This exception does not however invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public License.
+ *
+ * This exception applies only to the code released by the
+ * Free Software Foundation under the name GUILE. If you copy
+ * code from other Free Software Foundation releases into a copy of
+ * GUILE, as the General Public License permits, the exception does
+ * not apply to the code that you add in this way. To avoid misleading
+ * anyone as to the status of such modified files, you must delete
+ * this exception notice from them.
+ *
+ * If you write modifications of your own for GUILE, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ */
+
+
+/* $Id: coop.c,v 1.1 1997-04-15 01:34:30 jimb Exp $ */
+
+/* Cooperative thread library, based on QuickThreads */
+
+#include <qt.h>
+
+#define COOP_STKSIZE (0x10000)
+
+/* `alignment' must be a power of 2. */
+#define COOP_STKALIGN(sp, alignment) \
+((void *)((((qt_word_t)(sp)) + (alignment) - 1) & ~((alignment)-1)))
+
+
+
+/* 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;
+
+ q->t.all_prev = NULL;
+ q->t.all_next = NULL;
+}
+
+
+#ifdef __STDC__
+static coop_t *
+coop_qget (coop_q_t *q)
+#else
+static 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. */
+ }
+ q->tail = &q->t; /* Else now it is empty. */
+ }
+ return (t);
+}
+
+
+#ifdef __STDC__
+static void
+coop_qput (coop_q_t *q, coop_t *t)
+#else
+static 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;
+ t->all_prev = NULL;
+ t->all_next = q->t.all_next;
+ 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;
+ else
+ q->t.all_next = t->all_next;
+ if (t->all_next)
+ t->all_next->all_prev = t->all_prev;
+}
+
+
+ /* Thread routines. */
+
+coop_q_t coop_global_runq; /* A queue of runable threads. */
+coop_q_t coop_global_sleepq; /* A queue of sleeping threads. */
+static coop_q_t tmp_queue; /* A temp working queue */
+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. */
+
+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()
+#else
+void
+coop_init()
+#endif
+{
+ coop_qinit (&coop_global_runq);
+ coop_qinit (&coop_global_sleepq);
+ coop_qinit (&tmp_queue);
+ coop_qinit (&coop_global_allq);
+ coop_global_curr = &coop_global_main;
+}
+
+
+/* Return the next runnable thread. If no threads are currently runnable,
+ and there are sleeping threads - wait until one wakes up. Otherwise,
+ return NULL. */
+
+#ifdef __STDC__
+coop_t *
+coop_next_runnable_thread()
+#else
+coop_t *
+coop_next_runnable_thread()
+#endif
+{
+ int sleepers;
+ coop_t *t;
+ time_t now;
+
+ do {
+ sleepers = 0;
+ now = time(NULL);
+
+ /* Check the sleeping queue */
+ while ((t = coop_qget(&coop_global_sleepq)) != NULL)
+ {
+ sleepers++;
+ if (t->wakeup_time <= now)
+ coop_qput(&coop_global_runq, t);
+ else
+ coop_qput(&tmp_queue, t);
+ }
+ while ((t = coop_qget(&tmp_queue)) != NULL)
+ coop_qput(&coop_global_sleepq, t);
+
+ t = coop_qget (&coop_global_runq);
+
+ } while ((t == NULL) && (sleepers > 0));
+
+ return t;
+}
+
+
+#ifdef __STDC__
+void
+coop_start()
+#else
+void
+coop_start()
+#endif
+{
+ coop_t *next;
+
+ while ((next = coop_qget (&coop_global_runq)) != NULL) {
+ coop_global_curr = next;
+ QT_BLOCK (coop_starthelp, 0, 0, next->sp);
+ }
+}
+
+
+#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;
+ coop_qput (&coop_global_runq, &coop_global_main);
+ return NULL; /* not used, but keeps compiler happy */
+}
+
+#ifdef __STDC__
+void
+coop_mutex_init (coop_m *m)
+#else
+void
+coop_mutex_init (m)
+ coop_m *m;
+#endif
+{
+ m->owner = NULL;
+ coop_qinit(&(m->waiting));
+}
+
+#ifdef __STDC__
+void
+coop_mutex_lock (coop_m *m)
+#else
+void
+coop_mutex_lock ()
+ coop_m *m;
+#endif
+{
+ if (m->owner == NULL)
+ {
+ m->owner = coop_global_curr;
+ }
+ else
+ {
+ coop_t *old, *newthread;
+
+ /* Record the current top-of-stack before going to sleep */
+ coop_global_curr->top = &old;
+
+ newthread = coop_next_runnable_thread();
+ old = coop_global_curr;
+ coop_global_curr = newthread;
+ QT_BLOCK (coop_yieldhelp, old, &(m->waiting), newthread->sp);
+ }
+}
+
+
+#ifdef __STDC__
+void
+coop_mutex_unlock (coop_m *m)
+#else
+void
+coop_mutex_unlock (m)
+ coop_m *m;
+#endif
+{
+ coop_t *old, *newthread;
+
+ newthread = coop_qget (&(m->waiting));
+ if (newthread != NULL)
+ {
+ /* Record the current top-of-stack before going to sleep */
+ coop_global_curr->top = &old;
+
+ old = coop_global_curr;
+ coop_global_curr = newthread;
+ m->owner = coop_global_curr;
+ QT_BLOCK (coop_yieldhelp, old, &coop_global_runq, newthread->sp);
+ }
+ else
+ {
+ m->owner = NULL;
+ }
+}
+
+
+#ifdef __STDC__
+void
+coop_condition_variable_init (coop_c *c)
+#else
+void
+coop_condition_variable_init (c)
+ coop_c *c;
+#endif
+{
+ coop_qinit(&(c->waiting));
+}
+
+#ifdef __STDC__
+void
+coop_condition_variable_wait (coop_c *c)
+#else
+void
+coop_condition_variable_wait (c)
+ coop_c *c;
+#endif
+{
+ coop_t *old, *newthread;
+
+ newthread = coop_next_runnable_thread();
+ old = coop_global_curr;
+ coop_global_curr = newthread;
+ QT_BLOCK (coop_yieldhelp, old, &(c->waiting), newthread->sp);
+}
+
+#ifdef __STDC__
+void
+coop_condition_variable_signal (coop_c *c)
+#else
+void
+coop_condition_variable_signal (c)
+ coop_c *c;
+#endif
+{
+ coop_t *newthread;
+
+ while ((newthread = coop_qget (&(c->waiting))) != NULL)
+ {
+ coop_qput (&coop_global_runq, newthread);
+ }
+}
+
+
+#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;
+ void *sto;
+
+ 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;
+ t->sp = QT_ARGS (t->sp, pu, t, (qt_userf_t *)f, coop_only);
+ t->joining = NULL;
+ coop_qput (&coop_global_runq, t);
+ coop_all_qput (&coop_global_allq, t);
+
+ return t;
+}
+
+
+#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);
+ coop_abort();
+ /* NOTREACHED */
+}
+
+
+#ifdef __STDC__
+void
+coop_abort ()
+#else
+void
+coop_abort ()
+#endif
+{
+ coop_t *old, *newthread;
+
+ /* Wake up any threads that are waiting to join this one */
+ if (coop_global_curr->joining)
+ {
+ while ((newthread = coop_qget ((coop_q_t *)(coop_global_curr->joining)))
+ != NULL)
+ {
+ coop_qput (&coop_global_runq, newthread);
+ }
+ free(coop_global_curr->joining);
+ }
+
+ newthread = coop_next_runnable_thread();
+ 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);
+}
+
+
+#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;
+
+ free (oldthread->sto);
+
+ /* "old" is freed in scm_threads_thread_die().
+ Marking old->base NULL indicates that this thread is dead */
+
+ oldthread->base = NULL;
+
+ 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)
+ {
+ t->joining = malloc(sizeof(coop_q_t));
+ coop_qinit((coop_q_t *) t->joining);
+ }
+
+ newthread = coop_next_runnable_thread();
+ old = coop_global_curr;
+ coop_global_curr = newthread;
+ 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;
+
+ newthread = coop_next_runnable_thread();
+
+ /* There may be no other runnable threads. Return if this is the
+ case. */
+ if (newthread == NULL)
+ return;
+
+ old = coop_global_curr;
+
+ coop_global_curr = newthread;
+ QT_BLOCK (coop_yieldhelp, old, &coop_global_runq, newthread->sp);
+}
+
+
+#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);
+ return NULL;
+}
+
+/* Replacement for the system's sleep() function. Does the right thing
+ for the process - but not for the system (it busy-waits) */
+
+#ifdef __STDC__
+static void *
+coop_sleephelp (qt_t *sp, void *old, void *blockq)
+#else
+static void *
+coop_sleephelp (sp, old, bolckq)
+ qt_t *sp;
+ void *old;
+ void *blockq;
+#endif
+{
+ ((coop_t *)old)->sp = sp;
+ /* old is already on the sleep queue - so there's no need to
+ do anything extra here */
+ return NULL;
+}
+
+#ifdef __STDC__
+unsigned
+sleep (unsigned s)
+#else
+unsigned
+sleep (s)
+ unsigned s;
+#endif
+{
+ coop_t *newthread, *old;
+ time_t now = time(NULL);
+ coop_global_curr->wakeup_time = now + s;
+
+ /* Put the current thread on the sleep queue */
+ coop_qput (&coop_global_sleepq, coop_global_curr);
+
+ newthread = coop_next_runnable_thread();
+
+ /* If newthread is the same as the sleeping thread, do nothing */
+ if (newthread == coop_global_curr)
+ return s;
+
+ old = coop_global_curr;
+
+ coop_global_curr = newthread;
+ QT_BLOCK (coop_sleephelp, old, NULL, newthread->sp);
+
+ return s;
+}