summaryrefslogtreecommitdiff
path: root/libthreads
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
parent964ab87456cf79f0f5787796c3e8917502d9f9d8 (diff)
Initial revision
Diffstat (limited to 'libthreads')
-rw-r--r--libthreads/cprocs.c1122
-rw-r--r--libthreads/cthread_internals.h198
-rw-r--r--libthreads/cthreads.c451
-rw-r--r--libthreads/cthreads.h556
-rw-r--r--libthreads/i386/csw.S139
-rw-r--r--libthreads/i386/thread.c114
-rw-r--r--libthreads/malloc.c349
-rw-r--r--libthreads/mig_support.c157
-rw-r--r--libthreads/stack.c382
9 files changed, 3468 insertions, 0 deletions
diff --git a/libthreads/cprocs.c b/libthreads/cprocs.c
new file mode 100644
index 00000000..e721abd2
--- /dev/null
+++ b/libthreads/cprocs.c
@@ -0,0 +1,1122 @@
+/*
+ * 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: cprocs.c,v $
+ * Revision 2.15 92/03/06 14:09:31 rpd
+ * Replaced swtch_pri with yield.
+ * [92/03/06 rpd]
+ *
+ * Revision 2.14 91/08/28 11:19:16 jsb
+ * Fixed the loop in cproc_fork_child that frees cprocs.
+ * [91/08/23 rpd]
+ *
+ * Revision 2.13 91/07/31 18:33:04 dbg
+ * Fix some more bad types. Ints are NOT pointers.
+ *
+ * Fix argument type mismatch in cproc_create.
+ * [91/07/30 17:32:59 dbg]
+ *
+ * Revision 2.12 91/05/14 17:56:11 mrt
+ * Correcting copyright
+ *
+ * Revision 2.11 91/02/14 14:19:26 mrt
+ * Added new Mach copyright
+ * [91/02/13 12:40:50 mrt]
+ *
+ * Revision 2.10 90/11/05 14:36:41 rpd
+ * Added cproc_fork_{prepare,parent,child}.
+ * [90/11/02 rwd]
+ *
+ * Fix for positive stack growth.
+ * [90/11/01 rwd]
+ *
+ * Add spin_lock_t.
+ * [90/10/31 rwd]
+ *
+ * Revision 2.9 90/10/12 13:07:12 rpd
+ * Fix type
+ * [90/10/10 15:09:59 rwd]
+ *
+ * Comment code.
+ * [90/10/02 rwd]
+ *
+ * Revision 2.8 90/09/09 14:34:44 rpd
+ * Remove special mutex. Remove thread_calls and debug_mutex
+ * [90/08/24 rwd]
+ * Fix up old call to cthread_msg_busy to new format.
+ * [90/08/22 rwd]
+ *
+ * Revision 2.7 90/08/06 15:09:17 rwd
+ * Fixed arguments to cthread_mach_msg.
+ * [90/06/26 rwd]
+ * Add additional STATISTICS.
+ * [90/06/07 rwd]
+ *
+ * Attempt to reduce number of times a cthread is released to to a
+ * msg_receive by adding min/max instead of single number to
+ * cthread_msg calls.
+ * [90/06/06 rwd]
+ *
+ * Revision 2.6 90/06/02 15:13:36 rpd
+ * Converted to new IPC.
+ * [90/03/20 20:46:16 rpd]
+ *
+ * Revision 2.5 90/05/29 18:40:11 rwd
+ * Don't incr special field until the mutex grab is successful.
+ * [90/05/09 rwd]
+ *
+ * Revision 2.4 90/03/14 21:12:02 rwd
+ * Added WAIT_DEBUG code for deadlock debugging.
+ * [90/03/01 rwd]
+ * Insert cprocs in cproc_list as allocated.
+ * [90/03/01 10:20:16 rwd]
+ *
+ * Revision 2.3 90/01/19 14:36:57 rwd
+ * Make cthread_msg_busy only release new thread if this is still
+ * busy. Ie don't release two on back to back calls.
+ * [90/01/11 rwd]
+ * Add THREAD_CALL code. Add CPROC_ARUN state.
+ * [90/01/03 rwd]
+ * Add new cthread_msg_rpc call
+ * [89/12/20 rwd]
+ * Change cproc_self pointer to top of stack. Now need to change
+ * the stack of the first thread.
+ * [89/12/12 rwd]
+ *
+ * Revision 2.2 89/12/08 19:53:13 rwd
+ * Added CPROC_CONDWAIT state to deal with lock held
+ * across mutex_unlock problem.
+ * [89/11/29 rwd]
+ * Changed mutexes to not hand off. MUTEX_EXTRA conditional is
+ * now obsolete.
+ * [89/11/27 rwd]
+ *
+ * Add MUTEX_EXTRA code for extra kernel threads to serve special
+ * mutexes in time of need.
+ * [89/11/25 rwd]
+ * Add MUTEX_SPECIAL and DEBUG_MUTEX code
+ * [89/11/24 rwd]
+ * Changed mutex_lock to mutex_lock_solid. Mutex_lock is now a
+ * macro which tries the spin_lock before making a subroutine call.
+ * Mutex_unlock is now a macro with mutex_unlock_solid for worst case.
+ * [89/11/13 rwd]
+ *
+ * Rewrite most to merge coroutine and thread implementation.
+ * New routines are cthread_set_kernel_limit, cthread_kernel_limit,
+ * cthread_wire, cthread_unwire, and cthread_receive.
+ * [89/10/23 rwd]
+ *
+ * Revision 2.1 89/08/03 17:07:10 rwd
+ * Created.
+ *
+ * 11-Apr-89 David Golub (dbg) at Carnegie-Mellon University
+ * Made condition_yield loop break if swtch_pri returns TRUE (in
+ * case we fix it).
+ *
+ * 31-Mar-89 David Golub (dbg) at Carnegie-Mellon University
+ * Change cond_signal, cond_broadcast, and cproc_continue so that
+ * the condition's spin lock is not held while continuing the
+ * process.
+ *
+ * 16-Jan-89 David Golub (dbg) at Carnegie-Mellon University
+ * Changes for stand-alone library to run on pure kernel:
+ * . made IPC_WAIT standard, as calls that are used if IPC_WAIT == 0
+ * vanished a year ago.
+ * . Removed (as much as possible) references to stdio or other U*X
+ * features.
+ *
+ *
+ * 01-Apr-88 Eric Cooper (ecc) at Carnegie Mellon University
+ * Changed condition_clear(c) to acquire c->lock,
+ * to serialize after any threads still doing condition_signal(c).
+ * Suggested by Dan Julin.
+ *
+ * 19-Feb-88 Eric Cooper (ecc) at Carnegie Mellon University
+ * Extended the inline scripts to handle spin_unlock() and mutex_unlock().
+ *
+ * 28-Jan-88 David Golub (dbg) at Carnegie Mellon University
+ * Removed thread_data argument from thread_create
+ * and converted to new thread_set_state call.
+ *
+ * 01-Dec-87 Eric Cooper (ecc) at Carnegie Mellon University
+ * Added inline expansion for cthread_sp() function.
+ *
+ * 21-Aug-87 Eric Cooper (ecc) at Carnegie Mellon University
+ * Fixed uninitialized reply_port in cproc_alloc() (found by rds).
+ *
+ * 14-Aug-87 Eric Cooper (ecc) at Carnegie Mellon University
+ * Tried using return value of swtch() to guide condition_wait().
+ * Performance was worse than using a hybrid spin/yield/block
+ * scheme, so the version using swtch() was commented out.
+ * Disabled IPC_WAIT in released version.
+ *
+ * 13-Aug-87 Eric Cooper (ecc) at Carnegie Mellon University
+ * Added IPC_WAIT option.
+ * If defined, thread synchronization (condition_wait() and
+ * cproc_continue()) are implemented using msg_receive() and
+ * msg_send() instead of thread_suspend() and thread_resume().
+ *
+ * 11-Aug-87 Eric Cooper (ecc) at Carnegie Mellon University
+ * Moved thread reply port to cproc structure in cthread_internals.h,
+ * because mig calls are made while cproc is idle (no cthread structure).
+ * Changed cproc_switch() and cproc_start (COROUTINE implementation)
+ * to use address of saved context, rather than address of enclosing cproc,
+ * to eliminate dependency on cproc layout.
+ */
+/*
+ * File: cprocs.c
+ * Author: Eric Cooper, Carnegie Mellon University
+ * Date: Aug, 1987
+ *
+ * Implementation of cprocs (lightweight processes)
+ * and primitive synchronization operations.
+ */
+
+
+#include <cthreads.h>
+#include "cthread_internals.h"
+#include <mach/message.h>
+
+/*
+ * C Threads imports:
+ */
+extern void alloc_stack();
+extern void cproc_switch(); /* cproc context switch */
+extern void cproc_start_wait(); /* cproc idle thread */
+extern vm_offset_t cproc_stack_base(); /* return start of stack */
+extern vm_offset_t stack_init();
+
+/*
+ * Port_entry's are used by cthread_mach_msg to store information
+ * about each port/port_set for which it is managing threads
+ */
+
+typedef struct port_entry {
+ struct port_entry *next; /* next port_entry */
+ mach_port_t port; /* which port/port_set */
+ struct cthread_queue queue; /* queue of runnable threads for
+ this port/port_set */
+ int min; /* minimum number of kernel threads
+ to be used by this port/port_set */
+ int max; /* maximum number of kernel threads
+ to be used by this port/port_set */
+ int held; /* actual number of kernel threads
+ currentlt in use */
+ spin_lock_t lock; /* lock governing all above fields */
+} *port_entry_t;
+
+#define PORT_ENTRY_NULL ((port_entry_t) 0)
+
+/* Available to outside for statistics */
+
+int cthread_wait_stack_size = 8192; /* stack size for idle threads */
+int cthread_max_kernel_threads = 0; /* max kernel threads */
+int cthread_kernel_threads = 0; /* current kernel threads */
+private spin_lock_t n_kern_lock = SPIN_LOCK_INITIALIZER;
+ /* lock for 2 above */
+#ifdef STATISTICS
+int cthread_ready = 0; /* currently runnable */
+int cthread_running = 1; /* currently running */
+int cthread_waiting = 0; /* currently waiting */
+int cthread_wired = 0; /* currently wired */
+private spin_lock_t wired_lock = SPIN_LOCK_INITIALIZER;
+ /* lock for above */
+int cthread_wait_stacks = 0; /* total cthread waiting stacks */
+int cthread_waiters = 0; /* total of watiers */
+int cthread_wakeup = 0; /* total times woken when starting to
+ block */
+int cthread_blocked = 0; /* total blocked */
+int cthread_rnone = 0; /* total times no cthread available
+ to meet minimum for port_entry */
+int cthread_yields = 0; /* total cthread_yields */
+int cthread_none = 0; /* total idle wakeups w/o runnable */
+int cthread_switches = 0; /* total number of cproc_switches */
+int cthread_no_mutex = 0; /* total number times woken to get
+ mutex and couldn't */
+private spin_lock_t mutex_count_lock = SPIN_LOCK_INITIALIZER;
+ /* lock for above */
+#endif STATISTICS
+
+cproc_t cproc_list = NO_CPROC; /* list of all cprocs */
+private cproc_list_lock = SPIN_LOCK_INITIALIZER;
+ /* lock for above */
+private int cprocs_started = FALSE; /* initialized? */
+private struct cthread_queue ready = QUEUE_INITIALIZER;
+ /* ready queue */
+private int ready_count = 0; /* number of ready threads on ready
+ queue - number of messages sent */
+private spin_lock_t ready_lock = SPIN_LOCK_INITIALIZER;
+ /* lock for 2 above */
+private mach_port_t wait_port = MACH_PORT_NULL;
+ /* port on which idle threads wait */
+private int wait_count = 0; /* number of waiters - messages pending
+ to wake them */
+private struct cthread_queue waiters = QUEUE_INITIALIZER;
+ /* queue of cthreads to run as idle */
+private spin_lock_t waiters_lock = SPIN_LOCK_INITIALIZER;
+ /* lock for 2 above */
+private port_entry_t port_list = PORT_ENTRY_NULL;
+ /* master list of port_entries */
+private spin_lock_t port_lock = SPIN_LOCK_INITIALIZER;
+ /* lock for above queue */
+private mach_msg_header_t wakeup_msg; /* prebuilt message used by idle
+ threads */
+
+/*
+ * Return current value for max kernel threads
+ * Note: 0 means no limit
+ */
+
+cthread_kernel_limit()
+{
+ return cthread_max_kernel_threads;
+}
+
+/*
+ * Set max number of kernel threads
+ * Note: This will not currently terminate existing threads
+ * over maximum.
+ */
+
+cthread_set_kernel_limit(n)
+ int n;
+{
+ cthread_max_kernel_threads = n;
+}
+
+/*
+ * Wire a cthread to its current kernel thread
+ */
+
+void cthread_wire()
+{
+ register cproc_t p = cproc_self();
+ kern_return_t r;
+
+ /*
+ * A wired thread has a port associated with it for all
+ * of its wait/block cases. We also prebuild a wakeup
+ * message.
+ */
+
+ if (p->wired == MACH_PORT_NULL) {
+ MACH_CALL(mach_port_allocate(mach_task_self(),
+ MACH_PORT_RIGHT_RECEIVE,
+ &p->wired), r);
+ MACH_CALL(mach_port_insert_right(mach_task_self(),
+ p->wired, p->wired,
+ MACH_MSG_TYPE_MAKE_SEND), r);
+ p->msg.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
+ p->msg.msgh_size = 0; /* initialized in call */
+ p->msg.msgh_remote_port = p->wired;
+ p->msg.msgh_local_port = MACH_PORT_NULL;
+ p->msg.msgh_kind = MACH_MSGH_KIND_NORMAL;
+ p->msg.msgh_id = 0;
+#ifdef STATISTICS
+ spin_lock(&wired_lock);
+ cthread_wired++;
+ spin_unlock(&wired_lock);
+#endif STATISTICS
+ }
+}
+
+/*
+ * Unwire a cthread. Deallocate its wait port.
+ */
+
+void cthread_unwire()
+{
+ register cproc_t p = cproc_self();
+ kern_return_t r;
+
+ if (p->wired != MACH_PORT_NULL) {
+ MACH_CALL(mach_port_mod_refs(mach_task_self(), p->wired,
+ MACH_PORT_RIGHT_SEND, -1), r);
+ MACH_CALL(mach_port_mod_refs(mach_task_self(), p->wired,
+ MACH_PORT_RIGHT_RECEIVE, -1), r);
+ p->wired = MACH_PORT_NULL;
+#ifdef STATISTICS
+ spin_lock(&wired_lock);
+ cthread_wired--;
+ spin_unlock(&wired_lock);
+#endif STATISTICS
+ }
+}
+
+private cproc_t
+cproc_alloc()
+{
+ register cproc_t p = (cproc_t) malloc(sizeof(struct cproc));
+
+ p->incarnation = NO_CTHREAD;
+ p->reply_port = MACH_PORT_NULL;
+
+ spin_lock_init(&p->lock);
+ p->wired = MACH_PORT_NULL;
+ p->state = CPROC_RUNNING;
+ p->busy = 0;
+ spin_lock(&cproc_list_lock);
+ p->list = cproc_list;
+ cproc_list = p;
+ spin_unlock(&cproc_list_lock);
+
+ return p;
+}
+
+/*
+ * Called by cthread_init to set up initial data structures.
+ */
+
+vm_offset_t
+cproc_init()
+{
+ kern_return_t r;
+
+ cproc_t p = cproc_alloc();
+
+ cthread_kernel_threads = 1;
+
+ MACH_CALL(mach_port_allocate(mach_task_self(),
+ MACH_PORT_RIGHT_RECEIVE,
+ &wait_port), r);
+ MACH_CALL(mach_port_insert_right(mach_task_self(),
+ wait_port, wait_port,
+ MACH_MSG_TYPE_MAKE_SEND), r);
+
+ wakeup_msg.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
+ wakeup_msg.msgh_size = 0; /* initialized in call */
+ wakeup_msg.msgh_remote_port = wait_port;
+ wakeup_msg.msgh_local_port = MACH_PORT_NULL;
+ wakeup_msg.msgh_kind = MACH_MSGH_KIND_NORMAL;
+ wakeup_msg.msgh_id = 0;
+
+ cprocs_started = TRUE;
+
+
+ /*
+ * We pass back the new stack which should be switched to
+ * by crt0. This guarantess correct size and alignment.
+ */
+ return (stack_init(p));
+}
+
+/*
+ * Insert cproc on ready queue. Make sure it is ready for queue by
+ * synching on its lock. Just send message to wired cproc.
+ */
+
+private int cproc_ready(p, preq)
+ register cproc_t p;
+ register int preq;
+{
+ register cproc_t s=cproc_self();
+ kern_return_t r;
+
+ if (p->wired != MACH_PORT_NULL) {
+ r = mach_msg(&p->msg, MACH_SEND_MSG,
+ sizeof p->msg, 0, MACH_PORT_NULL,
+ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+#ifdef CHECK_STATUS
+ if (r != MACH_MSG_SUCCESS) {
+ mach_error("mach_msg", r);
+ exit(1);
+ }
+#endif CHECK_STATUS
+ return TRUE;
+ }
+ spin_lock(&p->lock); /* is it ready to be queued? It
+ can appear on a queue before
+ being switched from. This lock
+ is released by cproc_switch as
+ its last operation. */
+ if (p->state & CPROC_SWITCHING) {
+ /*
+ * We caught it early on. Just set to RUNNING
+ * and we will save a lot of time.
+ */
+ p->state = (p->state & ~CPROC_SWITCHING) | CPROC_RUNNING;
+ spin_unlock(&p->lock);
+ return TRUE;
+ }
+ spin_unlock(&p->lock);
+
+ spin_lock(&ready_lock);
+
+ if (preq) {
+ cthread_queue_preq(&ready, p);
+ } else {
+ cthread_queue_enq(&ready, p);
+ }
+#ifdef STATISTICS
+ cthread_ready++;
+#endif STATISTICS
+ ready_count++;
+
+ if ((s->state & CPROC_CONDWAIT) && !(s->wired)) {
+ /*
+ * This is an optimiztion. Don't bother waking anyone to grab
+ * this guy off the ready queue since my thread will block
+ * momentarily for the condition wait.
+ */
+
+ spin_unlock(&ready_lock);
+ return TRUE;
+ }
+
+ if ((ready_count > 0) && wait_count) {
+ wait_count--;
+ ready_count--;
+ spin_unlock(&ready_lock);
+ r = mach_msg(&wakeup_msg, MACH_SEND_MSG,
+ sizeof wakeup_msg, 0, MACH_PORT_NULL,
+ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+#ifdef CHECK_STATUS
+ if (r != MACH_MSG_SUCCESS) {
+ mach_error("mach_msg", r);
+ exit(1);
+ }
+#endif CHECK_STATUS
+ return TRUE;
+ }
+ spin_unlock(&ready_lock);
+ return FALSE;
+}
+
+/*
+ * This is only run on a partial "waiting" stack and called from
+ * cproc_start_wait
+ */
+
+void
+cproc_waiting(p)
+ register cproc_t p;
+{
+ mach_msg_header_t msg;
+ register cproc_t new;
+ kern_return_t r;
+
+#ifdef STATISTICS
+ spin_lock(&ready_lock);
+ cthread_waiting++;
+ cthread_waiters++;
+ spin_unlock(&ready_lock);
+#endif STATISTICS
+ for (;;) {
+ MACH_CALL(mach_msg(&msg, MACH_RCV_MSG,
+ 0, sizeof msg, wait_port,
+ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL), r);
+ spin_lock(&ready_lock);
+ cthread_queue_deq(&ready, cproc_t, new);
+ if (new != NO_CPROC) break;
+ wait_count++;
+ ready_count++;
+#ifdef STATISTICS
+ cthread_none++;
+#endif STATISTICS
+ spin_unlock(&ready_lock);
+ }
+#ifdef STATISTICS
+ cthread_ready--;
+ cthread_running++;
+ cthread_waiting--;
+#endif STATISTICS
+ spin_unlock(&ready_lock);
+ spin_lock(&new->lock);
+ new->state = CPROC_RUNNING;
+ spin_unlock(&new->lock);
+ spin_lock(&waiters_lock);
+ cthread_queue_enq(&waiters, p);
+ spin_lock(&p->lock);
+ spin_unlock(&waiters_lock);
+ cproc_switch(&p->context,&new->context,&p->lock);
+}
+
+/*
+ * Get a waiter with stack
+ *
+ */
+
+cproc_t
+cproc_waiter()
+{
+ register cproc_t waiter;
+
+ spin_lock(&waiters_lock);
+ cthread_queue_deq(&waiters, cproc_t, waiter);
+ spin_unlock(&waiters_lock);
+ if (waiter == NO_CPROC) {
+ vm_address_t base;
+ kern_return_t r;
+#ifdef STATISTICS
+ spin_lock(&waiters_lock);
+ cthread_wait_stacks++;
+ spin_unlock(&waiters_lock);
+#endif STATISTICS
+ waiter = cproc_alloc();
+ MACH_CALL(vm_allocate(mach_task_self(), &base,
+ cthread_wait_stack_size, TRUE), r);
+ waiter->stack_base = base;
+ waiter->stack_size = cthread_wait_stack_size;
+ }
+ return (waiter);
+}
+
+
+/*
+ * Current cproc is blocked so switch to any ready cprocs, or, if
+ * none, go into the wait state.
+ *
+ * You must hold cproc_self()->lock when called.
+ */
+
+cproc_block()
+{
+ register cproc_t waiter, new, p = cproc_self();
+ register int extra;
+
+ if (p->wired != MACH_PORT_NULL) {
+ mach_msg_header_t msg;
+ kern_return_t r;
+
+ spin_unlock(&p->lock);
+ MACH_CALL(mach_msg(&msg, MACH_RCV_MSG,
+ 0, sizeof msg, p->wired,
+ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL), r);
+ return;
+ }
+ p->state = CPROC_SWITCHING;
+ spin_unlock(&p->lock);
+ spin_lock(&ready_lock);
+#ifdef STATISTICS
+ cthread_blocked++;
+#endif STATISTICS
+ cthread_queue_deq(&ready, cproc_t, new);
+ if (new) {
+#ifdef STATISTICS
+ cthread_ready--;
+ cthread_switches++;
+#endif STATISTICS
+ ready_count--;
+ spin_unlock(&ready_lock);
+ spin_lock(&p->lock);
+ if (p->state == CPROC_RUNNING) { /* have we been saved */
+ spin_unlock(&p->lock);
+#ifdef STATISTICS
+ spin_lock(&ready_lock);
+ cthread_wakeup++;
+ cthread_switches--;
+ spin_unlock(&ready_lock);
+#endif STATISTICS
+ cproc_ready(new, 1); /* requeue at head were it was */
+ } else {
+ p->state = CPROC_BLOCKED;
+ spin_lock(&new->lock); /* incase still switching */
+ new->state = CPROC_RUNNING;
+ spin_unlock(&new->lock);
+ cproc_switch(&p->context,&new->context,&p->lock);
+ }
+ } else {
+ wait_count++;
+#ifdef STATISTICS
+ cthread_running--;
+#endif STATISTICS
+ spin_unlock(&ready_lock);
+ waiter = cproc_waiter();
+ spin_lock(&p->lock);
+ if (p->state == CPROC_RUNNING) { /* we have been saved */
+ spin_unlock(&p->lock);
+ spin_lock(&ready_lock);
+ wait_count--;
+#ifdef STATISTICS
+ cthread_running++;
+ cthread_wakeup++;
+#endif STATISTICS
+ spin_unlock(&ready_lock);
+ spin_lock(&waiters_lock);
+ cthread_queue_preq(&waiters, waiter);
+ spin_unlock(&waiters_lock);
+ } else {
+ p->state = CPROC_BLOCKED;
+ spin_lock(&waiter->lock); /* in case still switching */
+ spin_unlock(&waiter->lock);
+ cproc_start_wait(&p->context, waiter,
+ cproc_stack_base(waiter, sizeof(ur_cthread_t *)),
+ &p->lock);
+ }
+ }
+}
+
+/*
+ * Implement C threads using MACH threads.
+ */
+cproc_t
+cproc_create()
+{
+ register cproc_t child = cproc_alloc();
+ register kern_return_t r;
+ extern void cproc_setup();
+ extern void cproc_prepare();
+ extern void cthread_body();
+ thread_t n;
+
+ alloc_stack(child);
+ spin_lock(&n_kern_lock);
+ if (cthread_max_kernel_threads == 0 ||
+ cthread_kernel_threads < cthread_max_kernel_threads) {
+ cthread_kernel_threads++;
+ spin_unlock(&n_kern_lock);
+ MACH_CALL(thread_create(mach_task_self(), &n), r);
+ cproc_setup(child, n, cthread_body); /* machine dependent */
+ MACH_CALL(thread_resume(n), r);
+#ifdef STATISTICS
+ spin_lock(&ready_lock);
+ cthread_running++;
+ spin_unlock(&ready_lock);
+#endif STATISTICS
+ } else {
+ spin_unlock(&n_kern_lock);
+ child->state = CPROC_BLOCKED;
+ cproc_prepare(child, &child->context,
+ cproc_stack_base(child, 0));
+ cproc_ready(child,0);
+ }
+ return child;
+}
+
+void
+condition_wait(c, m)
+ register condition_t c;
+ mutex_t m;
+{
+ register cproc_t p = cproc_self();
+
+ p->state = CPROC_CONDWAIT | CPROC_SWITCHING;
+
+ spin_lock(&c->lock);
+ cthread_queue_enq(&c->queue, p);
+ spin_unlock(&c->lock);
+#ifdef WAIT_DEBUG
+ p->waiting_for = (char *)c;
+#endif WAIT_DEBUG
+
+ mutex_unlock(m);
+
+ spin_lock(&p->lock);
+ if (p->state & CPROC_SWITCHING) {
+ cproc_block();
+ } else {
+ p->state = CPROC_RUNNING;
+ spin_unlock(&p->lock);
+ }
+
+
+#ifdef WAIT_DEBUG
+ p->waiting_for = (char *)0;
+#endif WAIT_DEBUG
+
+ /*
+ * Re-acquire the mutex and return.
+ */
+ mutex_lock(m);
+}
+
+void
+cond_signal(c)
+ register condition_t c;
+{
+ register cproc_t p;
+
+ spin_lock(&c->lock);
+ cthread_queue_deq(&c->queue, cproc_t, p);
+ spin_unlock(&c->lock);
+ if (p != NO_CPROC) {
+ cproc_ready(p,0);
+ }
+}
+
+void
+cond_broadcast(c)
+ register condition_t c;
+{
+ register cproc_t p;
+ struct cthread_queue blocked_queue;
+
+ cthread_queue_init(&blocked_queue);
+
+ spin_lock(&c->lock);
+ for (;;) {
+ register int old_state;
+
+ cthread_queue_deq(&c->queue, cproc_t, p);
+ if (p == NO_CPROC)
+ break;
+ cthread_queue_enq(&blocked_queue, p);
+ }
+ spin_unlock(&c->lock);
+
+ for(;;) {
+ cthread_queue_deq(&blocked_queue, cproc_t, p);
+ if (p == NO_CPROC)
+ break;
+ cproc_ready(p,0);
+ }
+}
+
+void
+cthread_yield()
+{
+ register cproc_t new, p = cproc_self();
+
+ if (p->wired != MACH_PORT_NULL) {
+ yield();
+ return;
+ }
+ spin_lock(&ready_lock);
+#ifdef STATISTICS
+ cthread_yields++;
+#endif STATISTICS
+ cthread_queue_deq(&ready, cproc_t, new);
+ if (new) {
+ cthread_queue_enq(&ready, p);
+ spin_lock(&p->lock);
+ p->state = CPROC_BLOCKED;
+ spin_unlock(&ready_lock);
+ spin_lock(&new->lock);
+ new->state = CPROC_RUNNING;
+ spin_unlock(&new->lock);
+ cproc_switch(&p->context,&new->context,&p->lock);
+ } else {
+ spin_unlock(&ready_lock);
+ yield();
+ }
+}
+
+/*
+ * Mutex objects.
+ */
+
+void
+mutex_lock_solid(m)
+ register mutex_t m;
+{
+ register cproc_t p = cproc_self();
+ register int queued;
+ register int tried = 0;
+
+#ifdef WAIT_DEBUG
+ p->waiting_for = (char *)m;
+#endif WAIT_DEBUG
+ while (1) {
+ spin_lock(&m->lock);
+ if (cthread_queue_head(&m->queue, cproc_t) == NO_CPROC) {
+ cthread_queue_enq(&m->queue, p);
+ queued = 1;
+ } else {
+ queued = 0;
+ }
+ if (spin_try_lock(&m->held)) {
+ if (queued) cthread_queue_deq(&m->queue, cproc_t, p);
+ spin_unlock(&m->lock);
+#ifdef WAIT_DEBUG
+ p->waiting_for = (char *)0;
+#endif WAIT_DEBUG
+ return;
+ } else {
+ if (!queued) cthread_queue_enq(&m->queue, p);
+ spin_lock(&p->lock);
+ spin_unlock(&m->lock);
+ cproc_block();
+ if (spin_try_lock(&m->held)) {
+#ifdef WAIT_DEBUG
+ p->waiting_for = (char *)0;
+#endif WAIT_DEBUG
+ return;
+ }
+#ifdef STATISTICS
+ spin_lock(&mutex_count_lock);
+ cthread_no_mutex++;
+ spin_unlock(&mutex_count_lock);
+#endif STATISTICS
+ }
+ }
+}
+
+void
+mutex_unlock_solid(m)
+ register mutex_t m;
+{
+ register cproc_t new;
+
+ if (!spin_try_lock(&m->held))
+ return;
+ spin_lock(&m->lock);
+ cthread_queue_deq(&m->queue, cproc_t, new);
+ spin_unlock(&m->held);
+ spin_unlock(&m->lock);
+ if (new) {
+ cproc_ready(new,0);
+ }
+}
+
+/*
+ * Use instead of mach_msg in a multi-threaded server so as not
+ * to tie up excessive kernel threads. This uses a simple linked list for
+ * ports since this should never be more than a few.
+ */
+
+/*
+ * A cthread holds a reference to a port_entry even after it receives a
+ * message. This reference is not released until the thread does a
+ * cthread_msg_busy. This allows the fast case of a single mach_msg
+ * call to occur as often as is possible.
+ */
+
+private port_entry_t get_port_entry(port, min, max)
+ mach_port_t port;
+{
+ register port_entry_t i;
+
+ spin_lock(&port_lock);
+ for(i=port_list;i!=PORT_ENTRY_NULL;i=i->next)
+ if (i->port == port) {
+ spin_unlock(&port_lock);
+ return i;
+ }
+ i = (port_entry_t)malloc(sizeof(struct port_entry));
+ cthread_queue_init(&i->queue);
+ i->port = port;
+ i->next = port_list;
+ port_list = i;
+ i->min = min;
+ i->max = max;
+ i->held = 0;
+ spin_lock_init(&i->lock);
+ spin_unlock(&port_lock);
+ return i;
+}
+
+cthread_msg_busy(port, min, max)
+ mach_port_t port;
+{
+ register port_entry_t port_entry;
+ register cproc_t new, p = cproc_self();
+
+ if (p->busy) {
+ port_entry = get_port_entry(port, min, max);
+ spin_lock(&port_entry->lock);
+ p->busy = 0;
+ if (port_entry->held <= port_entry->min) {
+ cthread_queue_deq(&port_entry->queue, cproc_t, new);
+ if (new != NO_CPROC){
+ spin_unlock(&port_entry->lock);
+ cproc_ready(new,0);
+ } else {
+ port_entry->held--;
+ spin_unlock(&port_entry->lock);
+#ifdef STATISTICS
+ spin_lock(&port_lock);
+ cthread_rnone++;
+ spin_unlock(&port_lock);
+#endif STATISTICS
+ }
+ } else {
+ port_entry->held--;
+ spin_unlock(&port_entry->lock);
+ }
+ }
+
+}
+
+cthread_msg_active(port, min, max)
+mach_port_t port;
+{
+ register cproc_t p = cproc_self();
+ register port_entry_t port_entry;
+
+ if (!p->busy) {
+ port_entry = get_port_entry(port, min, max);
+ if (port_entry == 0) return;
+ spin_lock(&port_entry->lock);
+ if (port_entry->held < port_entry->max) {
+ port_entry->held++;
+ p->busy = (int)port_entry;
+ }
+ spin_unlock(&port_entry->lock);
+ }
+}
+
+mach_msg_return_t
+cthread_mach_msg(header, option,
+ send_size, rcv_size, rcv_name,
+ timeout, notify, min, max)
+ register mach_msg_header_t *header;
+ register mach_msg_option_t option;
+ mach_msg_size_t send_size;
+ mach_msg_size_t rcv_size;
+ register mach_port_t rcv_name;
+ mach_msg_timeout_t timeout;
+ mach_port_t notify;
+ int min, max;
+{
+ register port_entry_t port_entry;
+ register cproc_t p = cproc_self();
+ register int sent=0;
+ mach_msg_return_t r;
+ port_entry_t op = (port_entry_t)p->busy;
+
+ port_entry = get_port_entry(rcv_name, min, max);
+
+ if (op && (port_entry_t)op != port_entry)
+ cthread_msg_busy(op->port, op->min, op->max);
+ spin_lock(&port_entry->lock);
+ if (!(port_entry == (port_entry_t)p->busy)) {
+ if (port_entry->held >= max) {
+ if (option & MACH_SEND_MSG) {
+ spin_unlock(&port_entry->lock);
+ r = mach_msg(header, option &~ MACH_RCV_MSG,
+ send_size, 0, MACH_PORT_NULL,
+ timeout, notify);
+ if (r != MACH_MSG_SUCCESS) return r;
+ spin_lock(&port_entry->lock);
+ sent=1;
+ }
+ if (port_entry->held >= max) {
+ spin_lock(&p->lock);
+ cthread_queue_preq(&port_entry->queue, p);
+ spin_unlock(&port_entry->lock);
+#ifdef WAIT_DEBUG
+ p->waiting_for = (char *)port_entry;
+#endif WAIT_DEBUG
+ cproc_block();
+ } else {
+ port_entry->held++;
+ spin_unlock(&port_entry->lock);
+ }
+ } else {
+ port_entry->held++;
+ spin_unlock(&port_entry->lock);
+ }
+ } else {
+ spin_unlock(&port_entry->lock);
+ }
+#ifdef WAIT_DEBUG
+ p->waiting_for = (char *)0;
+#endif WAIT_DEBUG
+ p->busy = (int)port_entry;
+ if ((option & MACH_SEND_MSG) && !sent) {
+ r = mach_msg(header, option,
+ send_size, rcv_size, rcv_name,
+ timeout, notify);
+ } else {
+ r = mach_msg(header, option &~ MACH_SEND_MSG,
+ 0, rcv_size, rcv_name,
+ timeout, notify);
+ }
+ return r;
+}
+
+cproc_fork_prepare()
+{
+ register cproc_t p = cproc_self();
+
+ vm_inherit(mach_task_self(),p->stack_base, p->stack_size, VM_INHERIT_COPY);
+ spin_lock(&port_lock);
+ spin_lock(&cproc_list_lock);
+}
+
+cproc_fork_parent()
+{
+ register cproc_t p = cproc_self();
+
+ spin_unlock(&cproc_list_lock);
+ spin_unlock(&port_lock);
+ vm_inherit(mach_task_self(),p->stack_base, p->stack_size, VM_INHERIT_NONE);
+}
+
+cproc_fork_child()
+{
+ register cproc_t l,p = cproc_self();
+ cproc_t m;
+ register port_entry_t pe;
+ port_entry_t pet;
+ kern_return_t r;
+
+
+ vm_inherit(mach_task_self(),p->stack_base, p->stack_size, VM_INHERIT_NONE);
+ spin_lock_init(&n_kern_lock);
+ cthread_kernel_threads=0;
+#ifdef STATISTICS
+ cthread_ready = 0;
+ cthread_running = 1;
+ cthread_waiting = 0;
+ cthread_wired = 0;
+ spin_lock_init(&wired_lock);
+ cthread_wait_stacks = 0;
+ cthread_waiters = 0;
+ cthread_wakeup = 0;
+ cthread_blocked = 0;
+ cthread_rnone = 0;
+ cthread_yields = 0;
+ cthread_none = 0;
+ cthread_switches = 0;
+ cthread_no_mutex = 0;
+ spin_lock_init(&mutex_count_lock);
+#endif STATISTICS
+
+ for(l=cproc_list;l!=NO_CPROC;l=m) {
+ m=l->next;
+ if (l!=p)
+ free(l);
+ }
+
+ cproc_list = p;
+ p->next = NO_CPROC;
+ spin_lock_init(&cproc_list_lock);
+ cprocs_started = FALSE;
+ cthread_queue_init(&ready);
+ ready_count = 0;
+ spin_lock_init(&ready_lock);
+
+ MACH_CALL(mach_port_allocate(mach_task_self(),
+ MACH_PORT_RIGHT_RECEIVE,
+ &wait_port), r);
+ MACH_CALL(mach_port_insert_right(mach_task_self(),
+ wait_port, wait_port,
+ MACH_MSG_TYPE_MAKE_SEND), r);
+ wakeup_msg.msgh_remote_port = wait_port;
+ wait_count = 0;
+ cthread_queue_init(&waiters);
+ spin_lock_init(&waiters_lock);
+ for(pe=port_list;pe!=PORT_ENTRY_NULL;pe=pet) {
+ pet = pe->next;
+ free(pe);
+ }
+ port_list = PORT_ENTRY_NULL;
+ spin_lock_init(&port_lock);
+
+ if (p->wired) cthread_wire();
+}
diff --git a/libthreads/cthread_internals.h b/libthreads/cthread_internals.h
new file mode 100644
index 00000000..ed8ce445
--- /dev/null
+++ b/libthreads/cthread_internals.h
@@ -0,0 +1,198 @@
+/*
+ * 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: cthread_internals.h,v $
+ * Revision 2.14 92/08/03 18:03:56 jfriedl
+ * Made state element of struct cproc volatile.
+ * [92/08/02 jfriedl]
+ *
+ * Revision 2.13 92/03/06 14:09:24 rpd
+ * Added yield, defined using thread_switch.
+ * [92/03/06 rpd]
+ *
+ * Revision 2.12 92/03/01 00:40:23 rpd
+ * Removed exit declaration. It conflicted with the real thing.
+ * [92/02/29 rpd]
+ *
+ * Revision 2.11 91/08/28 11:19:23 jsb
+ * Fixed MACH_CALL to allow multi-line expressions.
+ * [91/08/23 rpd]
+ *
+ * Revision 2.10 91/07/31 18:33:33 dbg
+ * Protect against redefinition of ASSERT.
+ * [91/07/30 17:33:21 dbg]
+ *
+ * Revision 2.9 91/05/14 17:56:24 mrt
+ * Correcting copyright
+ *
+ * Revision 2.8 91/02/14 14:19:42 mrt
+ * Added new Mach copyright
+ * [91/02/13 12:41:02 mrt]
+ *
+ * Revision 2.7 90/11/05 14:36:55 rpd
+ * Added spin_lock_t.
+ * [90/10/31 rwd]
+ *
+ * Revision 2.6 90/09/09 14:34:51 rpd
+ * Remove special field.
+ * [90/08/24 rwd]
+ *
+ * Revision 2.5 90/06/02 15:13:44 rpd
+ * Converted to new IPC.
+ * [90/03/20 20:52:47 rpd]
+ *
+ * Revision 2.4 90/03/14 21:12:11 rwd
+ * Added waiting_for field for debugging deadlocks.
+ * [90/03/01 rwd]
+ * Added list field to keep a master list of all cprocs.
+ * [90/03/01 rwd]
+ *
+ * Revision 2.3 90/01/19 14:37:08 rwd
+ * Keep track of real thread for use in thread_* substitutes.
+ * Add CPROC_ARUN for about to run and CPROC_HOLD to avoid holding
+ * spin_locks over system calls.
+ * [90/01/03 rwd]
+ * Add busy field to be used by cthread_msg calls to make sure we
+ * have the right number of blocked kernel threads.
+ * [89/12/21 rwd]
+ *
+ * Revision 2.2 89/12/08 19:53:28 rwd
+ * Added CPROC_CONDWAIT state
+ * [89/11/28 rwd]
+ * Added on_special field.
+ * [89/11/26 rwd]
+ * Removed MSGOPT conditionals
+ * [89/11/25 rwd]
+ * Removed old debugging code. Add wired port/flag. Add state
+ * for small state machine.
+ * [89/10/30 rwd]
+ * Added CPDEBUG code
+ * [89/10/26 rwd]
+ * Change TRACE to {x;} else.
+ * [89/10/24 rwd]
+ * Rewrote to work for limited number of kernel threads. This is
+ * basically a merge of coroutine and thread. Added
+ * cthread_receivce call for use by servers.
+ * [89/10/23 rwd]
+ *
+ */
+/*
+ * cthread_internals.h
+ *
+ *
+ * Private definitions for the C Threads implementation.
+ *
+ * The cproc structure is used for different implementations
+ * of the basic schedulable units that execute cthreads.
+ *
+ */
+
+
+#include "options.h"
+#include <mach/port.h>
+#include <mach/message.h>
+#include <mach/thread_switch.h>
+
+#if !defined(__STDC__) && !defined(volatile)
+# ifdef __GNUC__
+# define volatile __volatile__
+# else
+# define volatile /* you lose */
+# endif
+#endif
+
+/*
+ * Low-level thread implementation.
+ * This structure must agree with struct ur_cthread in cthreads.h
+ */
+typedef struct cproc {
+ struct cproc *next; /* for lock, condition, and ready queues */
+ cthread_t incarnation; /* for cthread_self() */
+
+ struct cproc *list; /* for master cproc list */
+#ifdef WAIT_DEBUG
+ volatile char *waiting_for; /* address of mutex/cond waiting for */
+#endif WAIT_DEBUG
+
+ mach_port_t reply_port; /* for mig_get_reply_port() */
+
+ int context;
+ spin_lock_t lock;
+ volatile int state; /* current state */
+#define CPROC_RUNNING 0
+#define CPROC_SWITCHING 1
+#define CPROC_BLOCKED 2
+#define CPROC_CONDWAIT 4
+
+ mach_port_t wired; /* is cthread wired to kernel thread */
+ int busy; /* used with cthread_msg calls */
+
+ mach_msg_header_t msg;
+
+ unsigned int stack_base;
+ unsigned int stack_size;
+} *cproc_t;
+
+#define NO_CPROC ((cproc_t) 0)
+#define cproc_self() ((cproc_t) ur_cthread_self())
+
+/*
+ * C Threads imports:
+ */
+extern char *malloc();
+
+/*
+ * Mach imports:
+ */
+extern void mach_error();
+
+/*
+ * Macro for MACH kernel calls.
+ */
+#ifdef CHECK_STATUS
+#define MACH_CALL(expr, ret) \
+ if (((ret) = (expr)) != KERN_SUCCESS) { \
+ quit(1, "error in %s at %d: %s\n", __FILE__, __LINE__, \
+ mach_error_string(ret)); \
+ } else
+#else CHECK_STATUS
+#define MACH_CALL(expr, ret) (ret) = (expr)
+#endif CHECK_STATUS
+
+#define private static
+#ifndef ASSERT
+#define ASSERT(x)
+#endif
+#define TRACE(x)
+
+/*
+ * What we do to yield the processor:
+ * (This depresses the thread's priority for up to 10ms.)
+ */
+
+#define yield() \
+ (void) thread_switch(MACH_PORT_NULL, SWITCH_OPTION_DEPRESS, 10)
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 */
+}
diff --git a/libthreads/cthreads.h b/libthreads/cthreads.h
new file mode 100644
index 00000000..d5fbd401
--- /dev/null
+++ b/libthreads/cthreads.h
@@ -0,0 +1,556 @@
+/*
+ * 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.h,v $
+ * Revision 2.12 92/05/22 18:38:36 jfriedl
+ * From Mike Kupfer <kupfer@sprite.Berkeley.EDU>:
+ * Add declaration for cthread_wire().
+ * Merge in Jonathan Chew's changes for thread-local data.
+ * Use MACRO_BEGIN and MACRO_END.
+ *
+ * Revision 1.8 91/03/25 14:14:49 jjc
+ * For compatibility with cthread_data:
+ * 1) Added private_data field to cthread structure
+ * for use by POSIX thread specific data routines.
+ * 2) Conditionalized old data field used by cthread_data
+ * under CTHREAD_DATA for binary compatibility.
+ * 3) Changed macros, cthread_set_data and cthread_data,
+ * into routines which use the POSIX routines for
+ * source compatibility.
+ * Also, conditionalized under CTHREAD_DATA.
+ * [91/03/18 jjc]
+ * Added support for multiplexing the thread specific global
+ * variable, cthread_data, using the POSIX threads interface
+ * for thread private data.
+ * [91/03/14 jjc]
+ *
+ * Revision 2.11 91/08/03 18:20:15 jsb
+ * Removed the infamous line 122.
+ * [91/08/01 22:40:24 jsb]
+ *
+ * Revision 2.10 91/07/31 18:35:42 dbg
+ * Fix the standard-C conditional: it's __STDC__.
+ *
+ * Allow for macro-redefinition of cthread_sp, spin_try_lock,
+ * spin_unlock (from machine/cthreads.h).
+ * [91/07/30 17:34:28 dbg]
+ *
+ * Revision 2.9 91/05/14 17:56:42 mrt
+ * Correcting copyright
+ *
+ * Revision 2.8 91/02/14 14:19:52 mrt
+ * Added new Mach copyright
+ * [91/02/13 12:41:15 mrt]
+ *
+ * Revision 2.7 90/11/05 14:37:12 rpd
+ * Include machine/cthreads.h. Added spin_lock_t.
+ * [90/10/31 rwd]
+ *
+ * Revision 2.6 90/10/12 13:07:24 rpd
+ * Channge to allow for positive stack growth.
+ * [90/10/10 rwd]
+ *
+ * Revision 2.5 90/09/09 14:34:56 rpd
+ * Remove mutex_special and debug_mutex.
+ * [90/08/24 rwd]
+ *
+ * Revision 2.4 90/08/07 14:31:14 rpd
+ * Removed RCS keyword nonsense.
+ *
+ * Revision 2.3 90/01/19 14:37:18 rwd
+ * Add back pointer to cthread structure.
+ * [90/01/03 rwd]
+ * Change definition of cthread_init and change ur_cthread_self macro
+ * to reflect movement of self pointer on stack.
+ * [89/12/18 19:18:34 rwd]
+ *
+ * Revision 2.2 89/12/08 19:53:49 rwd
+ * Change spin_try_lock to int.
+ * [89/11/30 rwd]
+ * Changed mutex macros to deal with special mutexs
+ * [89/11/26 rwd]
+ * Make mutex_{set,clear}_special routines instead of macros.
+ * [89/11/25 rwd]
+ * Added mutex_special to specify a need to context switch on this
+ * mutex.
+ * [89/11/21 rwd]
+ *
+ * Made mutex_lock a macro trying to grab the spin_lock first.
+ * [89/11/13 rwd]
+ * Removed conditionals. Mutexes are more like conditions now.
+ * Changed for limited kernel thread version.
+ * [89/10/23 rwd]
+ *
+ * Revision 2.1 89/08/03 17:09:40 rwd
+ * Created.
+ *
+ *
+ * 28-Oct-88 Eric Cooper (ecc) at Carnegie Mellon University
+ * Implemented spin_lock() as test and test-and-set logic
+ * (using mutex_try_lock()) in sync.c. Changed ((char *) 0)
+ * to 0, at Mike Jones's suggestion, and turned on ANSI-style
+ * declarations in either C++ or _STDC_.
+ *
+ * 29-Sep-88 Eric Cooper (ecc) at Carnegie Mellon University
+ * Changed NULL to ((char *) 0) to avoid dependency on <stdio.h>,
+ * at Alessandro Forin's suggestion.
+ *
+ * 08-Sep-88 Alessandro Forin (af) at Carnegie Mellon University
+ * Changed queue_t to cthread_queue_t and string_t to char *
+ * to avoid conflicts.
+ *
+ * 01-Apr-88 Eric Cooper (ecc) at Carnegie Mellon University
+ * Changed compound statement macros to use the
+ * do { ... } while (0) trick, so that they work
+ * in all statement contexts.
+ *
+ * 19-Feb-88 Eric Cooper (ecc) at Carnegie Mellon University
+ * Made spin_unlock() and mutex_unlock() into procedure calls
+ * rather than macros, so that even smart compilers can't reorder
+ * the clearing of the lock. Suggested by Jeff Eppinger.
+ * Removed the now empty <machine>/cthreads.h.
+ *
+ * 01-Dec-87 Eric Cooper (ecc) at Carnegie Mellon University
+ * Changed cthread_self() to mask the current SP to find
+ * the self pointer stored at the base of the stack.
+ *
+ * 22-Jul-87 Eric Cooper (ecc) at Carnegie Mellon University
+ * Fixed bugs in mutex_set_name and condition_set_name
+ * due to bad choice of macro formal parameter name.
+ *
+ * 21-Jul-87 Eric Cooper (ecc) at Carnegie Mellon University
+ * Moved #include <machine/cthreads.h> to avoid referring
+ * to types before they are declared (required by C++).
+ *
+ * 9-Jul-87 Michael Jones (mbj) at Carnegie Mellon University
+ * Added conditional type declarations for C++.
+ * Added _cthread_init_routine and _cthread_exit_routine variables
+ * for automatic initialization and finalization by crt0.
+ */
+/*
+ * File: cthreads.h
+ * Author: Eric Cooper, Carnegie Mellon University
+ * Date: Jul, 1987
+ *
+ * Definitions for the C Threads package.
+ *
+ */
+
+
+#ifndef _CTHREADS_
+#define _CTHREADS_ 1
+
+#include <machine/cthreads.h>
+
+#if c_plusplus || __STDC__
+
+#ifndef C_ARG_DECLS
+#define C_ARG_DECLS(arglist) arglist
+#endif /* not C_ARG_DECLS */
+
+typedef void *any_t;
+
+#else /* not (c_plusplus || __STDC__) */
+
+#ifndef C_ARG_DECLS
+#define C_ARG_DECLS(arglist) ()
+#endif /* not C_ARG_DECLS */
+
+typedef char *any_t;
+
+#endif /* not (c_plusplus || __STDC__) */
+
+#include <mach/mach.h>
+#include <mach/machine/vm_param.h>
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif /* TRUE */
+
+#ifndef MACRO_BEGIN
+
+#ifdef lint
+int NEVER;
+#else lint
+#define NEVER 0
+#endif lint
+
+#define MACRO_BEGIN do {
+#define MACRO_END } while (NEVER)
+
+#endif MACRO_BEGIN
+
+/*
+ * C Threads package initialization.
+ */
+
+extern int cthread_init();
+extern any_t calloc C_ARG_DECLS((unsigned n, unsigned size));
+
+/*
+ * Queues.
+ */
+typedef struct cthread_queue {
+ struct cthread_queue_item *head;
+ struct cthread_queue_item *tail;
+} *cthread_queue_t;
+
+typedef struct cthread_queue_item {
+ struct cthread_queue_item *next;
+} *cthread_queue_item_t;
+
+#define NO_QUEUE_ITEM ((cthread_queue_item_t) 0)
+
+#define QUEUE_INITIALIZER { NO_QUEUE_ITEM, NO_QUEUE_ITEM }
+
+#define cthread_queue_alloc() ((cthread_queue_t) calloc(1, sizeof(struct cthread_queue)))
+#define cthread_queue_init(q) ((q)->head = (q)->tail = 0)
+#define cthread_queue_free(q) free((any_t) (q))
+
+#define cthread_queue_enq(q, x) \
+ MACRO_BEGIN \
+ (x)->next = 0; \
+ if ((q)->tail == 0) \
+ (q)->head = (cthread_queue_item_t) (x); \
+ else \
+ (q)->tail->next = (cthread_queue_item_t) (x); \
+ (q)->tail = (cthread_queue_item_t) (x); \
+ MACRO_END
+
+#define cthread_queue_preq(q, x) \
+ MACRO_BEGIN \
+ if ((q)->tail == 0) \
+ (q)->tail = (cthread_queue_item_t) (x); \
+ ((cthread_queue_item_t) (x))->next = (q)->head; \
+ (q)->head = (cthread_queue_item_t) (x); \
+ MACRO_END
+
+#define cthread_queue_head(q, t) ((t) ((q)->head))
+
+#define cthread_queue_deq(q, t, x) \
+ MACRO_BEGIN \
+ if (((x) = (t) ((q)->head)) != 0 && \
+ ((q)->head = (cthread_queue_item_t) ((x)->next)) == 0) \
+ (q)->tail = 0; \
+ MACRO_END
+
+#define cthread_queue_map(q, t, f) \
+ MACRO_BEGIN \
+ register cthread_queue_item_t x, next; \
+ for (x = (cthread_queue_item_t) ((q)->head); x != 0; x = next) { \
+ next = x->next; \
+ (*(f))((t) x); \
+ } \
+ MACRO_END
+
+/*
+ * Spin locks.
+ */
+extern void
+spin_lock_solid C_ARG_DECLS((spin_lock_t *p));
+
+#ifndef spin_unlock
+extern void
+spin_unlock C_ARG_DECLS((spin_lock_t *p));
+#endif
+
+#ifndef spin_try_lock
+extern int
+spin_try_lock C_ARG_DECLS((spin_lock_t *p));
+#endif
+
+#define spin_lock(p) if (!spin_try_lock(p)) spin_lock_solid(p); else
+
+/*
+ * Mutex objects.
+ */
+typedef struct mutex {
+ spin_lock_t lock;
+ char *name;
+ struct cthread_queue queue;
+ spin_lock_t held;
+} *mutex_t;
+
+#define MUTEX_INITIALIZER { SPIN_LOCK_INITIALIZER, 0, QUEUE_INITIALIZER, SPIN_LOCK_INITIALIZER}
+
+#define mutex_alloc() ((mutex_t) calloc(1, sizeof(struct mutex)))
+#define mutex_init(m) \
+ MACRO_BEGIN \
+ spin_lock_init(&(m)->lock); \
+ cthread_queue_init(&(m)->queue); \
+ spin_lock_init(&(m)->held); \
+ MACRO_END
+#define mutex_set_name(m, x) ((m)->name = (x))
+#define mutex_name(m) ((m)->name != 0 ? (m)->name : "?")
+#define mutex_clear(m) /* nop */???
+#define mutex_free(m) free((any_t) (m))
+
+extern void
+mutex_lock_solid C_ARG_DECLS((mutex_t m)); /* blocking */
+
+extern void
+mutex_unlock_solid C_ARG_DECLS((mutex_t m));
+
+#define mutex_try_lock(m) spin_try_lock(&(m)->held)
+#define mutex_lock(m) \
+ MACRO_BEGIN \
+ if (!spin_try_lock(&(m)->held)) { \
+ mutex_lock_solid(m); \
+ } \
+ MACRO_END
+#define mutex_unlock(m) \
+ MACRO_BEGIN \
+ if (spin_unlock(&(m)->held), \
+ cthread_queue_head(&(m)->queue, int) != 0) { \
+ mutex_unlock_solid(m); \
+ } \
+ MACRO_END
+
+/*
+ * Condition variables.
+ */
+typedef struct condition {
+ spin_lock_t lock;
+ struct cthread_queue queue;
+ char *name;
+} *condition_t;
+
+#define CONDITION_INITIALIZER { SPIN_LOCK_INITIALIZER, QUEUE_INITIALIZER, 0 }
+
+#define condition_alloc() ((condition_t) calloc(1, sizeof(struct condition)))
+#define condition_init(c) \
+ MACRO_BEGIN \
+ spin_lock_init(&(c)->lock); \
+ cthread_queue_init(&(c)->queue); \
+ MACRO_END
+#define condition_set_name(c, x) ((c)->name = (x))
+#define condition_name(c) ((c)->name != 0 ? (c)->name : "?")
+#define condition_clear(c) \
+ MACRO_BEGIN \
+ condition_broadcast(c); \
+ spin_lock(&(c)->lock); \
+ MACRO_END
+#define condition_free(c) \
+ MACRO_BEGIN \
+ condition_clear(c); \
+ free((any_t) (c)); \
+ MACRO_END
+
+#define condition_signal(c) \
+ MACRO_BEGIN \
+ if ((c)->queue.head) { \
+ cond_signal(c); \
+ } \
+ MACRO_END
+
+#define condition_broadcast(c) \
+ MACRO_BEGIN \
+ if ((c)->queue.head) { \
+ cond_broadcast(c); \
+ } \
+ MACRO_END
+
+extern void
+cond_signal C_ARG_DECLS((condition_t c));
+
+extern void
+cond_broadcast C_ARG_DECLS((condition_t c));
+
+extern void
+condition_wait C_ARG_DECLS((condition_t c, mutex_t m));
+
+/*
+ * Threads.
+ */
+
+typedef any_t (*cthread_fn_t) C_ARG_DECLS((any_t arg));
+
+#include <setjmp.h>
+
+typedef struct cthread {
+ struct cthread *next;
+ struct mutex lock;
+ struct condition done;
+ int state;
+ jmp_buf catch;
+ cthread_fn_t func;
+ any_t arg;
+ any_t result;
+ char *name;
+#ifdef CTHREAD_DATA
+ any_t data;
+#endif CTHREAD_DATA
+ any_t private_data;
+ struct ur_cthread *ur;
+} *cthread_t;
+
+#define NO_CTHREAD ((cthread_t) 0)
+
+extern cthread_t
+cthread_fork C_ARG_DECLS((cthread_fn_t func, any_t arg));
+
+extern void
+cthread_detach C_ARG_DECLS((cthread_t t));
+
+extern any_t
+cthread_join C_ARG_DECLS((cthread_t t));
+
+extern void
+cthread_yield();
+
+extern void
+cthread_exit C_ARG_DECLS((any_t result));
+
+/*
+ * This structure must agree with struct cproc in cthread_internals.h
+ */
+typedef struct ur_cthread {
+ struct ur_cthread *next;
+ cthread_t incarnation;
+} *ur_cthread_t;
+
+#ifndef cthread_sp
+extern int
+cthread_sp();
+#endif
+
+extern int cthread_stack_mask;
+
+#ifdef STACK_GROWTH_UP
+#define ur_cthread_ptr(sp) \
+ (* (ur_cthread_t *) ((sp) & cthread_stack_mask))
+#else STACK_GROWTH_UP
+#define ur_cthread_ptr(sp) \
+ (* (ur_cthread_t *) ( ((sp) | cthread_stack_mask) + 1 \
+ - sizeof(ur_cthread_t *)) )
+#endif STACK_GROWTH_UP
+
+#define ur_cthread_self() (ur_cthread_ptr(cthread_sp()))
+
+#define cthread_assoc(id, t) ((((ur_cthread_t) (id))->incarnation = (t)), \
+ ((t) ? ((t)->ur = (ur_cthread_t)(id)) : 0))
+#define cthread_self() (ur_cthread_self()->incarnation)
+
+extern void
+cthread_set_name C_ARG_DECLS((cthread_t t, char *name));
+
+extern char *
+cthread_name C_ARG_DECLS((cthread_t t));
+
+extern int
+cthread_count();
+
+extern void
+cthread_set_limit C_ARG_DECLS((int n));
+
+extern int
+cthread_limit();
+
+extern void
+cthread_wire C_ARG_DECLS((void));
+
+#ifdef CTHREAD_DATA
+/*
+ * Set or get thread specific "global" variable
+ *
+ * The thread given must be the calling thread (ie. thread_self).
+ * XXX This is for compatibility with the old cthread_data. XXX
+ */
+extern int
+cthread_set_data C_ARG_DECLS((cthread_t t, any_t x));
+
+extern any_t
+cthread_data C_ARG_DECLS((cthread_t t));
+#endif CTHREAD_DATA
+
+/*
+ * Support for POSIX thread specific data
+ *
+ * Multiplexes a thread specific "global" variable
+ * into many thread specific "global" variables.
+ */
+#define CTHREAD_DATA_VALUE_NULL (any_t)0
+#define CTHREAD_KEY_INVALID (cthread_key_t)-1
+
+typedef int cthread_key_t;
+
+/*
+ * Create key to private data visible to all threads in task.
+ * Different threads may use same key, but the values bound to the key are
+ * maintained on a thread specific basis.
+ */
+extern int
+cthread_keycreate C_ARG_DECLS((cthread_key_t *key));
+
+/*
+ * Get value currently bound to key for calling thread
+ */
+extern int
+cthread_getspecific C_ARG_DECLS((cthread_key_t key, any_t *value));
+
+/*
+ * Bind value to given key for calling thread
+ */
+extern int
+cthread_setspecific C_ARG_DECLS((cthread_key_t key, any_t value));
+
+/*
+ * Debugging support.
+ */
+#ifdef DEBUG
+
+#ifndef ASSERT
+/*
+ * Assertion macro, similar to <assert.h>
+ */
+#include <stdio.h>
+#define ASSERT(p) \
+ MACRO_BEGIN \
+ if (!(p)) { \
+ fprintf(stderr, \
+ "File %s, line %d: assertion p failed.\n", \
+ __FILE__, __LINE__); \
+ abort(); \
+ } \
+ MACRO_END
+
+#endif ASSERT
+
+#define SHOULDNT_HAPPEN 0
+
+extern int cthread_debug;
+
+#else DEBUG
+
+#ifndef ASSERT
+#define ASSERT(p)
+#endif ASSERT
+
+#endif DEBUG
+
+#endif _CTHREADS_
diff --git a/libthreads/i386/csw.S b/libthreads/i386/csw.S
new file mode 100644
index 00000000..efc739f0
--- /dev/null
+++ b/libthreads/i386/csw.S
@@ -0,0 +1,139 @@
+/*
+ * 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: csw.s,v $
+ * Revision 2.7 91/07/31 18:36:32 dbg
+ * Fix for ANSI C preprocessor.
+ * [91/07/30 17:35:16 dbg]
+ *
+ * Revision 2.6 91/05/14 17:56:56 mrt
+ * Correcting copyright
+ *
+ * Revision 2.5 91/05/08 13:35:49 dbg
+ * Unlock lock with a locked instruction (xchg).
+ * [91/03/20 dbg]
+ *
+ * Revision 2.4 91/02/14 14:20:02 mrt
+ * Changed to new Mach copyright
+ * [91/02/13 12:15:27 mrt]
+ *
+ * Revision 2.3 91/01/08 16:46:20 rpd
+ * Don't use Times - horta doesn't like it for some reason.
+ * [91/01/06 rpd]
+ *
+ * Revision 2.2 90/05/03 15:54:37 dbg
+ * Created.
+ * [90/02/05 dbg]
+ *
+ */
+#include <i386/asm.h>
+
+/*
+ * Suspend the current thread and resume the next one.
+ *
+ * void cproc_switch(int *cur, int *next, int *lock)
+ */
+ENTRY(cproc_switch)
+ pushl %ebp / save ebp
+ movl %esp,%ebp / set frame pointer to get arguments
+ pushl %ebx / save ebx
+ pushl %esi / esi
+ pushl %edi / edi
+ movl B_ARG0,%eax / get cur
+ movl %esp,(%eax) / save current esp
+ movl B_ARG2,%edx / get address of lock before switching
+ / stacks
+ movl B_ARG1,%eax / get next
+ movl (%eax),%esp / get new stack pointer
+ xorl %eax,%eax / unlock
+ xchgl %eax,(%edx) / the lock - now old thread can run
+
+ popl %edi / restore di
+ popl %esi / si
+ popl %ebx / bx
+ popl %ebp / and bp (don`t use "leave" - bp
+ / still points to old stack)
+ ret
+
+/*
+ * Create a new stack frame for a 'waiting' thread,
+ * save current thread's frame, and switch to waiting thread.
+ *
+ * void cproc_start_wait(int *cur,
+ * cproc_t child,
+ * int stackp,
+ * int *lock)
+ */
+ENTRY(cproc_start_wait)
+ pushl %ebp / save ebp
+ movl %esp,%ebp / set frame pointer
+ pushl %ebx / save ebx
+ pushl %esi / esi
+ pushl %edi / edi
+ movl B_ARG0,%eax / get cur
+ movl %esp,(%eax) / save current esp
+ movl B_ARG1,%eax / get child thread
+ movl B_ARG3,%edx / point to lock before switching stack
+ movl B_ARG2,%esp / get new stack
+ pushl %eax / push child thread as argument
+ movl $0,%ebp / (clear frame pointer)
+ xorl %eax,%eax / unlock
+ xchgl %eax,(%edx) / the lock - now old thread can run
+ call _cproc_waiting / call cproc_waiting
+ /*NOTREACHED*/
+
+/*
+ * Set up a thread's stack so that when cproc_switch switches to
+ * it, it will start up as if it called
+ * cproc_body(child)
+ *
+ * void cproc_prepare(cproc_t child, int *context, int stack)
+ */
+ENTRY(cproc_prepare)
+ pushl %ebp / save ebp
+ movl %esp,%ebp / set frame pointer
+ movl B_ARG2,%edx / get child`s stack
+ subl $28,%edx
+ / make room for context:
+ / 0 saved edi ()
+ / 4 saved esi ()
+ / 8 saved ebx ()
+ / 12 saved ebp ()
+ / 16 return PC from cproc_switch
+ / 20 return PC from cthread_body
+ / 24 argument to cthread_body
+ movl $0,12(%edx) / clear frame pointer
+ movl $_cthread_body,16(%edx)
+ / resume at cthread_body
+ movl $0,20(%edx) / fake return address from cthread_body
+ movl B_ARG0,%ecx / get child thread pointer
+ movl %ecx,24(%edx) / set as argument to cthread_body
+ movl B_ARG1,%ecx / get pointer to context
+ movl %edx,(%ecx) / save context
+ leave
+ ret
+
diff --git a/libthreads/i386/thread.c b/libthreads/i386/thread.c
new file mode 100644
index 00000000..155146b2
--- /dev/null
+++ b/libthreads/i386/thread.c
@@ -0,0 +1,114 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 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: thread.c,v $
+ * Revision 2.6 91/07/31 18:37:07 dbg
+ * Undefine cthread_sp macro around function definition.
+ * [91/07/30 17:36:23 dbg]
+ *
+ * Revision 2.5 91/05/14 17:57:27 mrt
+ * Correcting copyright
+ *
+ * Revision 2.4 91/02/14 14:20:21 mrt
+ * Changed to new Mach copyright
+ * [91/02/13 12:20:10 mrt]
+ *
+ * Revision 2.3 90/06/02 15:13:53 rpd
+ * Added definition of cthread_sp.
+ * [90/06/02 rpd]
+ *
+ * Revision 2.2 90/05/03 15:55:03 dbg
+ * Created (from 68020 version).
+ * [90/02/05 dbg]
+ *
+ */
+/*
+ * i386/thread.c
+ *
+ */
+
+#ifndef lint
+static char rcs_id[] = "$Header: cvs-sans-libpthread/hurd/libthreads/i386/thread.c,v 1.1 1992/10/06 18:31:16 mib Exp $";
+#endif not lint
+
+
+#include <cthreads.h>
+#include "cthread_internals.h"
+
+
+#include <mach/mach.h>
+
+/*
+ * C library imports:
+ */
+extern bzero();
+
+/*
+ * Set up the initial state of a MACH thread
+ * so that it will invoke cthread_body(child)
+ * when it is resumed.
+ */
+void
+cproc_setup(child, thread, routine)
+ register cproc_t child;
+ int thread;
+ int routine;
+{
+ register int *top = (int *) (child->stack_base + child->stack_size);
+ struct i386_thread_state state;
+ register struct i386_thread_state *ts = &state;
+ kern_return_t r;
+ unsigned int count;
+
+ /*
+ * Set up i386 call frame and registers.
+ * Read registers first to get correct segment values.
+ */
+ count = i386_THREAD_STATE_COUNT;
+ MACH_CALL(thread_get_state(thread,i386_THREAD_STATE,(thread_state_t) &state,&count),r);
+
+ ts->eip = routine;
+ *--top = (int) child; /* argument to function */
+ *--top = 0; /* fake return address */
+ ts->uesp = (int) top; /* set stack pointer */
+ ts->ebp = 0; /* clear frame pointer */
+
+ MACH_CALL(thread_set_state(thread,i386_THREAD_STATE,(thread_state_t) &state,i386_THREAD_STATE_COUNT),r);
+}
+
+#ifdef cthread_sp
+#undef cthread_sp
+#endif
+
+int
+cthread_sp()
+{
+ int x;
+
+ return (int) &x;
+}
+
diff --git a/libthreads/malloc.c b/libthreads/malloc.c
new file mode 100644
index 00000000..b6a31c80
--- /dev/null
+++ b/libthreads/malloc.c
@@ -0,0 +1,349 @@
+/*
+ * 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: malloc.c,v $
+ * Revision 2.7 91/05/14 17:57:34 mrt
+ * Correcting copyright
+ *
+ * Revision 2.6 91/02/14 14:20:26 mrt
+ * Added new Mach copyright
+ * [91/02/13 12:41:21 mrt]
+ *
+ * Revision 2.5 90/11/05 14:37:33 rpd
+ * Added malloc_fork* code.
+ * [90/11/02 rwd]
+ *
+ * Add spin_lock_t.
+ * [90/10/31 rwd]
+ *
+ * Revision 2.4 90/08/07 14:31:28 rpd
+ * Removed RCS keyword nonsense.
+ *
+ * Revision 2.3 90/06/02 15:14:00 rpd
+ * Converted to new IPC.
+ * [90/03/20 20:56:57 rpd]
+ *
+ * Revision 2.2 89/12/08 19:53:59 rwd
+ * Removed conditionals.
+ * [89/10/23 rwd]
+ *
+ * Revision 2.1 89/08/03 17:09:46 rwd
+ * Created.
+ *
+ *
+ * 13-Sep-88 Eric Cooper (ecc) at Carnegie Mellon University
+ * Changed realloc() to copy min(old size, new size) bytes.
+ * Bug found by Mike Kupfer at Olivetti.
+ */
+/*
+ * File: malloc.c
+ * Author: Eric Cooper, Carnegie Mellon University
+ * Date: July, 1988
+ *
+ * Memory allocator for use with multiple threads.
+ */
+
+
+#include <cthreads.h>
+#include "cthread_internals.h"
+
+/*
+ * C library imports:
+ */
+extern bcopy();
+
+/*
+ * Structure of memory block header.
+ * When free, next points to next block on free list.
+ * When allocated, fl points to free list.
+ * Size of header is 4 bytes, so minimum usable block size is 8 bytes.
+ */
+typedef union header {
+ union header *next;
+ struct free_list *fl;
+} *header_t;
+
+#define MIN_SIZE 8 /* minimum block size */
+
+typedef struct free_list {
+ spin_lock_t lock; /* spin lock for mutual exclusion */
+ header_t head; /* head of free list for this size */
+#ifdef DEBUG
+ int in_use; /* # mallocs - # frees */
+#endif DEBUG
+} *free_list_t;
+
+/*
+ * Free list with index i contains blocks of size 2^(i+3) including header.
+ * Smallest block size is 8, with 4 bytes available to user.
+ * Size argument to malloc is a signed integer for sanity checking,
+ * so largest block size is 2^31.
+ */
+#define NBUCKETS 29
+
+static struct free_list malloc_free_list[NBUCKETS];
+
+static void
+more_memory(size, fl)
+ int size;
+ register free_list_t fl;
+{
+ register int amount;
+ register int n;
+ vm_address_t where;
+ register header_t h;
+ kern_return_t r;
+
+ if (size <= vm_page_size) {
+ amount = vm_page_size;
+ n = vm_page_size / size;
+ /*
+ * We lose vm_page_size - n*size bytes here.
+ */
+ } else {
+ amount = size;
+ n = 1;
+ }
+ MACH_CALL(vm_allocate(mach_task_self(), &where, (vm_size_t) amount, TRUE), r);
+ h = (header_t) where;
+ do {
+ h->next = fl->head;
+ fl->head = h;
+ h = (header_t) ((char *) h + size);
+ } while (--n != 0);
+}
+
+char *
+malloc(size)
+ register unsigned int size;
+{
+ register int i, n;
+ register free_list_t fl;
+ register header_t h;
+
+ if ((int) size <= 0) /* sanity check */
+ return 0;
+ size += sizeof(union header);
+ /*
+ * Find smallest power-of-two block size
+ * big enough to hold requested size plus header.
+ */
+ i = 0;
+ n = MIN_SIZE;
+ while (n < size) {
+ i += 1;
+ n <<= 1;
+ }
+ ASSERT(i < NBUCKETS);
+ fl = &malloc_free_list[i];
+ spin_lock(&fl->lock);
+ h = fl->head;
+ if (h == 0) {
+ /*
+ * Free list is empty;
+ * allocate more blocks.
+ */
+ more_memory(n, fl);
+ h = fl->head;
+ if (h == 0) {
+ /*
+ * Allocation failed.
+ */
+ spin_unlock(&fl->lock);
+ return 0;
+ }
+ }
+ /*
+ * Pop block from free list.
+ */
+ fl->head = h->next;
+#ifdef DEBUG
+ fl->in_use += 1;
+#endif DEBUG
+ spin_unlock(&fl->lock);
+ /*
+ * Store free list pointer in block header
+ * so we can figure out where it goes
+ * at free() time.
+ */
+ h->fl = fl;
+ /*
+ * Return pointer past the block header.
+ */
+ return ((char *) h) + sizeof(union header);
+}
+
+free(base)
+ char *base;
+{
+ register header_t h;
+ register free_list_t fl;
+ register int i;
+
+ if (base == 0)
+ return;
+ /*
+ * Find free list for block.
+ */
+ h = (header_t) (base - sizeof(union header));
+ fl = h->fl;
+ i = fl - malloc_free_list;
+ /*
+ * Sanity checks.
+ */
+ if (i < 0 || i >= NBUCKETS) {
+ ASSERT(0 <= i && i < NBUCKETS);
+ return;
+ }
+ if (fl != &malloc_free_list[i]) {
+ ASSERT(fl == &malloc_free_list[i]);
+ return;
+ }
+ /*
+ * Push block on free list.
+ */
+ spin_lock(&fl->lock);
+ h->next = fl->head;
+ fl->head = h;
+#ifdef DEBUG
+ fl->in_use -= 1;
+#endif DEBUG
+ spin_unlock(&fl->lock);
+ return;
+}
+
+char *
+realloc(old_base, new_size)
+ char *old_base;
+ unsigned int new_size;
+{
+ register header_t h;
+ register free_list_t fl;
+ register int i;
+ unsigned int old_size;
+ char *new_base;
+
+ if (old_base == 0)
+ return 0;
+ /*
+ * Find size of old block.
+ */
+ h = (header_t) (old_base - sizeof(union header));
+ fl = h->fl;
+ i = fl - malloc_free_list;
+ /*
+ * Sanity checks.
+ */
+ if (i < 0 || i >= NBUCKETS) {
+ ASSERT(0 <= i && i < NBUCKETS);
+ return 0;
+ }
+ if (fl != &malloc_free_list[i]) {
+ ASSERT(fl == &malloc_free_list[i]);
+ return 0;
+ }
+ /*
+ * Free list with index i contains blocks of size 2^(i+3) including header.
+ */
+ old_size = (1 << (i+3)) - sizeof(union header);
+ /*
+ * Allocate new block, copy old bytes, and free old block.
+ */
+ new_base = malloc(new_size);
+ if (new_base != 0)
+ bcopy(old_base, new_base, (int) (old_size < new_size ? old_size : new_size));
+ free(old_base);
+ return new_base;
+}
+
+#ifdef DEBUG
+void
+print_malloc_free_list()
+{
+ register int i, size;
+ register free_list_t fl;
+ register int n;
+ register header_t h;
+ int total_used = 0;
+ int total_free = 0;
+
+ fprintf(stderr, " Size In Use Free Total\n");
+ for (i = 0, size = MIN_SIZE, fl = malloc_free_list;
+ i < NBUCKETS;
+ i += 1, size <<= 1, fl += 1) {
+ spin_lock(&fl->lock);
+ if (fl->in_use != 0 || fl->head != 0) {
+ total_used += fl->in_use * size;
+ for (n = 0, h = fl->head; h != 0; h = h->next, n += 1)
+ ;
+ total_free += n * size;
+ fprintf(stderr, "%10d %10d %10d %10d\n",
+ size, fl->in_use, n, fl->in_use + n);
+ }
+ spin_unlock(&fl->lock);
+ }
+ fprintf(stderr, " all sizes %10d %10d %10d\n",
+ total_used, total_free, total_used + total_free);
+}
+#endif DEBUG
+
+void malloc_fork_prepare()
+/*
+ * Prepare the malloc module for a fork by insuring that no thread is in a
+ * malloc critical section.
+ */
+{
+ register int i;
+
+ for (i = 0; i < NBUCKETS; i++) {
+ spin_lock(&malloc_free_list[i].lock);
+ }
+}
+
+void malloc_fork_parent()
+/*
+ * Called in the parent process after a fork() to resume normal operation.
+ */
+{
+ register int i;
+
+ for (i = NBUCKETS-1; i >= 0; i--) {
+ spin_unlock(&malloc_free_list[i].lock);
+ }
+}
+
+void malloc_fork_child()
+/*
+ * Called in the child process after a fork() to resume normal operation.
+ */
+{
+ register int i;
+
+ for (i = NBUCKETS-1; i >= 0; i--) {
+ spin_unlock(&malloc_free_list[i].lock);
+ }
+}
diff --git a/libthreads/mig_support.c b/libthreads/mig_support.c
new file mode 100644
index 00000000..e9c834fb
--- /dev/null
+++ b/libthreads/mig_support.c
@@ -0,0 +1,157 @@
+/*
+ * 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: mig_support.c,v $
+ * Revision 2.6 91/05/14 17:57:41 mrt
+ * Correcting copyright
+ *
+ * Revision 2.5 91/02/14 14:20:30 mrt
+ * Added new Mach copyright
+ * [91/02/13 12:41:26 mrt]
+ *
+ * Revision 2.4 90/08/07 14:31:41 rpd
+ * Removed RCS keyword nonsense.
+ *
+ * Revision 2.3 90/08/07 14:27:48 rpd
+ * When we recycle the global reply port by giving it to the first
+ * cthread, clear the global reply port. This will take care of
+ * someone accidently calling this twice.
+ * [90/08/07 rwd]
+ *
+ * Revision 2.2 90/06/02 15:14:04 rpd
+ * Converted to new IPC.
+ * [90/03/20 20:56:50 rpd]
+ *
+ * Revision 2.1 89/08/03 17:09:50 rwd
+ * Created.
+ *
+ * 18-Jan-89 David Golub (dbg) at Carnegie-Mellon University
+ * Replaced task_data() by thread_reply().
+ *
+ *
+ * 27-Aug-87 Eric Cooper (ecc) at Carnegie Mellon University
+ * Changed mig_support.c to avoid deadlock that can occur
+ * if tracing is turned on during calls to mig_get_reply_port().
+ *
+ * 10-Aug-87 Eric Cooper (ecc) at Carnegie Mellon University
+ * Changed mig_support.c to use MACH_CALL.
+ * Changed "is_init" to "multithreaded" and reversed its sense.
+ *
+ * 30-Jul-87 Mary Thompson (mrt) at Carnegie Mellon University
+ * Created.
+ */
+/*
+ * File: mig_support.c
+ * Author: Mary R. Thompson, Carnegie Mellon University
+ * Date: July, 1987
+ *
+ * Routines to set and deallocate the mig reply port for the current thread.
+ * Called from mig-generated interfaces.
+ *
+ */
+
+
+#include <mach/mach.h>
+#include <cthreads.h>
+#include "cthread_internals.h"
+
+private boolean_t multithreaded = FALSE;
+/* use a global reply port before becoming multi-threaded */
+private mach_port_t mig_reply_port = MACH_PORT_NULL;
+
+/*
+ * Called by mach_init with 0 before cthread_init is
+ * called and again with initial cproc at end of cthread_init.
+ */
+void
+mig_init(initial)
+ register cproc_t initial;
+{
+ if (initial == NO_CPROC) {
+ /* called from mach_init before cthread_init,
+ possibly after a fork. clear global reply port. */
+
+ multithreaded = FALSE;
+ mig_reply_port = MACH_PORT_NULL;
+ } else {
+ /* recycle global reply port as this cthread's reply port */
+
+ multithreaded = TRUE;
+ initial->reply_port = mig_reply_port;
+ mig_reply_port = MACH_PORT_NULL;
+ }
+}
+
+/*
+ * Called by mig interface code whenever a reply port is needed.
+ */
+mach_port_t
+mig_get_reply_port()
+{
+ register mach_port_t reply_port;
+
+ if (multithreaded) {
+ register cproc_t self;
+
+ self = cproc_self();
+ ASSERT(self != NO_CPROC);
+
+ if ((reply_port = self->reply_port) == MACH_PORT_NULL)
+ self->reply_port = reply_port = mach_reply_port();
+ } else {
+ if ((reply_port = mig_reply_port) == MACH_PORT_NULL)
+ mig_reply_port = reply_port = mach_reply_port();
+ }
+
+ return reply_port;
+}
+
+/*
+ * Called by mig interface code after a timeout on the reply port.
+ * May also be called by user.
+ */
+void
+mig_dealloc_reply_port()
+{
+ register mach_port_t reply_port;
+
+ if (multithreaded) {
+ register cproc_t self;
+
+ self = cproc_self();
+ ASSERT(self != NO_CPROC);
+
+ reply_port = self->reply_port;
+ self->reply_port = MACH_PORT_NULL;
+ } else {
+ reply_port = mig_reply_port;
+ mig_reply_port = MACH_PORT_NULL;
+ }
+
+ (void) mach_port_mod_refs(mach_task_self(), reply_port,
+ MACH_PORT_RIGHT_RECEIVE, -1);
+}
diff --git a/libthreads/stack.c b/libthreads/stack.c
new file mode 100644
index 00000000..2764470f
--- /dev/null
+++ b/libthreads/stack.c
@@ -0,0 +1,382 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 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: stack.c,v $
+ * Revision 2.13 92/01/14 16:48:54 rpd
+ * Fixed addr_range_check to deallocate the object port from vm_region.
+ * [92/01/14 rpd]
+ *
+ * Revision 2.12 92/01/03 20:37:10 dbg
+ * Export cthread_stack_size, and use it if non-zero instead of
+ * probing the stack. Fix error in deallocating unused initial
+ * stack (STACK_GROWTH_UP case).
+ * [91/08/28 dbg]
+ *
+ * Revision 2.11 91/07/31 18:39:34 dbg
+ * Fix some bad stack references (stack direction).
+ * [91/07/30 17:36:50 dbg]
+ *
+ * Revision 2.10 91/05/14 17:58:49 mrt
+ * Correcting copyright
+ *
+ * Revision 2.9 91/02/14 14:21:08 mrt
+ * Added new Mach copyright
+ * [91/02/13 12:41:35 mrt]
+ *
+ * Revision 2.8 90/11/05 18:10:46 rpd
+ * Added cproc_stack_base. Add stack_fork_child().
+ * [90/11/01 rwd]
+ *
+ * Revision 2.7 90/11/05 14:37:51 rpd
+ * Fixed addr_range_check for new vm_region semantics.
+ * [90/11/02 rpd]
+ *
+ * Revision 2.6 90/10/12 13:07:34 rpd
+ * Deal with positively growing stacks.
+ * [90/10/10 rwd]
+ * Deal with initial user stacks that are not perfectly aligned.
+ * [90/09/26 11:51:46 rwd]
+ *
+ * Leave extra stack page around in case it is needed before we
+ * switch stacks.
+ * [90/09/25 rwd]
+ *
+ * Revision 2.5 90/08/07 14:31:46 rpd
+ * Removed RCS keyword nonsense.
+ *
+ * Revision 2.4 90/06/02 15:14:18 rpd
+ * Moved cthread_sp to machine-dependent files.
+ * [90/04/24 rpd]
+ * Converted to new IPC.
+ * [90/03/20 20:56:35 rpd]
+ *
+ * Revision 2.3 90/01/19 14:37:34 rwd
+ * Move self pointer to top of stack
+ * [89/12/12 rwd]
+ *
+ * Revision 2.2 89/12/08 19:49:52 rwd
+ * Back out change from af.
+ * [89/12/08 rwd]
+ *
+ * Revision 2.1.1.3 89/12/06 12:54:17 rwd
+ * Gap fix from af
+ * [89/12/06 rwd]
+ *
+ * Revision 2.1.1.2 89/11/21 15:01:40 rwd
+ * Add RED_ZONE ifdef.
+ * [89/11/20 rwd]
+ *
+ * Revision 2.1.1.1 89/10/24 13:00:44 rwd
+ * Remove conditionals.
+ * [89/10/23 rwd]
+ *
+ * Revision 2.1 89/08/03 17:10:05 rwd
+ * Created.
+ *
+ * 18-Jan-89 David Golub (dbg) at Carnegie-Mellon University
+ * Altered for stand-alone use:
+ * use vm_region to probe for the bottom of the initial thread's
+ * stack.
+ *
+ *
+ * 01-Dec-87 Eric Cooper (ecc) at Carnegie Mellon University
+ * Changed cthread stack allocation to use aligned stacks
+ * and store self pointer at base of stack.
+ * Added inline expansion for cthread_sp() function.
+ */
+/*
+ * File: stack.c
+ * Author: Eric Cooper, Carnegie Mellon University
+ * Date: Dec, 1987
+ *
+ * C Thread stack allocation.
+ *
+ */
+
+#include <cthreads.h>
+#include "cthread_internals.h"
+
+#define BYTES_TO_PAGES(b) (((b) + vm_page_size - 1) / vm_page_size)
+
+int cthread_stack_mask;
+vm_size_t cthread_stack_size;
+private vm_address_t next_stack_base;
+
+vm_offset_t cproc_stack_base(); /* forward */
+
+/*
+ * Set up a stack segment for a thread.
+ * Segment has a red zone (invalid page)
+ * for early detection of stack overflow.
+ * The cproc_self pointer is stored at the top.
+ *
+ * --------- (high address)
+ * | self |
+ * | ... |
+ * | |
+ * | stack |
+ * | |
+ * | ... |
+ * | |
+ * ---------
+ * | |
+ * |invalid|
+ * | |
+ * --------- (stack base)
+ * --------- (low address)
+ *
+ * or the reverse, if the stack grows up.
+ */
+
+private void
+setup_stack(p, base)
+ register cproc_t p;
+ register vm_address_t base;
+{
+ register kern_return_t r;
+
+ p->stack_base = base;
+ /*
+ * Stack size is segment size minus size of self pointer
+ */
+ p->stack_size = cthread_stack_size;
+ /*
+ * Protect red zone.
+ */
+#ifdef RED_ZONE
+ MACH_CALL(vm_protect(mach_task_self(), base + vm_page_size, vm_page_size, FALSE, VM_PROT_NONE), r);
+#endif RED_ZONE
+ /*
+ * Store self pointer.
+ */
+ *(cproc_t *)&ur_cthread_ptr(base) = p;
+}
+
+vm_offset_t
+addr_range_check(start_addr, end_addr, desired_protection)
+ vm_offset_t start_addr, end_addr;
+ vm_prot_t desired_protection;
+{
+ register vm_offset_t addr;
+
+ addr = start_addr;
+ while (addr < end_addr) {
+ vm_offset_t r_addr;
+ vm_size_t r_size;
+ vm_prot_t r_protection,
+ r_max_protection;
+ vm_inherit_t r_inheritance;
+ boolean_t r_is_shared;
+ memory_object_name_t r_object_name;
+ vm_offset_t r_offset;
+ kern_return_t kr;
+
+ r_addr = addr;
+ kr = vm_region(mach_task_self(), &r_addr, &r_size,
+ &r_protection, &r_max_protection, &r_inheritance,
+ &r_is_shared, &r_object_name, &r_offset);
+ if ((kr == KERN_SUCCESS) && MACH_PORT_VALID(r_object_name))
+ (void) mach_port_deallocate(mach_task_self(), r_object_name);
+
+ if ((kr != KERN_SUCCESS) ||
+ (r_addr > addr) ||
+ ((r_protection & desired_protection) != desired_protection))
+ return (0);
+ addr = r_addr + r_size;
+ }
+ return (addr);
+}
+
+/*
+ * Probe for bottom and top of stack.
+ * Assume:
+ * 1. stack grows DOWN
+ * 2. There is an unallocated region below the stack.
+ */
+void
+probe_stack(stack_bottom, stack_top)
+ vm_offset_t *stack_bottom;
+ vm_offset_t *stack_top;
+{
+ /*
+ * Since vm_region returns the region starting at
+ * or ABOVE the given address, we cannot use it
+ * directly to search downwards. However, we
+ * also want a size that is the closest power of
+ * 2 to the stack size (so we can mask off the stack
+ * address and get the stack base). So we probe
+ * in increasing powers of 2 until we find a gap
+ * in the stack.
+ */
+ vm_offset_t start_addr, end_addr;
+ vm_offset_t last_start_addr, last_end_addr;
+ vm_size_t stack_size;
+
+ /*
+ * Start with a page
+ */
+ start_addr = cthread_sp() & ~(vm_page_size - 1);
+ end_addr = start_addr + vm_page_size;
+
+ stack_size = vm_page_size;
+
+ /*
+ * Increase the tentative stack size, by doubling each
+ * time, until we have exceeded the stack (some of the
+ * range is not valid).
+ */
+ do {
+ /*
+ * Save last addresses
+ */
+ last_start_addr = start_addr;
+ last_end_addr = end_addr;
+
+ /*
+ * Double the stack size
+ */
+ stack_size <<= 1;
+ start_addr = end_addr - stack_size;
+
+ /*
+ * Check that the entire range exists and is writable
+ */
+ } while (end_addr = (addr_range_check(start_addr,
+ end_addr,
+ VM_PROT_READ|VM_PROT_WRITE)));
+ /*
+ * Back off to previous power of 2.
+ */
+ *stack_bottom = last_start_addr;
+ *stack_top = last_end_addr;
+}
+
+vm_offset_t
+stack_init(p)
+ cproc_t p;
+{
+ vm_offset_t stack_bottom,
+ stack_top,
+ start;
+ vm_size_t size;
+ kern_return_t r;
+
+ void alloc_stack();
+
+ /*
+ * Probe for bottom and top of stack, as a power-of-2 size.
+ */
+ probe_stack(&stack_bottom, &stack_top);
+
+ /*
+ * Use the stack size found for the Cthread stack size,
+ * if not already specified.
+ */
+ if (cthread_stack_size == 0)
+ cthread_stack_size = stack_top - stack_bottom;
+#ifdef STACK_GROWTH_UP
+ cthread_stack_mask = ~(cthread_stack_size - 1);
+#else STACK_GROWTH_UP
+ cthread_stack_mask = cthread_stack_size - 1;
+#endif STACK_GROWTH_UP
+
+ /*
+ * Guess at first available region for stack.
+ */
+ next_stack_base = 0;
+
+ /*
+ * Set up stack for main thread.
+ */
+ alloc_stack(p);
+
+ /*
+ * Delete rest of old stack.
+ */
+
+#ifdef STACK_GROWTH_UP
+ start = (cthread_sp() | (vm_page_size - 1)) + 1 + vm_page_size;
+ size = stack_top - start;
+#else STACK_GROWTH_UP
+ start = stack_bottom;
+ size = (cthread_sp() & ~(vm_page_size - 1)) - stack_bottom -
+ vm_page_size;
+#endif STACK_GROWTH_UP
+ MACH_CALL(vm_deallocate(mach_task_self(),start,size),r);
+
+ /*
+ * Return new stack; it gets passed back to the caller
+ * of cthread_init who must switch to it.
+ */
+ return cproc_stack_base(p, sizeof(ur_cthread_t *));
+}
+
+/*
+ * Allocate a stack segment for a thread.
+ * Stacks are never deallocated.
+ *
+ * The variable next_stack_base is used to align stacks.
+ * It may be updated by several threads in parallel,
+ * but mutual exclusion is unnecessary: at worst,
+ * the vm_allocate will fail and the thread will try again.
+ */
+
+void
+alloc_stack(p)
+ cproc_t p;
+{
+ vm_address_t base = next_stack_base;
+
+ for (base = next_stack_base;
+ vm_allocate(mach_task_self(), &base, cthread_stack_size, FALSE) != KERN_SUCCESS;
+ base += cthread_stack_size)
+ ;
+ next_stack_base = base + cthread_stack_size;
+ setup_stack(p, base);
+}
+
+vm_offset_t
+cproc_stack_base(cproc, offset)
+ register cproc_t cproc;
+ register int offset;
+{
+#ifdef STACK_GROWTH_UP
+ return (cproc->stack_base + offset);
+#else STACK_GROWTH_UP
+ return (cproc->stack_base + cproc->stack_size - offset);
+#endif STACK_GROWTH_UP
+
+}
+
+void stack_fork_child()
+/*
+ * Called in the child after a fork(). Resets stack data structures to
+ * coincide with the reality that we now have a single cproc and cthread.
+ */
+{
+ next_stack_base = 0;
+}