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 >
C/C++ Source or Header  |  1992-03-08  |  29KB  |  1,123 lines

  1. /* 
  2.  * Mach Operating System
  3.  * Copyright (c) 1991,1990,1989 Carnegie Mellon University
  4.  * All Rights Reserved.
  5.  * 
  6.  * Permission to use, copy, modify and distribute this software and its
  7.  * documentation is hereby granted, provided that both the copyright
  8.  * notice and this permission notice appear in all copies of the
  9.  * software, derivative works or modified versions, and any portions
  10.  * thereof, and that both notices appear in supporting documentation.
  11.  * 
  12.  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
  13.  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
  14.  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
  15.  * 
  16.  * Carnegie Mellon requests users of this software to return to
  17.  * 
  18.  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
  19.  *  School of Computer Science
  20.  *  Carnegie Mellon University
  21.  *  Pittsburgh PA 15213-3890
  22.  * 
  23.  * any improvements or extensions that they make and grant Carnegie Mellon
  24.  * the rights to redistribute these changes.
  25.  */
  26. /*
  27.  * HISTORY
  28.  * $Log:    cprocs.c,v $
  29.  * Revision 2.15  92/03/06  14:09:31  rpd
  30.  *     Replaced swtch_pri with yield.
  31.  *     [92/03/06            rpd]
  32.  * 
  33.  * Revision 2.14  91/08/28  11:19:16  jsb
  34.  *     Fixed the loop in cproc_fork_child that frees cprocs.
  35.  *     [91/08/23            rpd]
  36.  * 
  37.  * Revision 2.13  91/07/31  18:33:04  dbg
  38.  *     Fix some more bad types.  Ints are NOT pointers.
  39.  * 
  40.  *     Fix argument type mismatch in cproc_create.
  41.  *     [91/07/30  17:32:59  dbg]
  42.  * 
  43.  * Revision 2.12  91/05/14  17:56:11  mrt
  44.  *     Correcting copyright
  45.  * 
  46.  * Revision 2.11  91/02/14  14:19:26  mrt
  47.  *     Added new Mach copyright
  48.  *     [91/02/13  12:40:50  mrt]
  49.  * 
  50.  * Revision 2.10  90/11/05  14:36:41  rpd
  51.  *     Added cproc_fork_{prepare,parent,child}.
  52.  *     [90/11/02            rwd]
  53.  * 
  54.  *     Fix for positive stack growth.
  55.  *     [90/11/01            rwd]
  56.  * 
  57.  *     Add spin_lock_t.
  58.  *     [90/10/31            rwd]
  59.  * 
  60.  * Revision 2.9  90/10/12  13:07:12  rpd
  61.  *     Fix type
  62.  *     [90/10/10  15:09:59  rwd]
  63.  * 
  64.  *     Comment code.
  65.  *     [90/10/02            rwd]
  66.  * 
  67.  * Revision 2.8  90/09/09  14:34:44  rpd
  68.  *     Remove special mutex.  Remove thread_calls and debug_mutex
  69.  *     [90/08/24            rwd]
  70.  *     Fix up old call to cthread_msg_busy to new format.
  71.  *     [90/08/22            rwd]
  72.  * 
  73.  * Revision 2.7  90/08/06  15:09:17  rwd
  74.  *     Fixed arguments to cthread_mach_msg.
  75.  *     [90/06/26            rwd]
  76.  *     Add additional STATISTICS.
  77.  *     [90/06/07            rwd]
  78.  * 
  79.  *     Attempt to reduce number of times a cthread is released to to a
  80.  *     msg_receive by adding min/max instead of single number to
  81.  *     cthread_msg calls.
  82.  *     [90/06/06            rwd]
  83.  * 
  84.  * Revision 2.6  90/06/02  15:13:36  rpd
  85.  *     Converted to new IPC.
  86.  *     [90/03/20  20:46:16  rpd]
  87.  * 
  88.  * Revision 2.5  90/05/29  18:40:11  rwd
  89.  *     Don't incr special field until the mutex grab is successful.
  90.  *     [90/05/09            rwd]
  91.  * 
  92.  * Revision 2.4  90/03/14  21:12:02  rwd
  93.  *     Added WAIT_DEBUG code for deadlock debugging.
  94.  *     [90/03/01            rwd]
  95.  *     Insert cprocs in cproc_list as allocated.
  96.  *     [90/03/01  10:20:16  rwd]
  97.  * 
  98.  * Revision 2.3  90/01/19  14:36:57  rwd
  99.  *     Make cthread_msg_busy only release new thread if this is still
  100.  *     busy.  Ie don't release two on back to back calls.
  101.  *     [90/01/11            rwd]
  102.  *     Add THREAD_CALL code.  Add CPROC_ARUN state.
  103.  *     [90/01/03            rwd]
  104.  *     Add new cthread_msg_rpc call
  105.  *     [89/12/20            rwd]
  106.  *     Change cproc_self pointer to top of stack.  Now need to change
  107.  *     the stack of the first thread.
  108.  *     [89/12/12            rwd]
  109.  * 
  110.  * Revision 2.2  89/12/08  19:53:13  rwd
  111.  *     Added CPROC_CONDWAIT state to deal with lock held
  112.  *     across mutex_unlock problem.
  113.  *     [89/11/29            rwd]
  114.  *     Changed mutexes to not hand off.  MUTEX_EXTRA conditional is
  115.  *     now obsolete.
  116.  *     [89/11/27            rwd]
  117.  * 
  118.  *     Add MUTEX_EXTRA code for extra kernel threads to serve special
  119.  *     mutexes in time of need.
  120.  *     [89/11/25            rwd]
  121.  *     Add MUTEX_SPECIAL and DEBUG_MUTEX code
  122.  *     [89/11/24            rwd]
  123.  *     Changed mutex_lock to mutex_lock_solid.  Mutex_lock is now a
  124.  *     macro which tries the spin_lock before making a subroutine call.
  125.  *     Mutex_unlock is now a macro with mutex_unlock_solid for worst case.
  126.  *     [89/11/13            rwd]
  127.  * 
  128.  *     Rewrite most to merge coroutine and thread implementation.
  129.  *     New routines are cthread_set_kernel_limit, cthread_kernel_limit,
  130.  *     cthread_wire, cthread_unwire, and cthread_receive.
  131.  *     [89/10/23            rwd]
  132.  * 
  133.  * Revision 2.1  89/08/03  17:07:10  rwd
  134.  * Created.
  135.  * 
  136.  * 11-Apr-89  David Golub (dbg) at Carnegie-Mellon University
  137.  *    Made condition_yield loop break if swtch_pri returns TRUE (in
  138.  *    case we fix it).
  139.  *
  140.  * 31-Mar-89  David Golub (dbg) at Carnegie-Mellon University
  141.  *    Change cond_signal, cond_broadcast, and cproc_continue so that
  142.  *    the condition's spin lock is not held while continuing the
  143.  *    process.
  144.  *
  145.  * 16-Jan-89  David Golub (dbg) at Carnegie-Mellon University
  146.  *    Changes for stand-alone library to run on pure kernel:
  147.  *    . made IPC_WAIT standard, as calls that are used if IPC_WAIT == 0
  148.  *      vanished a year ago.
  149.  *    . Removed (as much as possible) references to stdio or other U*X
  150.  *      features.
  151.  *
  152.  *
  153.  * 01-Apr-88  Eric Cooper (ecc) at Carnegie Mellon University
  154.  *    Changed condition_clear(c) to acquire c->lock,
  155.  *    to serialize after any threads still doing condition_signal(c).
  156.  *    Suggested by Dan Julin.
  157.  *
  158.  * 19-Feb-88  Eric Cooper (ecc) at Carnegie Mellon University
  159.  *     Extended the inline scripts to handle spin_unlock() and mutex_unlock().
  160.  *
  161.  * 28-Jan-88  David Golub (dbg) at Carnegie Mellon University
  162.  *    Removed thread_data argument from thread_create
  163.  *    and converted to new thread_set_state call.
  164.  *
  165.  * 01-Dec-87  Eric Cooper (ecc) at Carnegie Mellon University
  166.  *    Added inline expansion for cthread_sp() function.
  167.  *
  168.  * 21-Aug-87  Eric Cooper (ecc) at Carnegie Mellon University
  169.  *    Fixed uninitialized reply_port in cproc_alloc() (found by rds).
  170.  *
  171.  * 14-Aug-87  Eric Cooper (ecc) at Carnegie Mellon University
  172.  *    Tried using return value of swtch() to guide condition_wait().
  173.  *    Performance was worse than using a hybrid spin/yield/block
  174.  *    scheme, so the version using swtch() was commented out.
  175.  *    Disabled IPC_WAIT in released version.
  176.  *
  177.  * 13-Aug-87  Eric Cooper (ecc) at Carnegie Mellon University
  178.  *    Added IPC_WAIT option.
  179.  *    If defined, thread synchronization (condition_wait() and
  180.  *    cproc_continue()) are implemented using    msg_receive() and
  181.  *    msg_send() instead of thread_suspend() and thread_resume().
  182.  *
  183.  * 11-Aug-87  Eric Cooper (ecc) at Carnegie Mellon University
  184.  *    Moved thread reply port to cproc structure in cthread_internals.h,
  185.  *    because mig calls are made while cproc is idle (no cthread structure).
  186.  *    Changed cproc_switch() and cproc_start (COROUTINE implementation)
  187.  *    to use address of saved context, rather than address of enclosing cproc,
  188.  *    to eliminate dependency on cproc layout.
  189.  */
  190. /*
  191.  *     File:     cprocs.c 
  192.  *    Author: Eric Cooper, Carnegie Mellon University
  193.  *    Date:    Aug, 1987
  194.  *
  195.  *     Implementation of cprocs (lightweight processes)
  196.  *     and primitive synchronization operations.
  197.  */
  198.  
  199.  
  200. #include <cthreads.h>
  201. #include "cthread_internals.h"
  202. #include <mach/message.h>
  203.  
  204. /*
  205.  * C Threads imports:
  206.  */
  207. extern void alloc_stack();
  208. extern void cproc_switch();        /* cproc context switch */
  209. extern void cproc_start_wait();        /* cproc idle thread */
  210. extern vm_offset_t cproc_stack_base();    /* return start of stack */
  211. extern vm_offset_t stack_init();
  212.  
  213. /*
  214.  * Port_entry's are used by cthread_mach_msg to store information
  215.  * about each port/port_set for which it is managing threads
  216.  */
  217.  
  218. typedef struct port_entry {
  219.     struct port_entry *next;    /* next port_entry */
  220.     mach_port_t    port;        /* which port/port_set */
  221.     struct cthread_queue queue;    /* queue of runnable threads for
  222.                        this port/port_set */
  223.     int min;            /* minimum number of kernel threads
  224.                        to be used by this port/port_set */
  225.     int max;            /* maximum number of kernel threads
  226.                        to be used by this port/port_set */
  227.     int held;            /* actual number of kernel threads
  228.                        currentlt in use */
  229.     spin_lock_t lock;        /* lock governing all above fields */
  230. } *port_entry_t;
  231.  
  232. #define PORT_ENTRY_NULL ((port_entry_t) 0)
  233.  
  234. /* Available to outside for statistics */
  235.  
  236. int cthread_wait_stack_size = 8192;    /* stack size for idle threads */
  237. int cthread_max_kernel_threads = 0;    /* max kernel threads */
  238. int cthread_kernel_threads = 0;        /* current kernel threads */
  239. private spin_lock_t n_kern_lock = SPIN_LOCK_INITIALIZER;
  240.                     /* lock for 2 above */
  241. #ifdef STATISTICS
  242. int cthread_ready = 0;            /* currently runnable */
  243. int cthread_running = 1;        /* currently running */
  244. int cthread_waiting = 0;        /* currently waiting */
  245. int cthread_wired = 0;            /* currently wired */
  246. private spin_lock_t wired_lock = SPIN_LOCK_INITIALIZER;
  247.                     /* lock for above */
  248. int cthread_wait_stacks = 0;        /* total cthread waiting stacks */
  249. int cthread_waiters = 0;        /* total of watiers */
  250. int cthread_wakeup = 0;            /* total times woken when starting to
  251.                        block */
  252. int cthread_blocked = 0;        /* total blocked */
  253. int cthread_rnone = 0;            /* total times no cthread available
  254.                        to meet minimum for port_entry */
  255. int cthread_yields = 0;            /* total cthread_yields */
  256. int cthread_none = 0;            /* total idle wakeups w/o runnable */
  257. int cthread_switches = 0;        /* total number of cproc_switches */
  258. int cthread_no_mutex = 0;        /* total number times woken to get
  259.                        mutex and couldn't */
  260. private spin_lock_t mutex_count_lock = SPIN_LOCK_INITIALIZER;
  261.                     /* lock for above */
  262. #endif STATISTICS
  263.  
  264. cproc_t cproc_list = NO_CPROC;        /* list of all cprocs */
  265. private cproc_list_lock = SPIN_LOCK_INITIALIZER;
  266.                     /* lock for above */
  267. private int cprocs_started = FALSE;    /* initialized? */
  268. private struct cthread_queue ready = QUEUE_INITIALIZER;
  269.                     /* ready queue */
  270. private int ready_count = 0;        /* number of ready threads on ready
  271.                        queue - number of messages sent */
  272. private spin_lock_t ready_lock = SPIN_LOCK_INITIALIZER;
  273.                     /* lock for 2 above */
  274. private mach_port_t wait_port = MACH_PORT_NULL;
  275.                     /* port on which idle threads wait */
  276. private int wait_count = 0;        /* number of waiters - messages pending
  277.                        to wake them */
  278. private struct cthread_queue waiters = QUEUE_INITIALIZER;
  279.                     /* queue of cthreads to run as idle */
  280. private spin_lock_t waiters_lock = SPIN_LOCK_INITIALIZER;
  281.                     /* lock for 2 above */
  282. private port_entry_t port_list = PORT_ENTRY_NULL;
  283.                     /* master list of port_entries */
  284. private spin_lock_t port_lock = SPIN_LOCK_INITIALIZER;
  285.                     /* lock for above queue */
  286. private mach_msg_header_t wakeup_msg;    /* prebuilt message used by idle
  287.                        threads */
  288.  
  289. /*
  290.  * Return current value for max kernel threads
  291.  * Note: 0 means no limit
  292.  */
  293.  
  294. cthread_kernel_limit()
  295. {
  296.     return cthread_max_kernel_threads;
  297. }
  298.  
  299. /*
  300.  * Set max number of kernel threads
  301.  * Note:    This will not currently terminate existing threads
  302.  *         over maximum.
  303.  */
  304.  
  305. cthread_set_kernel_limit(n)
  306.     int n;
  307. {
  308.     cthread_max_kernel_threads = n;
  309. }
  310.  
  311. /*
  312.  * Wire a cthread to its current kernel thread
  313.  */
  314.  
  315. void cthread_wire()
  316. {
  317.     register cproc_t p = cproc_self();
  318.     kern_return_t r;
  319.  
  320.     /*
  321.      * A wired thread has a port associated with it for all
  322.      * of its wait/block cases.  We also prebuild a wakeup
  323.      * message.
  324.      */
  325.  
  326.     if (p->wired == MACH_PORT_NULL) {
  327.         MACH_CALL(mach_port_allocate(mach_task_self(),
  328.                          MACH_PORT_RIGHT_RECEIVE,
  329.                          &p->wired), r);
  330.         MACH_CALL(mach_port_insert_right(mach_task_self(),
  331.                          p->wired, p->wired,
  332.                          MACH_MSG_TYPE_MAKE_SEND), r);
  333.         p->msg.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
  334.         p->msg.msgh_size = 0; /* initialized in call */
  335.         p->msg.msgh_remote_port = p->wired;
  336.         p->msg.msgh_local_port = MACH_PORT_NULL;
  337.         p->msg.msgh_kind = MACH_MSGH_KIND_NORMAL;
  338.         p->msg.msgh_id = 0;
  339. #ifdef STATISTICS
  340.         spin_lock(&wired_lock);
  341.         cthread_wired++;
  342.         spin_unlock(&wired_lock);
  343. #endif STATISTICS
  344.     }
  345. }
  346.  
  347. /*
  348.  * Unwire a cthread.  Deallocate its wait port.
  349.  */
  350.  
  351. void cthread_unwire()
  352. {
  353.     register cproc_t p = cproc_self();
  354.     kern_return_t r;
  355.  
  356.     if (p->wired != MACH_PORT_NULL) {
  357.         MACH_CALL(mach_port_mod_refs(mach_task_self(), p->wired,
  358.                          MACH_PORT_RIGHT_SEND, -1), r);
  359.         MACH_CALL(mach_port_mod_refs(mach_task_self(), p->wired,
  360.                          MACH_PORT_RIGHT_RECEIVE, -1), r);
  361.         p->wired = MACH_PORT_NULL;
  362. #ifdef STATISTICS
  363.         spin_lock(&wired_lock);
  364.         cthread_wired--;
  365.         spin_unlock(&wired_lock);
  366. #endif STATISTICS
  367.     }
  368. }
  369.  
  370. private cproc_t
  371. cproc_alloc()
  372. {
  373.     register cproc_t p = (cproc_t) malloc(sizeof(struct cproc));
  374.  
  375.     p->incarnation = NO_CTHREAD;
  376.     p->reply_port = MACH_PORT_NULL;
  377.  
  378.     spin_lock_init(&p->lock);
  379.     p->wired = MACH_PORT_NULL;
  380.     p->state = CPROC_RUNNING;
  381.     p->busy = 0;
  382.     spin_lock(&cproc_list_lock);
  383.     p->list = cproc_list;
  384.     cproc_list = p;
  385.     spin_unlock(&cproc_list_lock);
  386.  
  387.     return p;
  388. }
  389.  
  390. /*
  391.  * Called by cthread_init to set up initial data structures.
  392.  */
  393.  
  394. vm_offset_t
  395. cproc_init()
  396. {
  397.     kern_return_t r;
  398.  
  399.     cproc_t p = cproc_alloc();
  400.  
  401.     cthread_kernel_threads = 1;
  402.  
  403.     MACH_CALL(mach_port_allocate(mach_task_self(),
  404.                      MACH_PORT_RIGHT_RECEIVE,
  405.                      &wait_port), r);
  406.     MACH_CALL(mach_port_insert_right(mach_task_self(),
  407.                      wait_port, wait_port,
  408.                      MACH_MSG_TYPE_MAKE_SEND), r);
  409.  
  410.     wakeup_msg.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
  411.     wakeup_msg.msgh_size = 0; /* initialized in call */
  412.     wakeup_msg.msgh_remote_port = wait_port;
  413.     wakeup_msg.msgh_local_port = MACH_PORT_NULL;
  414.     wakeup_msg.msgh_kind = MACH_MSGH_KIND_NORMAL;
  415.     wakeup_msg.msgh_id = 0;
  416.  
  417.     cprocs_started = TRUE;
  418.  
  419.  
  420.     /*
  421.      * We pass back the new stack which should be switched to
  422.      * by crt0.  This guarantess correct size and alignment.
  423.      */
  424.     return (stack_init(p));
  425. }
  426.  
  427. /*
  428.  * Insert cproc on ready queue.  Make sure it is ready for queue by
  429.  * synching on its lock.  Just send message to wired cproc.
  430.  */
  431.  
  432. private int cproc_ready(p, preq)
  433.     register cproc_t p;
  434.     register int preq;
  435. {
  436.     register cproc_t s=cproc_self();
  437.     kern_return_t r;
  438.  
  439.     if (p->wired != MACH_PORT_NULL) {
  440.         r = mach_msg(&p->msg, MACH_SEND_MSG,
  441.                  sizeof p->msg, 0, MACH_PORT_NULL,
  442.                  MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
  443. #ifdef CHECK_STATUS
  444.         if (r != MACH_MSG_SUCCESS) {
  445.             mach_error("mach_msg", r);
  446.             exit(1);
  447.         }
  448. #endif CHECK_STATUS
  449.         return TRUE;
  450.     }
  451.     spin_lock(&p->lock);        /* is it ready to be queued?  It
  452.                        can appear on a queue before
  453.                        being switched from.  This lock
  454.                        is released by cproc_switch as
  455.                        its last operation. */
  456.     if (p->state & CPROC_SWITCHING) {
  457.         /*
  458.          * We caught it early on.  Just set to RUNNING
  459.          * and we will save a lot of time.
  460.          */
  461.         p->state = (p->state & ~CPROC_SWITCHING) | CPROC_RUNNING;
  462.         spin_unlock(&p->lock);
  463.         return TRUE;
  464.     }
  465.     spin_unlock(&p->lock);
  466.  
  467.     spin_lock(&ready_lock);
  468.  
  469.     if (preq) {
  470.         cthread_queue_preq(&ready, p);
  471.     } else {
  472.         cthread_queue_enq(&ready, p);
  473.     }
  474. #ifdef STATISTICS
  475.     cthread_ready++;
  476. #endif STATISTICS
  477.     ready_count++;
  478.  
  479.     if ((s->state & CPROC_CONDWAIT) && !(s->wired)) {
  480.         /*
  481.          * This is an optimiztion.  Don't bother waking anyone to grab
  482.          * this guy off the ready queue since my thread will block
  483.          * momentarily for the condition wait.
  484.          */
  485.  
  486.         spin_unlock(&ready_lock);
  487.         return TRUE;
  488.     }
  489.  
  490.     if ((ready_count > 0) && wait_count) {
  491.         wait_count--;
  492.         ready_count--;
  493.         spin_unlock(&ready_lock);
  494.         r = mach_msg(&wakeup_msg, MACH_SEND_MSG,
  495.                  sizeof wakeup_msg, 0, MACH_PORT_NULL,
  496.                  MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
  497. #ifdef CHECK_STATUS
  498.         if (r != MACH_MSG_SUCCESS) {
  499.             mach_error("mach_msg", r);
  500.             exit(1);
  501.         }
  502. #endif CHECK_STATUS
  503.         return TRUE;
  504.     }
  505.     spin_unlock(&ready_lock);
  506.     return FALSE;
  507. }
  508.  
  509. /*
  510.  * This is only run on a partial "waiting" stack and called from
  511.  * cproc_start_wait
  512.  */
  513.  
  514. void
  515. cproc_waiting(p)
  516.     register cproc_t p;
  517. {
  518.     mach_msg_header_t msg;
  519.     register cproc_t new;
  520.     kern_return_t r;
  521.  
  522. #ifdef STATISTICS
  523.     spin_lock(&ready_lock);
  524.     cthread_waiting++;
  525.     cthread_waiters++;
  526.     spin_unlock(&ready_lock);
  527. #endif STATISTICS
  528.     for (;;) {
  529.         MACH_CALL(mach_msg(&msg, MACH_RCV_MSG,
  530.                    0, sizeof msg, wait_port,
  531.                    MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL), r);
  532.         spin_lock(&ready_lock);
  533.         cthread_queue_deq(&ready, cproc_t, new);
  534.         if (new != NO_CPROC) break;
  535.         wait_count++;
  536.         ready_count++;
  537. #ifdef STATISTICS
  538.         cthread_none++;
  539. #endif STATISTICS
  540.         spin_unlock(&ready_lock);
  541.     } 
  542. #ifdef STATISTICS
  543.     cthread_ready--;
  544.     cthread_running++;
  545.     cthread_waiting--;
  546. #endif STATISTICS
  547.     spin_unlock(&ready_lock);
  548.     spin_lock(&new->lock);
  549.     new->state = CPROC_RUNNING;
  550.     spin_unlock(&new->lock);
  551.     spin_lock(&waiters_lock);
  552.     cthread_queue_enq(&waiters, p);
  553.     spin_lock(&p->lock);
  554.     spin_unlock(&waiters_lock);
  555.     cproc_switch(&p->context,&new->context,&p->lock);
  556. }
  557.  
  558. /*
  559.  * Get a waiter with stack
  560.  *
  561.  */
  562.  
  563. cproc_t
  564. cproc_waiter()
  565. {
  566.     register cproc_t waiter;
  567.  
  568.     spin_lock(&waiters_lock);
  569.     cthread_queue_deq(&waiters, cproc_t, waiter);
  570.     spin_unlock(&waiters_lock);
  571.     if (waiter == NO_CPROC) {
  572.         vm_address_t base;
  573.         kern_return_t r;
  574. #ifdef STATISTICS
  575.         spin_lock(&waiters_lock);
  576.         cthread_wait_stacks++;
  577.         spin_unlock(&waiters_lock);
  578. #endif STATISTICS
  579.         waiter = cproc_alloc();
  580.         MACH_CALL(vm_allocate(mach_task_self(), &base,
  581.                       cthread_wait_stack_size, TRUE), r);
  582.         waiter->stack_base = base;
  583.         waiter->stack_size = cthread_wait_stack_size;
  584.     }
  585.     return (waiter);
  586. }
  587.  
  588.  
  589. /*
  590.  * Current cproc is blocked so switch to any ready cprocs, or, if
  591.  * none, go into the wait state.
  592.  *
  593.  * You must hold cproc_self()->lock when called.
  594.  */
  595.  
  596. cproc_block()
  597. {
  598.     register cproc_t waiter, new, p = cproc_self();
  599.     register int extra;
  600.  
  601.     if (p->wired != MACH_PORT_NULL) {
  602.         mach_msg_header_t msg;
  603.         kern_return_t r;
  604.  
  605.         spin_unlock(&p->lock);
  606.         MACH_CALL(mach_msg(&msg, MACH_RCV_MSG,
  607.                    0, sizeof msg, p->wired,
  608.                    MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL), r);
  609.         return;
  610.     }
  611.     p->state = CPROC_SWITCHING;
  612.     spin_unlock(&p->lock);
  613.     spin_lock(&ready_lock);
  614. #ifdef STATISTICS
  615.     cthread_blocked++;
  616. #endif STATISTICS
  617.     cthread_queue_deq(&ready, cproc_t, new);
  618.     if (new) {
  619. #ifdef STATISTICS
  620.         cthread_ready--;
  621.         cthread_switches++;
  622. #endif STATISTICS
  623.         ready_count--;
  624.         spin_unlock(&ready_lock);
  625.         spin_lock(&p->lock);
  626.         if (p->state == CPROC_RUNNING) { /* have we been saved */
  627.             spin_unlock(&p->lock);
  628. #ifdef STATISTICS
  629.             spin_lock(&ready_lock);
  630.             cthread_wakeup++;
  631.             cthread_switches--;
  632.             spin_unlock(&ready_lock);
  633. #endif STATISTICS
  634.             cproc_ready(new, 1);  /* requeue at head were it was */
  635.         } else {
  636.             p->state = CPROC_BLOCKED;
  637.             spin_lock(&new->lock); /* incase still switching */
  638.             new->state = CPROC_RUNNING;
  639.             spin_unlock(&new->lock);
  640.             cproc_switch(&p->context,&new->context,&p->lock);
  641.         }
  642.     } else {
  643.         wait_count++;
  644. #ifdef STATISTICS
  645.         cthread_running--;
  646. #endif STATISTICS
  647.         spin_unlock(&ready_lock);
  648.         waiter = cproc_waiter();
  649.         spin_lock(&p->lock);
  650.         if (p->state == CPROC_RUNNING) { /* we have been saved */
  651.             spin_unlock(&p->lock);
  652.             spin_lock(&ready_lock);
  653.             wait_count--;
  654. #ifdef STATISTICS
  655.             cthread_running++;
  656.             cthread_wakeup++;
  657. #endif STATISTICS
  658.             spin_unlock(&ready_lock);
  659.             spin_lock(&waiters_lock);
  660.             cthread_queue_preq(&waiters, waiter);
  661.             spin_unlock(&waiters_lock);
  662.         } else {
  663.             p->state = CPROC_BLOCKED;
  664.             spin_lock(&waiter->lock); /* in case still switching */
  665.             spin_unlock(&waiter->lock);
  666.             cproc_start_wait(&p->context, waiter, 
  667.              cproc_stack_base(waiter, sizeof(ur_cthread_t *)),
  668.              &p->lock);
  669.         }
  670.     }
  671. }
  672.  
  673. /*
  674.  * Implement C threads using MACH threads.
  675.  */
  676. cproc_t
  677. cproc_create()
  678. {
  679.     register cproc_t child = cproc_alloc();
  680.     register kern_return_t r;
  681.     extern void cproc_setup();
  682.     extern void cproc_prepare();
  683.     extern void cthread_body();
  684.     thread_t n;
  685.  
  686.           alloc_stack(child);
  687.     spin_lock(&n_kern_lock);
  688.     if (cthread_max_kernel_threads == 0 ||
  689.         cthread_kernel_threads < cthread_max_kernel_threads) {
  690.         cthread_kernel_threads++;
  691.         spin_unlock(&n_kern_lock);
  692.         MACH_CALL(thread_create(mach_task_self(), &n), r);
  693.         cproc_setup(child, n, cthread_body);    /* machine dependent */
  694.         MACH_CALL(thread_resume(n), r);
  695. #ifdef STATISTICS
  696.         spin_lock(&ready_lock);
  697.         cthread_running++;
  698.         spin_unlock(&ready_lock);
  699. #endif STATISTICS
  700.     } else {
  701.         spin_unlock(&n_kern_lock);
  702.         child->state = CPROC_BLOCKED;
  703.         cproc_prepare(child, &child->context,
  704.             cproc_stack_base(child, 0));
  705.         cproc_ready(child,0);
  706.     }
  707.     return child;
  708. }
  709.  
  710. void
  711. condition_wait(c, m)
  712.     register condition_t c;
  713.     mutex_t m;
  714. {
  715.     register cproc_t p = cproc_self();
  716.  
  717.     p->state = CPROC_CONDWAIT | CPROC_SWITCHING;
  718.  
  719.     spin_lock(&c->lock);
  720.     cthread_queue_enq(&c->queue, p);
  721.     spin_unlock(&c->lock);
  722. #ifdef    WAIT_DEBUG
  723.     p->waiting_for = (char *)c;
  724. #endif    WAIT_DEBUG
  725.  
  726.     mutex_unlock(m);
  727.  
  728.     spin_lock(&p->lock);
  729.     if (p->state & CPROC_SWITCHING) {
  730.         cproc_block();
  731.     } else {
  732.         p->state = CPROC_RUNNING;
  733.         spin_unlock(&p->lock);
  734.     }
  735.  
  736.  
  737. #ifdef    WAIT_DEBUG
  738.     p->waiting_for = (char *)0;
  739. #endif    WAIT_DEBUG
  740.  
  741.     /*
  742.      * Re-acquire the mutex and return.
  743.      */
  744.     mutex_lock(m);
  745. }
  746.  
  747. void
  748. cond_signal(c)
  749.     register condition_t c;
  750. {
  751.     register cproc_t p;
  752.  
  753.     spin_lock(&c->lock);
  754.     cthread_queue_deq(&c->queue, cproc_t, p);
  755.     spin_unlock(&c->lock);
  756.     if (p != NO_CPROC) {
  757.         cproc_ready(p,0);
  758.     }
  759. }
  760.  
  761. void
  762. cond_broadcast(c)
  763.     register condition_t c;
  764. {
  765.     register cproc_t p;
  766.     struct cthread_queue blocked_queue;
  767.  
  768.     cthread_queue_init(&blocked_queue);
  769.  
  770.     spin_lock(&c->lock);
  771.     for (;;) {
  772.         register int    old_state;
  773.  
  774.         cthread_queue_deq(&c->queue, cproc_t, p);
  775.         if (p == NO_CPROC)
  776.             break;
  777.         cthread_queue_enq(&blocked_queue, p);
  778.     }
  779.     spin_unlock(&c->lock);
  780.  
  781.     for(;;) {
  782.         cthread_queue_deq(&blocked_queue, cproc_t, p);
  783.         if (p == NO_CPROC)
  784.             break;
  785.         cproc_ready(p,0);
  786.     }
  787. }
  788.  
  789. void
  790. cthread_yield()
  791. {
  792.     register cproc_t new, p = cproc_self();
  793.  
  794.     if (p->wired != MACH_PORT_NULL) {
  795.         yield();
  796.         return;
  797.     }
  798.     spin_lock(&ready_lock);
  799. #ifdef STATISTICS
  800.     cthread_yields++;
  801. #endif STATISTICS
  802.     cthread_queue_deq(&ready, cproc_t, new);
  803.     if (new) {
  804.         cthread_queue_enq(&ready, p);
  805.         spin_lock(&p->lock);
  806.         p->state = CPROC_BLOCKED;
  807.         spin_unlock(&ready_lock);
  808.         spin_lock(&new->lock);
  809.         new->state = CPROC_RUNNING;
  810.         spin_unlock(&new->lock);
  811.         cproc_switch(&p->context,&new->context,&p->lock);
  812.     } else {
  813.         spin_unlock(&ready_lock);
  814.         yield();
  815.     }
  816. }
  817.  
  818. /*
  819.  * Mutex objects.
  820.  */
  821.  
  822. void
  823. mutex_lock_solid(m)
  824.     register mutex_t m;
  825. {
  826.     register cproc_t p = cproc_self();
  827.     register int queued;
  828.     register int tried = 0;
  829.  
  830. #ifdef    WAIT_DEBUG
  831.     p->waiting_for = (char *)m;
  832. #endif    WAIT_DEBUG
  833.     while (1) {
  834.         spin_lock(&m->lock);
  835.         if (cthread_queue_head(&m->queue, cproc_t) == NO_CPROC) {
  836.             cthread_queue_enq(&m->queue, p);
  837.             queued = 1;
  838.         } else {
  839.             queued = 0;
  840.         }
  841.         if (spin_try_lock(&m->held)) {
  842.             if (queued) cthread_queue_deq(&m->queue, cproc_t, p);
  843.             spin_unlock(&m->lock);
  844. #ifdef    WAIT_DEBUG
  845.             p->waiting_for = (char *)0;
  846. #endif    WAIT_DEBUG
  847.             return;
  848.         } else {
  849.             if (!queued) cthread_queue_enq(&m->queue, p);
  850.             spin_lock(&p->lock);
  851.             spin_unlock(&m->lock);
  852.             cproc_block();
  853.             if (spin_try_lock(&m->held)) {
  854. #ifdef    WAIT_DEBUG
  855.                 p->waiting_for = (char *)0;
  856. #endif    WAIT_DEBUG
  857.                 return;
  858.             }
  859. #ifdef STATISTICS
  860.             spin_lock(&mutex_count_lock);
  861.             cthread_no_mutex++;
  862.             spin_unlock(&mutex_count_lock);
  863. #endif STATISTICS
  864.         }
  865.     }
  866. }
  867.  
  868. void
  869. mutex_unlock_solid(m)
  870.     register mutex_t m;
  871. {
  872.     register cproc_t new;
  873.  
  874.     if (!spin_try_lock(&m->held))
  875.         return;
  876.     spin_lock(&m->lock);
  877.     cthread_queue_deq(&m->queue, cproc_t, new);
  878.     spin_unlock(&m->held);
  879.     spin_unlock(&m->lock);
  880.     if (new) {
  881.         cproc_ready(new,0);
  882.     }
  883. }
  884.  
  885. /*
  886.  * Use instead of mach_msg in a multi-threaded server so as not
  887.  * to tie up excessive kernel threads.  This uses a simple linked list for
  888.  * ports since this should never be more than a few.
  889.  */
  890.  
  891. /*
  892.  * A cthread holds a reference to a port_entry even after it receives a
  893.  * message.  This reference is not released until the thread does a
  894.  * cthread_msg_busy.  This allows the fast case of a single mach_msg
  895.  * call to occur as often as is possible.
  896.  */
  897.  
  898. private port_entry_t get_port_entry(port, min, max)
  899.     mach_port_t port;
  900. {
  901.     register port_entry_t i;
  902.  
  903.     spin_lock(&port_lock);
  904.     for(i=port_list;i!=PORT_ENTRY_NULL;i=i->next)
  905.         if (i->port == port) {
  906.             spin_unlock(&port_lock);
  907.             return i;
  908.         }
  909.     i = (port_entry_t)malloc(sizeof(struct port_entry));
  910.     cthread_queue_init(&i->queue);
  911.     i->port = port;
  912.     i->next = port_list;
  913.     port_list = i;
  914.     i->min = min;
  915.     i->max = max;
  916.     i->held = 0;
  917.     spin_lock_init(&i->lock);
  918.     spin_unlock(&port_lock);
  919.     return i;
  920. }
  921.  
  922. cthread_msg_busy(port, min, max)
  923.     mach_port_t port;
  924. {
  925.     register port_entry_t port_entry;
  926.     register cproc_t new, p = cproc_self();
  927.  
  928.     if (p->busy) {
  929.         port_entry = get_port_entry(port, min, max);
  930.         spin_lock(&port_entry->lock);
  931.         p->busy = 0;
  932.         if (port_entry->held <= port_entry->min) {
  933.         cthread_queue_deq(&port_entry->queue, cproc_t, new);
  934.         if (new != NO_CPROC){
  935.             spin_unlock(&port_entry->lock);
  936.             cproc_ready(new,0);
  937.         } else {
  938.             port_entry->held--;
  939.             spin_unlock(&port_entry->lock);
  940. #ifdef STATISTICS
  941.             spin_lock(&port_lock);
  942.             cthread_rnone++;
  943.             spin_unlock(&port_lock);
  944. #endif STATISTICS
  945.         }
  946.         } else {
  947.         port_entry->held--;
  948.         spin_unlock(&port_entry->lock);
  949.         }
  950.     }
  951.  
  952. }
  953.  
  954. cthread_msg_active(port, min, max)
  955. mach_port_t port;
  956. {
  957.     register cproc_t p = cproc_self();
  958.     register port_entry_t port_entry;
  959.  
  960.     if (!p->busy) {
  961.         port_entry = get_port_entry(port, min, max);
  962.         if (port_entry == 0) return;
  963.         spin_lock(&port_entry->lock);
  964.         if (port_entry->held < port_entry->max) {
  965.         port_entry->held++;
  966.         p->busy = (int)port_entry;
  967.         }
  968.         spin_unlock(&port_entry->lock);
  969.     }
  970. }
  971.  
  972. mach_msg_return_t
  973. cthread_mach_msg(header, option,
  974.          send_size, rcv_size, rcv_name,
  975.          timeout, notify, min, max)
  976.     register mach_msg_header_t *header;
  977.     register mach_msg_option_t option;
  978.     mach_msg_size_t send_size;
  979.     mach_msg_size_t rcv_size;
  980.     register mach_port_t rcv_name;
  981.     mach_msg_timeout_t timeout;
  982.     mach_port_t notify;
  983.     int min, max;
  984. {
  985.     register port_entry_t port_entry;
  986.     register cproc_t p = cproc_self();
  987.     register int sent=0;
  988.     mach_msg_return_t r;
  989.     port_entry_t op = (port_entry_t)p->busy;
  990.  
  991.     port_entry = get_port_entry(rcv_name, min, max);
  992.  
  993.     if (op && (port_entry_t)op != port_entry)
  994.         cthread_msg_busy(op->port, op->min, op->max);
  995.     spin_lock(&port_entry->lock);
  996.     if (!(port_entry == (port_entry_t)p->busy)) {
  997.         if (port_entry->held >= max) {
  998.         if (option & MACH_SEND_MSG) {
  999.             spin_unlock(&port_entry->lock);
  1000.             r = mach_msg(header, option &~ MACH_RCV_MSG,
  1001.                  send_size, 0, MACH_PORT_NULL,
  1002.                  timeout, notify);
  1003.             if (r != MACH_MSG_SUCCESS) return r;
  1004.             spin_lock(&port_entry->lock);
  1005.             sent=1;
  1006.         }
  1007.         if (port_entry->held >= max) {
  1008.             spin_lock(&p->lock);
  1009.             cthread_queue_preq(&port_entry->queue, p);
  1010.             spin_unlock(&port_entry->lock);
  1011. #ifdef    WAIT_DEBUG
  1012.             p->waiting_for = (char *)port_entry;
  1013. #endif    WAIT_DEBUG
  1014.             cproc_block();
  1015.         } else {
  1016.             port_entry->held++;
  1017.             spin_unlock(&port_entry->lock);
  1018.         }
  1019.         } else {
  1020.         port_entry->held++;
  1021.         spin_unlock(&port_entry->lock);
  1022.         }
  1023.     } else {
  1024.         spin_unlock(&port_entry->lock);
  1025.     }
  1026. #ifdef    WAIT_DEBUG
  1027.     p->waiting_for = (char *)0;
  1028. #endif    WAIT_DEBUG
  1029.     p->busy = (int)port_entry;
  1030.     if ((option & MACH_SEND_MSG) && !sent) {
  1031.         r = mach_msg(header, option,
  1032.              send_size, rcv_size, rcv_name,
  1033.              timeout, notify);
  1034.     } else {
  1035.         r = mach_msg(header, option &~ MACH_SEND_MSG,
  1036.              0, rcv_size, rcv_name,
  1037.              timeout, notify);
  1038.     }
  1039.     return r;
  1040. }
  1041.  
  1042. cproc_fork_prepare()
  1043. {
  1044.     register cproc_t p = cproc_self();
  1045.  
  1046.     vm_inherit(mach_task_self(),p->stack_base, p->stack_size, VM_INHERIT_COPY);
  1047.     spin_lock(&port_lock);
  1048.     spin_lock(&cproc_list_lock);
  1049. }
  1050.  
  1051. cproc_fork_parent()
  1052. {
  1053.     register cproc_t p = cproc_self();
  1054.  
  1055.     spin_unlock(&cproc_list_lock);
  1056.     spin_unlock(&port_lock);
  1057.     vm_inherit(mach_task_self(),p->stack_base, p->stack_size, VM_INHERIT_NONE);
  1058. }
  1059.  
  1060. cproc_fork_child()
  1061. {
  1062.     register cproc_t l,p = cproc_self();
  1063.     cproc_t m;
  1064.     register port_entry_t pe;
  1065.     port_entry_t pet;
  1066.     kern_return_t r;
  1067.  
  1068.  
  1069.     vm_inherit(mach_task_self(),p->stack_base, p->stack_size, VM_INHERIT_NONE);
  1070.     spin_lock_init(&n_kern_lock);
  1071.     cthread_kernel_threads=0;
  1072. #ifdef STATISTICS
  1073.     cthread_ready = 0;
  1074.     cthread_running = 1;
  1075.     cthread_waiting = 0;
  1076.     cthread_wired = 0;
  1077.     spin_lock_init(&wired_lock);
  1078.     cthread_wait_stacks = 0;
  1079.     cthread_waiters = 0;
  1080.     cthread_wakeup = 0;
  1081.     cthread_blocked = 0;
  1082.     cthread_rnone = 0;
  1083.     cthread_yields = 0;
  1084.     cthread_none = 0;
  1085.     cthread_switches = 0;
  1086.     cthread_no_mutex = 0;
  1087.     spin_lock_init(&mutex_count_lock);
  1088. #endif STATISTICS
  1089.  
  1090.     for(l=cproc_list;l!=NO_CPROC;l=m) {
  1091.     m=l->next;
  1092.     if (l!=p)
  1093.         free(l);
  1094.     }
  1095.  
  1096.     cproc_list = p;
  1097.     p->next = NO_CPROC;
  1098.     spin_lock_init(&cproc_list_lock);
  1099.     cprocs_started = FALSE;
  1100.     cthread_queue_init(&ready);
  1101.     ready_count = 0;
  1102.     spin_lock_init(&ready_lock);
  1103.  
  1104.     MACH_CALL(mach_port_allocate(mach_task_self(),
  1105.                  MACH_PORT_RIGHT_RECEIVE,
  1106.                  &wait_port), r);
  1107.     MACH_CALL(mach_port_insert_right(mach_task_self(),
  1108.                      wait_port, wait_port,
  1109.                      MACH_MSG_TYPE_MAKE_SEND), r);
  1110.     wakeup_msg.msgh_remote_port = wait_port;
  1111.     wait_count = 0;
  1112.     cthread_queue_init(&waiters);
  1113.     spin_lock_init(&waiters_lock);
  1114.     for(pe=port_list;pe!=PORT_ENTRY_NULL;pe=pet) {
  1115.     pet = pe->next;
  1116.     free(pe);
  1117.     }
  1118.     port_list = PORT_ENTRY_NULL;
  1119.     spin_lock_init(&port_lock);
  1120.  
  1121.     if (p->wired) cthread_wire();
  1122. }
  1123.