From da328cb2c579c5b60725cbba903eff753061e17a Mon Sep 17 00:00:00 2001
From: "Michael I. Bushnell" <mib@gnu.org>
Date: Tue, 6 Oct 1992 18:31:16 +0000
Subject: Initial revision

---
 libthreads/cthreads.c | 451 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 451 insertions(+)
 create mode 100644 libthreads/cthreads.c

(limited to 'libthreads/cthreads.c')

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 */
+}
-- 
cgit v1.2.3