summaryrefslogtreecommitdiff
path: root/libthreads/cthreads.c
diff options
context:
space:
mode:
authorMichael I. Bushnell <mib@gnu.org>1992-10-06 18:31:16 +0000
committerMichael I. Bushnell <mib@gnu.org>1992-10-06 18:31:16 +0000
commitda328cb2c579c5b60725cbba903eff753061e17a (patch)
treec8f6634ad0298e628fa96bbd94f58dd504ef5d55 /libthreads/cthreads.c
parent964ab87456cf79f0f5787796c3e8917502d9f9d8 (diff)
Initial revision
Diffstat (limited to 'libthreads/cthreads.c')
-rw-r--r--libthreads/cthreads.c451
1 files changed, 451 insertions, 0 deletions
diff --git a/libthreads/cthreads.c b/libthreads/cthreads.c
new file mode 100644
index 00000000..1964c335
--- /dev/null
+++ b/libthreads/cthreads.c
@@ -0,0 +1,451 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * HISTORY
+ * $Log: cthreads.c,v $
+ * Revision 2.11 92/07/20 13:33:37 cmaeda
+ * In cthread_init, do machine dependent initialization if it's defined.
+ * [92/05/11 14:41:08 cmaeda]
+ *
+ * Revision 2.10 91/08/28 11:19:26 jsb
+ * Fixed mig_init initialization in cthread_fork_child.
+ * [91/08/23 rpd]
+ *
+ * Revision 2.9 91/07/31 18:34:23 dbg
+ * Fix bad self-pointer reference.
+ *
+ * Don't declare _setjmp and _longjmp; they are included by
+ * cthreads.h.
+ * [91/07/30 17:33:50 dbg]
+ *
+ * Revision 2.8 91/05/14 17:56:31 mrt
+ * Correcting copyright
+ *
+ * Revision 2.7 91/02/14 14:19:47 mrt
+ * Added new Mach copyright
+ * [91/02/13 12:41:07 mrt]
+ *
+ * Revision 2.6 90/11/05 14:37:03 rpd
+ * Added cthread_fork_{prepare,parent,child}.
+ * [90/11/02 rwd]
+ *
+ * Add spin_lock_t.
+ * [90/10/31 rwd]
+ *
+ * Revision 2.5 90/08/07 14:30:58 rpd
+ * Removed RCS keyword nonsense.
+ *
+ * Revision 2.4 90/06/02 15:13:49 rpd
+ * Converted to new IPC.
+ * [90/03/20 20:56:44 rpd]
+ *
+ * Revision 2.3 90/01/19 14:37:12 rwd
+ * Make cthread_init return pointer to new stack.
+ * [89/12/18 19:17:45 rwd]
+ *
+ * Revision 2.2 89/12/08 19:53:37 rwd
+ * Change cproc and cthread counters to globals with better names.
+ * [89/11/02 rwd]
+ *
+ * Revision 2.1 89/08/03 17:09:34 rwd
+ * Created.
+ *
+ *
+ * 31-Dec-87 Eric Cooper (ecc) at Carnegie Mellon University
+ * Changed cthread_exit() logic for the case of the main thread,
+ * to fix thread and stack memory leak found by Camelot group.
+ *
+ * 21-Aug-87 Eric Cooper (ecc) at Carnegie Mellon University
+ * Added consistency check in beginning of cthread_body().
+ *
+ * 11-Aug-87 Eric Cooper (ecc) at Carnegie Mellon University
+ * Removed cthread_port() and cthread_set_port().
+ * Removed port deallocation from cthread_free().
+ * Minor changes to cthread_body(), cthread_exit(), and cthread_done().
+ *
+ * 10-Aug-87 Eric Cooper (ecc) at Carnegie Mellon University
+ * Changed call to mig_init() in cthread_init() to pass 1 as argument.
+ *
+ * 31-Jul-87 Eric Cooper (ecc) at Carnegie Mellon University
+ * Added call to mig_init() from cthread_init().
+ */
+/*
+ * File: cthreads.c
+ * Author: Eric Cooper, Carnegie Mellon University
+ * Date: July, 1987
+ *
+ * Implementation of fork, join, exit, etc.
+ */
+
+#include <cthreads.h>
+#include "cthread_internals.h"
+
+/*
+ * C Threads imports:
+ */
+extern void cproc_create();
+extern vm_offset_t cproc_init();
+extern void mig_init();
+
+/*
+ * Mach imports:
+ */
+
+/*
+ * C library imports:
+ */
+
+/*
+ * Thread status bits.
+ */
+#define T_MAIN 0x1
+#define T_RETURNED 0x2
+#define T_DETACHED 0x4
+
+#ifdef DEBUG
+int cthread_debug = FALSE;
+#endif DEBUG
+
+private struct cthread_queue cthreads = QUEUE_INITIALIZER;
+private struct mutex cthread_lock = MUTEX_INITIALIZER;
+private struct condition cthread_needed = CONDITION_INITIALIZER;
+private struct condition cthread_idle = CONDITION_INITIALIZER;
+int cthread_cprocs = 0;
+int cthread_cthreads = 0;
+int cthread_max_cprocs = 0;
+
+private cthread_t free_cthreads = NO_CTHREAD; /* free list */
+private spin_lock_t free_lock = SPIN_LOCK_INITIALIZER; /* unlocked */
+
+private struct cthread initial_cthread = { 0 };
+
+private cthread_t
+cthread_alloc(func, arg)
+ cthread_fn_t func;
+ any_t arg;
+{
+ register cthread_t t = NO_CTHREAD;
+
+ if (free_cthreads != NO_CTHREAD) {
+ /*
+ * Don't try for the lock unless
+ * the list is likely to be nonempty.
+ * We can't be sure, though, until we lock it.
+ */
+ spin_lock(&free_lock);
+ t = free_cthreads;
+ if (t != NO_CTHREAD)
+ free_cthreads = t->next;
+ spin_unlock(&free_lock);
+ }
+ if (t == NO_CTHREAD) {
+ /*
+ * The free list was empty.
+ * We may have only found this out after
+ * locking it, which is why this isn't an
+ * "else" branch of the previous statement.
+ */
+ t = (cthread_t) malloc(sizeof(struct cthread));
+ }
+ *t = initial_cthread;
+ t->func = func;
+ t->arg = arg;
+ return t;
+}
+
+private void
+cthread_free(t)
+ register cthread_t t;
+{
+ spin_lock(&free_lock);
+ t->next = free_cthreads;
+ free_cthreads = t;
+ spin_unlock(&free_lock);
+}
+
+int
+cthread_init()
+{
+ static int cthreads_started = FALSE;
+ register cproc_t p;
+ register cthread_t t;
+ vm_offset_t stack;
+
+ if (cthreads_started)
+ return 0;
+ stack = cproc_init();
+ cthread_cprocs = 1;
+ t = cthread_alloc((cthread_fn_t) 0, (any_t) 0);
+
+#ifdef cthread_md_init
+ cthread_md_init();
+#endif
+
+ cthread_cthreads = 1;
+ t->state |= T_MAIN;
+ cthread_set_name(t, "main");
+
+ /* cproc_self() doesn't work yet, because
+ we haven't yet switched to the new stack. */
+
+ p = *(cproc_t *)&ur_cthread_ptr(stack);
+ p->incarnation = t;
+ mig_init(p); /* enable multi-threaded mig interfaces */
+
+ cthreads_started = TRUE;
+ return stack;
+}
+
+/*
+ * Used for automatic initialization by crt0.
+ * Cast needed since too many C compilers choke on the type void (*)().
+ */
+int (*_cthread_init_routine)() = (int (*)()) cthread_init;
+
+/*
+ * Procedure invoked at the base of each cthread.
+ */
+void
+cthread_body(self)
+ cproc_t self;
+{
+ register cthread_t t;
+
+ ASSERT(cproc_self() == self);
+ TRACE(printf("[idle] cthread_body(%x)\n", self));
+ mutex_lock(&cthread_lock);
+ for (;;) {
+ /*
+ * Dequeue a thread invocation request.
+ */
+ cthread_queue_deq(&cthreads, cthread_t, t);
+ if (t != NO_CTHREAD) {
+ /*
+ * We have a thread to execute.
+ */
+ mutex_unlock(&cthread_lock);
+ cthread_assoc(self, t); /* assume thread's identity */
+ if (_setjmp(t->catch) == 0) { /* catch for cthread_exit() */
+ /*
+ * Execute the fork request.
+ */
+ t->result = (*(t->func))(t->arg);
+ }
+ /*
+ * Return result from thread.
+ */
+ TRACE(printf("[%s] done()\n", cthread_name(t)));
+ mutex_lock(&t->lock);
+ if (t->state & T_DETACHED) {
+ mutex_unlock(&t->lock);
+ cthread_free(t);
+ } else {
+ t->state |= T_RETURNED;
+ mutex_unlock(&t->lock);
+ condition_signal(&t->done);
+ }
+ cthread_assoc(self, NO_CTHREAD);
+ mutex_lock(&cthread_lock);
+ cthread_cthreads -= 1;
+ } else {
+ /*
+ * Queue is empty.
+ * Signal that we're idle in case the main thread
+ * is waiting to exit, then wait for reincarnation.
+ */
+ condition_signal(&cthread_idle);
+ condition_wait(&cthread_needed, &cthread_lock);
+ }
+ }
+}
+
+cthread_t
+cthread_fork(func, arg)
+ cthread_fn_t func;
+ any_t arg;
+{
+ register cthread_t t;
+
+ TRACE(printf("[%s] fork()\n", cthread_name(cthread_self())));
+ mutex_lock(&cthread_lock);
+ t = cthread_alloc(func, arg);
+ cthread_queue_enq(&cthreads, t);
+ if (++cthread_cthreads > cthread_cprocs && (cthread_max_cprocs == 0 || cthread_cprocs < cthread_max_cprocs)) {
+ cthread_cprocs += 1;
+ cproc_create();
+ }
+ mutex_unlock(&cthread_lock);
+ condition_signal(&cthread_needed);
+ return t;
+}
+
+void
+cthread_detach(t)
+ cthread_t t;
+{
+ TRACE(printf("[%s] detach(%s)\n", cthread_name(cthread_self()), cthread_name(t)));
+ mutex_lock(&t->lock);
+ if (t->state & T_RETURNED) {
+ mutex_unlock(&t->lock);
+ cthread_free(t);
+ } else {
+ t->state |= T_DETACHED;
+ mutex_unlock(&t->lock);
+ }
+}
+
+any_t
+cthread_join(t)
+ cthread_t t;
+{
+ any_t result;
+
+ TRACE(printf("[%s] join(%s)\n", cthread_name(cthread_self()), cthread_name(t)));
+ mutex_lock(&t->lock);
+ ASSERT(! (t->state & T_DETACHED));
+ while (! (t->state & T_RETURNED))
+ condition_wait(&t->done, &t->lock);
+ result = t->result;
+ mutex_unlock(&t->lock);
+ cthread_free(t);
+ return result;
+}
+
+void
+cthread_exit(result)
+ any_t result;
+{
+ register cthread_t t = cthread_self();
+
+ TRACE(printf("[%s] exit()\n", cthread_name(t)));
+ t->result = result;
+ if (t->state & T_MAIN) {
+ mutex_lock(&cthread_lock);
+ while (cthread_cthreads > 1)
+ condition_wait(&cthread_idle, &cthread_lock);
+ mutex_unlock(&cthread_lock);
+ exit((int) result);
+ } else {
+ _longjmp(t->catch, TRUE);
+ }
+}
+
+/*
+ * Used for automatic finalization by crt0. Cast needed since too many C
+ * compilers choke on the type void (*)().
+ */
+int (*_cthread_exit_routine)() = (int (*)()) cthread_exit;
+
+void
+cthread_set_name(t, name)
+ cthread_t t;
+ char *name;
+{
+ t->name = name;
+}
+
+char *
+cthread_name(t)
+ cthread_t t;
+{
+ return (t == NO_CTHREAD
+ ? "idle"
+ : (t->name == 0 ? "?" : t->name));
+}
+
+int
+cthread_limit()
+{
+ return cthread_max_cprocs;
+}
+
+void
+cthread_set_limit(n)
+ int n;
+{
+ cthread_max_cprocs = n;
+}
+
+int
+cthread_count()
+{
+ return cthread_cthreads;
+}
+
+cthread_fork_prepare()
+{
+ spin_lock(&free_lock);
+ mutex_lock(&cthread_lock);
+ malloc_fork_prepare();
+ cproc_fork_prepare();
+}
+
+cthread_fork_parent()
+{
+ cproc_fork_parent();
+ malloc_fork_parent();
+ mutex_unlock(&cthread_lock);
+ spin_unlock(&free_lock);
+}
+
+cthread_fork_child()
+{
+ cthread_t t;
+ cproc_t p;
+
+ cproc_fork_child();
+ malloc_fork_child();
+ mutex_unlock(&cthread_lock);
+ spin_unlock(&free_lock);
+ condition_init(&cthread_needed);
+ condition_init(&cthread_idle);
+
+ cthread_max_cprocs = 0;
+
+ stack_fork_child();
+
+ while (TRUE) { /* Free cthread runnable list */
+ cthread_queue_deq(&cthreads, cthread_t, t);
+ if (t == NO_CTHREAD) break;
+ free((char *) t);
+ }
+
+ while (free_cthreads != NO_CTHREAD) { /* Free cthread free list */
+ t = free_cthreads;
+ free_cthreads = free_cthreads->next;
+ free((char *) t);
+ }
+
+ cthread_cprocs = 1;
+ t = cthread_self();
+ cthread_cthreads = 1;
+ t->state |= T_MAIN;
+ cthread_set_name(t, "main");
+
+ p = cproc_self();
+ p->incarnation = t;
+ mig_init(p); /* enable multi-threaded mig interfaces */
+}