home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Otherware
/
Otherware_1_SB_Development.iso
/
amiga
/
os
/
kludge03.tz
/
kludge03
/
mk74
/
user
/
threads
/
cprocs.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-03-08
|
29KB
|
1,123 lines
/*
* 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();
}