summaryrefslogtreecommitdiff
path: root/libthreads/cprocs.c
diff options
context:
space:
mode:
authorMichael I. Bushnell <mib@gnu.org>1992-10-06 18:31:16 +0000
committerMichael I. Bushnell <mib@gnu.org>1992-10-06 18:31:16 +0000
commitda328cb2c579c5b60725cbba903eff753061e17a (patch)
treec8f6634ad0298e628fa96bbd94f58dd504ef5d55 /libthreads/cprocs.c
parent964ab87456cf79f0f5787796c3e8917502d9f9d8 (diff)
Initial revision
Diffstat (limited to 'libthreads/cprocs.c')
-rw-r--r--libthreads/cprocs.c1122
1 files changed, 1122 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();
+}