home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / mach / doc / unpublished / machuse.doc.Z / machuse.doc
Encoding:
Text File  |  1992-09-01  |  34.6 KB  |  880 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.  
  8.  
  9.  
  10.  
  11.  
  12.  
  13.  
  14.  
  15.                A PROGRAMMER'S GUIDE TO THE MACH USER ENVIRONMENT
  16.  
  17.                                 Linda R. Walmer
  18.                                Mary R. Thompson
  19.  
  20.                         Department of Computer Science
  21.                           Carnegie-Mellon University
  22.                              Pittsburgh, PA 15213
  23.                                   Version of:
  24.                                16 November 1989
  25.  
  26.  
  27.  
  28.  
  29.  
  30.  
  31.  
  32.  
  33.  
  34.  
  35.  
  36.  
  37.  
  38.                                    ABSTRACT
  39.  
  40. This  document is one of two tutorials designed to teach basic Mach programming
  41. skills.  This manual demonstrates the use of the C Threads  library  primitives
  42. in writing a multi-threaded program and the use of the Mach Interface Generator
  43. (MIG) to generate remote procedure calls for interprocess communication.
  44.  
  45. The reader should be familiar  with  the  basic  Mach  abstractions  of  ports,
  46. messages,  virtual memory, tasks and threads. The introduction to the companion
  47. document to this one, A Programmer's Guide to the Mach System  Calls,  explains
  48. these concepts.
  49.  
  50. Comments, suggestions and additions to this document are wlecome.
  51. 1. Introduction
  52.   This  document  is  one  of  two  tutorials  designed  to  teach  basic  Mach
  53. programming skills.  This manual demonstrates the use of the C Threads  library
  54. primitives  in  writing  a  multi-threaded  program  and  the  use  of the Mach
  55. Interface Generator (MIG) to generate remote procedure calls  for  interprocess
  56. communication.  It  also  includes  a final section on where at CMU to find the
  57. include files and libraries that comprise  the  Mach  environment  as  well  as
  58. procedures   for  obtaining  these  files  and  setting  up  the  correct  user
  59. environment.
  60.  
  61.   The reader should be familiar with the  basic  Mach  abstractions  of  ports,
  62. messages,  virtual  memory, tasks and threads before reading this document. The
  63. introduction to the other tutorial document, A Programmer's Guide to  the  Mach
  64. System Calls, explains these concepts.
  65.  
  66. 2. C Threads, A Single Master Thread Spawning Concurrent Slaves
  67.   The  C  threads  package  is  a  runtime  library  that provides a C language
  68. interface to a number of low level, Mach primitives for manipulating threads of
  69. control.    The  constructs  provided  are:    forking  and joining of threads,
  70. protection of critical regions with mutex  variables,  and  synchronization  by
  71. means  of  condition  variables.  For  a  complete description of the C threads
  72. package see the  C  Threads  manual  by  Cooper  and  Draves.    It  is  highly
  73. recommended  that  a  programmer  doing  multi-threaded  applications use the C
  74. threads routines rather than the Mach system calls. Not only is the  C  threads
  75. package designed to provide a more natural set of primitives for multi-threaded
  76. applications, but the thread system call interface has  been  recently  revised
  77. and  may  not  be  completely stable.  Also, the C threads package is carefully
  78. optimized to produce the most efficient use of the system calls.
  79.  
  80.   The program at the end of this section is an example of how  to  structure  a
  81. program with a single master thread which spawns a number of concurrent slaves.
  82. The master thread waits until all the slaves have finished to exit.   A  random
  83. number  generator is used to determine the "life" of the slave processes.  Once
  84. created the slave processes in this  example  simply  loop  calling  a  cthread
  85. function  making  the  processor available to other threads.  The random number
  86. generator determines the length of this loop.  In a more useful version of this
  87. program, each slave process would do something while looping.
  88.  
  89.   In  order  for  the  master  thread  to determine when all of the slaves have
  90. exited, a count variable is needed to keep  track  of  the  number  of  current
  91. threads.    This  count  is  incremented  by the master with each creation of a
  92. slave.  Each slave decrements the count when it exits.   Because  two  or  more
  93. threads may be trying to access the count at the same time, a mutex called lock
  94. is used to provide exclusive access to count.  If any thread  wants  to  access
  95. the  count  variable,  it  should  first lock the mutex.  Consequently when the
  96. mutex is locked, any thread wanting the count  variable  must  wait  until  the
  97. mutex is unlocked.
  98.  
  99.   Condition  variables are used to provide synchronization between threads, e.g
  100. one thread wishes to wait until another thread has  finished  doing  something.
  101. Every  condition  variable  is  associated with a mutex. The condition variable
  102. represents a boolean state of the shared data that the mutex protects.  In this
  103. example  after  all  of  the slave threads have been created, the master thread
  104. waits until the count variable is equal to zero.  A condition variable done  is
  105. used to represent the possibility that the count may equal zero.  Just before a
  106. slave thread exits, it signals the condition done since  it  may  be  the  last
  107. slave  executing.  The master thread loops waiting on the condition done.  Each
  108. time the master thread is signalled by a condition_signal call,  it  tests  the
  109. count for a value of zero.
  110.  
  111.   cthread_init  is  the  first  function  called  in the example program.  This
  112. function initializes the C threads implementation and must be called before any
  113. of  the  other cthread functions. If a program is loaded with the /usr/mach/lib
  114. version of crt0, this call is no longer necessary as it has already  been  done
  115. by  crt0  (or gcrt0 or moncrt0).  The count which represents the current number
  116. of slaves is set to zero.  mutex_alloc is called to allocate a  mutex  assigned
  117. to  the  variable lock.  condition_alloc is used to allocate a condition object
  118. assigned to the variable done.  The last initialization call is to  the  random
  119. number generator.
  120.  
  121.   After  initialization,  the  master thread loops creating the number of slave
  122. processes desired and incrementing count with each  creation.    mutex_lock  is
  123. called  at  the beginning of the loop.  This call results in either locking the
  124. variable lock, or blocking until lock is unlocked by some other  thread.    The
  125. return  of mutex_lock signals that the master can now change the variable count
  126. knowing that no other thread will be accessing this variable until  the  master
  127. unlocks  the mutex.  count is incremented, and a slave is created.  To create a
  128. slave, the master calls cthread_fork followed by cthread_detach.   cthread_fork
  129. creates  a  new  thread of control which executes concurrently with the master.
  130. cthread_fork takes as a parameter  a  function  which  the  new  thread  is  to
  131. execute.  Since the master does not intend to later rendez-vous with the slave,
  132. cthread_detach is called.  Once  the  master  has  incremented  the  count  and
  133. created  a  slave, mutex_unlock is called to give the other threads a chance to
  134. lock the mutex and consequently access the count variable.
  135.  
  136.   Having created the desired number of slaves, the master thread stops  looping
  137. and waits for all of the slave threads to finish execution.  The variable count
  138. signals the number of slave processes still executing.  mutex_lock is called so
  139. the  master  may  safely access the count variable.  Now the master thread must
  140. wait on the condition done by calling condition_wait.   condition_wait  unlocks
  141. the  mutex  and  suspends  the  master,  letting other threads change the count
  142. variable.  When condition_wait returns, the mutex is automatically locked.  The
  143. master  resumes  and  checks  the  count to see if it is in fact equal to zero.
  144. Since there is no gaurantee that the condition will be true when the master  is
  145. resumed,  the condition_wait is called in a loop ending when the count is zero.
  146. Before exiting the master  calls  mutex_unlock.    cthread_exit  is  called  to
  147. terminate the master thread.
  148.  
  149.   When  the  new slave was created via cthread_fork, it was given a function to
  150. execute and one parameter to pass to that function.  In our example, the  slave
  151. function  is given a random number as a parameter.  The slave loops this number
  152. of times calling a function cthread_yield, which yields the processor to  other
  153. threads.    When finished looping, the thread must decrement the count variable
  154. because it is about to exit.  In order to safely access the  count,  mutex_lock
  155. is  called.    Once  the  mutex lock is locked, the count is decremented.  Next
  156. condition_signal is called to indicate that the condition  represented  by  the
  157. condition variable done may be true.  The slave calls mutex_unlock and exits.
  158.  
  159.  
  160.  
  161. 2.1. Initializing the C Threads Package
  162.   This  initialization  function  must  be  called  before  any  other C Thread
  163. functions.  This call is now called automatically by crt0, but  multiple  calls
  164. to this routine are harmless.
  165.  
  166.     cthread_init();
  167.  
  168.  
  169.  
  170.  
  171. 2.2. Allocation of a Mutex Variable
  172.   mutex_alloc provides dynamic allocation of a mutex variable.
  173.  
  174.     mutex_t lock;      /* mutual exclusion for count */
  175.  
  176.     lock = mutex_alloc();
  177.  
  178.  
  179.  
  180.  
  181. 2.3. Locking a Mutex Variable
  182.   mutex_lock  attempts  to  lock  the  given  mutex  variable.  If the mutex is
  183. already locked this call blocks until  the  mutex  is  unlocked.    If  several
  184. threads  attempt to lock the same mutex concurrently, one will succeed, and the
  185. others will block until the mutex is unlocked.  A deadlock will result  from  a
  186. thread attempting to lock a mutex it has already locked.
  187.  
  188.     mutex_t lock;      /* mutual exclusion for count */
  189.  
  190.     mutex_lock(lock);
  191.  
  192.  
  193.  
  194.  
  195. 2.4. Unlocking a Mutex Variable
  196.   mutex_unlock unlocks the mutex giving other threads a chance to lock it.
  197.  
  198.     mutex_t lock;      /* mutual exclusion for count */
  199.  
  200.     mutex_unlock(lock);
  201.  
  202.  
  203.  
  204.  
  205. 2.5. Allocation of a Condition Variable
  206.   condition_alloc provides dynamic allocation of a condition variable.
  207.  
  208.     condition_t done;  /* signalled each time a slave finishes */
  209.  
  210.     done = condition_alloc();
  211.  
  212.  
  213.  
  214.  
  215. 2.6. Waiting on a Condition
  216.   This  function  unlocks  the  mutex  it  takes as a parameter, suspending the
  217. calling  thread  until  another  thread  calls  condition_signal  on  the  same
  218. condition variable.  The mutex is then locked and the thread resumes.  There is
  219. no guarantee that the condition will be true when the thread resumes, therefore
  220. condition_wait should always be used in the form below.
  221.  
  222.     mutex_t lock;
  223.     condition_t done;
  224.  
  225.     mutex_lock(lock);
  226.     ...
  227.     while (count != 0)
  228.             condition_wait(done, lock);
  229.     ...
  230.     mutex_unlock(lock);
  231.  
  232.  
  233.  
  234.  
  235. 2.7. Signalling a Condition
  236.   condition_signal  is  called  when  one  thread  wishes  to indicate that the
  237. condition represented by the condition variable  may  now  be  true.    If  any
  238. threads  are waiting via condition_wait, at least one of them will be awakened.
  239. If no threads are waiting, nothing happens.
  240.  
  241.     condition_t done;  /* signalled each time a slave finishes */
  242.  
  243.     condition_signal(done);
  244. 2.8. Forking a C Thread
  245.   This function takes two parameters: a function for the new thread to execute,
  246. and a parameter to this function.  cthread_fork creates a new thread of control
  247. in which the specified function is  executed  concurrently  with  the  caller's
  248. thread.    This is the sole means of creating new threads.  A parameter that is
  249. larger than a pointer  must  be  passed  by  reference.    Similarly,  multiple
  250. parameters  must  be  simulated  by passing a pointer to a structure containing
  251. several components.  The call to cthread_fork returns a thread identifier  that
  252. can  be  passed to cthread_join or cthread_detach.  Every thread must be either
  253. joined or detached exactly once.
  254.  
  255.     /*  slave is a function that expects an integer parameter  */
  256.     /*  see Detaching a C Thread for description of chtread_detach  */
  257.  
  258.     cthread_detach(cthread_fork(slave, random() % 1000));
  259.  
  260.  
  261.  
  262.  
  263. 2.9. Detaching a C Thread
  264.   cthread_detach is used to indicate that a thread will never  be  joined.    A
  265. thread  may  be  detached  at  any time after it is forked, as long as no other
  266. attempt at joining or detaching has been made.  In the example  below,  at  the
  267. time  the  thread  was  forked,  it was known that it would never be joined and
  268. therefore it was detached.
  269.  
  270.     /*  slave is a function that expects an integer parameter  */
  271.     /*  see Forking a C Thread for description of chtread_fork  */
  272.  
  273.     cthread_detach(cthread_fork(slave, random() % 1000));
  274.  
  275.  
  276.  
  277.  
  278. 2.10. Yielding the Processor to other Threads
  279.   This procedure is a hint to the scheduler, suggesting that this  would  be  a
  280. convenient  point  to  schedule another thread to run on the current processor.
  281. Calls to cthread_yield are unnecessary in  an  implementation  with  preemptive
  282. scheduling,  but  may  be  required  to  avoid  starvation in a coroutine based
  283. implementation.
  284.  
  285.     int i, n;
  286.  
  287.     /* n is set previously */
  288.  
  289.     for (i = 0; i < n; i += 1)
  290.             cthread_yield();
  291.  
  292.  
  293.  
  294.  
  295. 2.11. Exiting a C Thread
  296.   cthread_exit  causes  termination  of  the  calling  thread.    An   implicit
  297. cthread_exit  occurs  when  the  top  level  function of a thread returns.  The
  298. result parameter will be passed  to  the  thread  that  joins  the  caller,  or
  299. discarded if the caller is detached.
  300.  
  301.     cthread_exit(0);
  302. 2.12. Example V, masterslave.c
  303. /*
  304.  * This program is an example of a master thread spawning a number of
  305.  * concurrent slaves.  The master thread waits until all of the slaves have
  306.  * finished to exit.  Once created a slave process doesn't do much in this
  307.  * simple example except loop.  A count variable is used by the master and
  308.  * slave processes to keep track of the current number of slaves executing.
  309.  * A mutex is associated with this count variable, and a condition variable
  310.  * with the mutex.  This program is a simple demonstration of the use of
  311.  * mutex and condition variables.
  312.  */
  313.  
  314. #include <stdio.h>
  315. #include <cthreads.h>
  316.  
  317. int count;         /* number of slaves active */
  318. mutex_t lock;      /* mutual exclusion for count */
  319. condition_t done;  /* signalled each time a slave finishes */
  320.  
  321. extern long random();
  322.  
  323.  
  324.  
  325.  
  326. init()
  327. {
  328.     cthread_init();
  329.     count = 0;
  330.     lock = mutex_alloc();
  331.     done = condition_alloc();
  332.     srandom(time((int *) 0));  /* initialize random number generator */
  333. }
  334.  
  335.  
  336.  
  337.  
  338. /*
  339.  * Each slave just counts up to its argument, yielding the processor on
  340.  * each iteration.  When it is finished, it decrements the global count
  341.  * and signals that it is done.
  342.  */
  343. slave(n)
  344.     int n;
  345. {
  346.     int i;
  347.  
  348.     for (i = 0; i < n; i += 1)
  349.         cthread_yield();
  350.     mutex_lock(lock);
  351.     count -= 1;
  352.     printf("Slave finished %d cycles.\n", n);
  353.     condition_signal(done);
  354.     mutex_unlock(lock);
  355. }
  356.  
  357.  
  358.  
  359.  
  360. /*
  361.  * The master spawns a given number of slaves and then waits for them all to
  362.  * finish.
  363.  */
  364. master(nslaves)
  365.     int nslaves;
  366. {
  367.     int i;
  368.  
  369.     for (i = 1; i <= nslaves; i += 1) {
  370.         mutex_lock(lock);
  371.         /*
  372.          * Fork a slave and detach it,
  373.          * since the master never joins it individually.
  374.          */
  375.         count += 1;
  376.         cthread_detach(cthread_fork(slave, random() % 1000));
  377.         mutex_unlock(lock);
  378.     }
  379.     mutex_lock(lock);
  380.     while (count != 0)
  381.         condition_wait(done, lock);
  382.     mutex_unlock(lock);
  383.     printf("All %d slaves have finished.\n", nslaves);
  384.     cthread_exit(0);
  385. }
  386.  
  387.  
  388.  
  389.  
  390. main()
  391. {
  392.     init();
  393.     master((int) random() % 16);  /* create up to 15 slaves */
  394. }
  395. 3. MIG - The Mach Interface Generator
  396.   Much  multi-task communication takes the form of one or more tasks requesting
  397. services or responses from another task.  This in fact is a  description  of  a
  398. Mach server process.  Since the creation and reading of messages requires a lot
  399. of repetitious code, it should come as no great surpise that  Mach  provides  a
  400. compiler  to  produce a remote procedure call interface to IPC message passing.
  401. A complete description of MIG including an example of its use can be  found  in
  402. MIG - the Mach Interface Generator by Draves, Jones and Thompson.
  403.  
  404.   A  brief  example of the use of MIG follows. The problem that is to be solved
  405. is to write a simple server that will return  either  a  random  integer  or  a
  406. random string. The user interface to this server is to consist of two calls:
  407.  
  408.     ret_code = get_random(server_port,num)
  409.             port_t  server_port;
  410.             int     num;
  411.  
  412.     ret_code = get_secret(server_port,password)
  413.             port_t  server_port;
  414.             string25 password;
  415.  
  416.  
  417.  
  418. 3.1. MIG Definition file
  419.   The  subsystem  implementor must first write a MIG definition file to specify
  420. the details of the procdure arguments  and  the  messages  to  be  used.    MIG
  421. understands  different  kinds  of  routines and many obscure options in the way
  422. messages are to be formatted, sent and received.  But for this simple  case  it
  423. is enough to define the name of the server, the types of the arguments that are
  424. being used, and the routines that are desired.  The  following  MIG  definition
  425. file will suffice to do this:
  426. subsystem random 500;
  427.  
  428. type int        = MSG_TYPE_INTEGER_32;
  429. type port_t     = MSG_TYPE_PORT;
  430. type boolean_t  = MSG_TYPE_INTEGER_32;
  431. type string25   = (MSG_TYPE_STRING_C,8*25);
  432.  
  433. import "random_types.h";
  434.  
  435. routine get_random(
  436.                 requestport     server_port     : port_t;
  437.                 out             num             : int);
  438.  
  439. routine get_secret(
  440.                 requestport     server_port     : port_t;
  441.                 inout           password        : string25);
  442.  
  443.   The first line of the definition file states that the name of the subsytem is
  444. to be random and the messages that are created will start with the  message  id
  445. of  500.  This  interface  will  send  four  different  messages:  one  for the
  446. get_random function which will have a msg_id of  500;  one  for  the  reply  to
  447. get_random  which  will  have  a msg_id of 600; one for the function get_secret
  448. which will have a msg_id of 501; and one for its reply which will have a msg_id
  449. of  601.  The  msg_id  is  used  by the server to identify which message it has
  450. received.
  451.  
  452.   The syntax of the type declaration is to define the C  type  name  first  and
  453. then  say  that  it is equal to an IPC type name.  The set of defined IPC types
  454. can be found in the MIG document or in the file <sys/message.h>. For string and
  455. unstructured types the number of bits in the type must follow the name.
  456.  
  457.   The  import  statement  gives the name of a header file to be included in the
  458. generated code. This header file must define any non-standard C types  used  by
  459. the interface. In this case it consists of the following definition:
  460.  
  461.     typedef char    string25[25];
  462.  
  463.   The  routine  declarations  specify the name of the routine and the order and
  464. types of the arguments. The first argument is the port  to  which  the  message
  465. will  be  sent.  The specification in, out or inout may precede the name of any
  466. other parameter and specify in what direction the argument is to be passed. Any
  467. unspecified parameter, except the first one, is assumed to be an in parameter.
  468.  
  469.   MIG  generates  three  C  files  from  the definition file. The file random.h
  470. defines the functions to be called by a client of  the  server  and  should  be
  471. #included  into  code  that  calls those functions. randomUser.c is the code to
  472. create and send the messages to the client and then wait to receive  the  reply
  473. message.  When  the  reply message is received, any out or inout parameters are
  474. taken out of the message and returned as function  parameters  to  the  caller.
  475. This  file  should be linked with the user of the server. randomServer.c is the
  476. server side of the message interface. It unpacks the request message,  calls  a
  477. function  provided  by  the  server implementor to execute the request and then
  478. creates the reply message.  The server implementor must write two more modules.
  479. One  is  the  main  program  of  the  server  which  receives  a message, calls
  480. randomServer to process the request, and then  sends  the  reply  message.  The
  481. other module must contain the functions that execute the requests.
  482.  
  483.  
  484.  
  485. 3.2. Server main program
  486.   In  this example the server waits on the special port name PORT_DEFAULT which
  487. indicates the set of all unrestricted ports belonging to  this  task.  In  this
  488. case  the  set consists of ServerPort and notify_port().  The thread reply port
  489. is not included in this set because it it  still  restricted.  When  ports  are
  490. created  they  are  restricted  by  default.  This  means they can only be used
  491. explicitly to receive messages on.
  492.  
  493.   This server may get some EMERGENCY_MSGs from the kernel on its  notify_port()
  494. which  it wishes to ignore. A more complicated server might need to take action
  495. on some of the kernel's emergency messages.
  496.  
  497.   Note that the function mach_errormsg is a library routine  that  returns  the
  498. string  associated  with  a  Mach  error  code.  This  function  is  defined in
  499. mach_error.h and is included in libmach.a.
  500.  
  501.   The following code shows a typical main loop for a MIG server.
  502. /***************************************************
  503.  *   Main program for random server
  504.  **************************************************/
  505. #include <stdio.h>
  506. #include <mach.h>
  507. #include <mach_error.h>
  508. #include <mig_errors.h>
  509. #include <mach/message.h>
  510. #include <mach/notify.h>
  511. #include <servers/netname.h>
  512.  
  513. extern boolean_t        random_server();
  514.  
  515. /*********************************************************
  516.  * procedure random_run:
  517.  *   Waits for messages to server,
  518.  *   handles them, and replies to sender.
  519.  *********************************************************/
  520. void random_run ()
  521. {
  522.    typedef int space[1024]; /* Maximum message size */
  523.  
  524.    typedef struct DumMsg
  525.         {
  526.            msg_header_t         head;
  527.            msg_type_t           retcodetype;
  528.            kern_return_t        return_code;
  529.            space                body;
  530.         } DumMsg;
  531.  
  532.    kern_return_t        retcode;
  533.    msg_return_t         msgcode;
  534.    boolean_t            ok;
  535.    DumMsg               *pInMsg, *pRepMsg;
  536.  
  537.    pInMsg = (DumMsg *)malloc(sizeof(DumMsg));
  538.    pRepMsg = (DumMsg *)malloc(sizeof(DumMsg));
  539.  
  540.    while (TRUE)
  541.    {
  542.        pInMsg->head.msg_size = sizeof(DumMsg); /* bytes */
  543.        pInMsg->head.msg_local_port = PORT_DEFAULT;
  544.  
  545.         /* wait to receive request from client */
  546.        msgcode = msg_receive(&pInMsg->head,MSG_OPTION_NONE,0);
  547.        if (msgcode != RCV_SUCCESS)
  548.            printf("error %s in Receive, message will be ignored.\n",
  549.                 mach_error_string((kern_return_t)msgcode));
  550.        else
  551.        {   if (pInMsg->head.msg_type == MSG_TYPE_EMERGENCY)
  552.            {
  553.               if (pInMsg->head.msg_id == NOTIFY_PORT_DELETED)
  554.                   {  /* probably the death of a client's reply */}
  555.               else
  556.                 printf("Unexpected emergency message received: id is %d\n",
  557.                         pInMsg->head.msg_id);
  558.            }
  559.           else  /* normal message */
  560.           {
  561.                 /* call server interface module */
  562.               ok = random_server((msg_header_t *)pInMsg,
  563.                                    (msg_header_t *)pRepMsg);
  564.  
  565.               if ((pRepMsg->return_code != MIG_NO_REPLY) &&
  566.                   (pInMsg->head.msg_remote_port != PORT_NULL))
  567.               {
  568.                   /* sending reply message to client */
  569.                 pRepMsg->head.msg_local_port = pInMsg->head.msg_local_port;
  570.                 pRepMsg->head.msg_remote_port = pInMsg->head.msg_remote_port;
  571.                 msgcode = msg_send(&pRepMsg->head,MSG_OPTION_NONE,0);
  572.                 if ((msgcode != SEND_SUCCESS) &&
  573.                     (msgcode != SEND_INVALID_PORT))
  574.                                /* Probably remote process death */
  575.                     printf("error %s at Send.\n",
  576.                                 mach_error_string((kern_return_t)msgcode));
  577.               }
  578.           } /* normal message */
  579.        } /* of message handling */
  580.      }  /* of main loop */
  581.  
  582. }
  583.  
  584.  
  585. main()
  586. {
  587.    port_t       ServerPort;
  588.    kern_return_t retcode;
  589.  
  590.         /* add notification port to default port set */
  591.    retcode = port_unrestrict(task_self(),task_notify());
  592.         /* allocate a service port */
  593.    retcode = port_allocate(task_self(), &ServerPort);
  594.    if (retcode == KERN_SUCCESS)
  595.    {    /* add service port to default port set */
  596.       (void) port_unrestrict(task_self(),ServerPort);
  597.         /* check it in so users can find it */
  598.       retcode  = netname_check_in(name_server_port,"RandomServerPort",
  599.                 PORT_NULL,ServerPort);
  600.    }
  601.    if (retcode != KERN_SUCCESS)
  602.         printf("netname_check_in of RandomServerPort failed with code %s\n",
  603.                         mach_error_string(retcode));
  604.    random_run ();
  605.    printf("(* !!!!! Random server exited - give it up !!!!! *)\n");
  606.    exit(2);
  607. }
  608.  
  609.  
  610.  
  611. 3.3. Server message dispatch code
  612.   The file randomServer.c contains the server side code that was  generated  by
  613. MIG.  It  exports the routine random_server that is called by the main program.
  614. It checks the message id to determine  what  message  was  received,  and  then
  615. unpacks  the  arguments  and  calls  the  appropriate  server  procedure.   The
  616. following fragment shows the control logic of the dispatch routine.
  617. boolean_t random_server(InHeadP, OutHeadP)
  618.         msg_header_t *InHeadP, *OutHeadP;
  619. {
  620.         register msg_header_t *InP = InHeadP;
  621.         register death_pill_t *OutP = (death_pill_t *) OutHeadP;
  622.  
  623.         /* set up legal return message with failure code */
  624.  
  625.         OutP->Head.msg_local_port = InP->msg_local_port;
  626.         OutP->Head.msg_remote_port = InP->msg_remote_port;
  627.         OutP->Head.msg_id = InP->msg_id + 100;
  628.         OutP->Head.msg_type = InP->msg_type;
  629.         OutP->Head.msg_size = sizeof *OutP;
  630.  
  631.         OutP->RetCodeType.msg_type_name = MSG_TYPE_INTEGER_32;
  632.         OutP->RetCodeType.msg_type_size = 32;
  633.         OutP->RetCodeType.msg_type_number = 1;
  634.         OutP->RetCodeType.msg_type_inline = TRUE;
  635.         OutP->RetCodeType.msg_type_longform = FALSE;
  636.         OutP->RetCodeType.msg_type_deallocate = FALSE;
  637.  
  638.         OutP->RetCode = MIG_BAD_ID;
  639.  
  640.         if ((InP->msg_id > 501) || (InP->msg_id < 500))
  641.                 return FALSE;
  642.          else {
  643.                 static novalue (*(routines[]))() = {
  644.                         _Xget_random,
  645.                         _Xget_secret,
  646.                 };
  647.  
  648.                 if (routines[InP->msg_id - 500])
  649.                         (routines[InP->msg_id - 500]) (InP, &OutP->Head);
  650.                  else
  651.                         return FALSE;
  652.         }
  653.         return TRUE;
  654. }
  655.   The two  internal  routines  that  do  most  of  the  message  unpacking  are
  656. _Xget_random  and _Xget_secret. These routines respectively call get_random and
  657. get_secret with the appropriate parameters. When those routines return, a reply
  658. message is created in the buffer that OutHeadP points to.
  659.  
  660.   The following is the code for _Xget_random;
  661. /* Routine get_random */
  662. mig_internal novalue _Xget_random(InHeadP, OutHeadP)
  663.         msg_header_t *InHeadP, *OutHeadP;
  664. {
  665.         typedef struct {
  666.                 msg_header_t Head;
  667.         } Request;
  668.  
  669.         typedef struct {
  670.                 msg_header_t Head;
  671.                 msg_type_t RetCodeType;
  672.                 kern_return_t RetCode;
  673.                 msg_type_t numType;
  674.                 int num;
  675.         } Reply;
  676.  
  677.         register Request *InP = (Request *) InHeadP;
  678.         register Reply *OutP = (Reply *) OutHeadP;
  679.         extern kern_return_t get_random();
  680.  
  681. #if     UseStaticMsgType
  682.         static msg_type_t numType = {
  683.                 /* msg_type_name = */           MSG_TYPE_INTEGER_32,
  684.                 /* msg_type_size = */           32,
  685.                 /* msg_type_number = */         1,
  686.                 /* msg_type_inline = */         TRUE,
  687.                 /* msg_type_longform = */       FALSE,
  688.                 /* msg_type_deallocate = */     FALSE,
  689.         };
  690. #endif  UseStaticMsgType
  691.  
  692. #if     TypeCheck
  693.         if (InP->Head.msg_size != sizeof(Request))
  694.                 { OutP->RetCode = MIG_BAD_ARGUMENTS; return; }
  695. #endif  TypeCheck
  696.  
  697.         OutP->RetCode = get_random(InP->Head.msg_local_port,
  698.                                         &OutP->num);
  699.         if (OutP->RetCode != KERN_SUCCESS)
  700.                 return;
  701.         OutP->Head.msg_simple = TRUE;
  702.         OutP->Head.msg_size = sizeof(Reply);
  703.  
  704. #if     UseStaticMsgType
  705.         OutP->numType = numType;
  706. #else   UseStaticMsgType
  707.         OutP->numType.msg_type_name = MSG_TYPE_INTEGER_32;
  708.         OutP->numType.msg_type_size = 32;
  709.         OutP->numType.msg_type_number = 1;
  710.         OutP->numType.msg_type_inline = TRUE;
  711.         OutP->numType.msg_type_longform = FALSE;
  712.         OutP->numType.msg_type_deallocate = FALSE;
  713. #endif  UseStaticMsgType
  714.  
  715. }
  716.  
  717.  
  718.  
  719. 3.4. Server procedures
  720.   Finally  the  subsystem  implementor must write the proceedures that actually
  721. perform the requested operations. For this server the following code will do.
  722. /* procedues of random_server */
  723. #include <mach.h>
  724. #include "random_types.h"
  725.  
  726. long    random();
  727.  
  728.  
  729. kern_return_t get_random(serv_port,num)
  730. /* get_random returns a random number between 0 and 2**32 - 1 */
  731.     port_t      serv_port;
  732.     int         *num;
  733. {
  734.         *num = (int) random();
  735.         return(KERN_SUCCESS);
  736. }
  737.  
  738. kern_return_t get_secret(serv_port,password)
  739.     port_t      serv_port;
  740.     string25    password;
  741. /* get_secret returns a random printable ascii string 25 chars long */
  742. {
  743.     int         i,j;
  744.     int         range = (int)'~' - (int)' ';
  745.  
  746.         for (i=0;i<25; i++)
  747.         {
  748.                 j = ((int)random()%range) + (int)' ';
  749.                 password[i] = (char)j;
  750.         }
  751.         password[25] = '\0';
  752.         return(KERN_SUCCESS);
  753. }
  754.  
  755.  
  756.  
  757. 3.5. User side
  758.   The following code shows an example of how to use the server.
  759. /* This is an example of a program using the random server */
  760. #include <mach.h>
  761. #include <servers/netname.h>
  762. #include "random.h"
  763. #include <mach_error.h>
  764. #include <stdio.h>
  765.  
  766.  
  767. main()
  768. {
  769.     int         number;
  770.     string25    password;
  771.     port_t      serv_port;
  772.     kern_return_t retcode;
  773.  
  774.         printf("Looking up port for random server\n");
  775.         retcode = netname_look_up(name_server_port,"","RandomServerPort",
  776.                         &serv_port);
  777.         if (retcode != KERN_SUCCESS)
  778.         {    mach_error("error in looking up port for random server",retcode);
  779.              printf("Random server may not be running\n");
  780.              exit();
  781.         }
  782.         printf("Calling get_random\n");
  783.         retcode = get_random(serv_port,&number);
  784.         if (retcode == KERN_SUCCESS)
  785.                 printf("Result from get_random is %d\n",number);
  786.         else mach_error("Error from get_random is",retcode);
  787.         printf("Calling get_secret\n");
  788.         retcode = get_secret(serv_port,password);
  789.         if (retcode == KERN_SUCCESS)
  790.                 printf("Result from get_secret is %s\n",password);
  791.         else mach_error("Error from get_secret is",retcode);
  792. }
  793.  
  794. 4. General Mach information
  795.   Structure of the Mach tree
  796. At CMU the Mach tree may either be used from /afs or  may  be  supped  to  your
  797. workstation.  If  you  wish to have a local copy supped to your workstation use
  798. the command.
  799.     modmisc + cs.misc.mach [-release beta|default] [-config minimal]
  800.  
  801.   Directories of interest are:
  802.         /usr/mach/man : Mach manual pages
  803.         /usr/mach/bin : Mach system programs such as MiG
  804.         /usr/mach/doc : on line copies of Mach documents
  805.         /usr/mach/etc : Mach servers such as the environment manager
  806.         /usr/mach/include : Mach include files
  807.         /usr/mach/lib : Mach libraries such as libmach.a and libthreads.a
  808.  
  809.  
  810.   Where to find examples and manuals
  811. The Mach sources are kept in /afs/cs/project/mach/src.  There  is  subdirectory
  812. examples  in  /usr/mach/doc that contains the example programs that are used in
  813. the two tutorial documents.
  814.  
  815.   Hard copies of the Mach manuals and documents can be found in WeH 7114.    To
  816. print  a copy yourself from the /usr/mach/doc/ directory, use the .ps files for
  817. the laser writers. See the lpr UNIX manual entry on how to print .ps files.
  818.  
  819.   Setting up search paths
  820. To compile or load a Mach program your paths must be set to look in the  proper
  821. place   for   Mach   include   files   and   libraries.      By  typing  source
  822. /usr/mach/lib/machpaths the appropriate Mach directories will be added to  your
  823. existing paths.
  824.  
  825.   Mach libraries
  826. The  Mach  library,  -lmach,  must be loaded with most of the examples given in
  827. this tutorial.  Any program using  Mach  kernel  interface  functions  must  be
  828. loaded with libmach and the /usr/cs/lib version of crt0.  For example, assuming
  829. that /usr/mach/lib and /usr/cs/lib are on your LPATH:
  830.         cp /../wb1/usr/mach/src/examples/simp_ipc.c .
  831.         cc -o simp_ipc simp_ipc.c -lmach
  832.  
  833.  
  834.   Two threads libraries exist: libthreads.a  and  libco_threads.a.    The  'co'
  835. (co-routine version of this library does not actually use Mach threads; it uses
  836. coroutines.  This library does not provide  real  parallelism,  a  single  UNIX
  837. process  is  running.    The  other  threads  library,  libthreads.a, uses Mach
  838. threads.
  839.  
  840.   Mach include files
  841. When writing a program that uses Mach facilities some of the following  include
  842. files may be needed:
  843.         mach.h : needed for all Mach programs
  844.         mach/message.h : if any message structures are used
  845.         mach_error.h : if mach_error() is used
  846.         servers/env_mgr.h : if environment manager server is used
  847.         servers/netname.h : if netname server is used
  848.         cthreads.h : if C threads package is used
  849.  
  850.  
  851.   Mach information/questions
  852. A  Mach  bboard  exists  to  keep  the  user  community  up to date on new Mach
  853. releases.  This bboard is called 'mach'.  See UNIX bboard manual entry.
  854.  
  855.   If you have questions relating to the use of  any  Mach  facilities,  or  any
  856. comments  on  this  tutorial,  send mail to machlib.  Comments on this tutorial
  857. will be greatly appreciated.
  858.                                Table of Contents
  859. 1. Introduction                                                               1
  860. 2. C Threads, A Single Master Thread Spawning Concurrent Slaves               1
  861.      2.1. Initializing the C Threads Package                                  1
  862.      2.2. Allocation of a Mutex Variable                                      1
  863.      2.3. Locking a Mutex Variable                                            1
  864.      2.4. Unlocking a Mutex Variable                                          1
  865.      2.5. Allocation of a Condition Variable                                  1
  866.      2.6. Waiting on a Condition                                              1
  867.      2.7. Signalling a Condition                                              1
  868.      2.8. Forking a C Thread                                                  2
  869.      2.9. Detaching a C Thread                                                2
  870.      2.10. Yielding the Processor to other Threads                            2
  871.      2.11. Exiting a C Thread                                                 2
  872.      2.12. Example V, masterslave.c                                           3
  873. 3. MIG - The Mach Interface Generator                                         4
  874.      3.1. MIG Definition file                                                 4
  875.      3.2. Server main program                                                 4
  876.      3.3. Server message dispatch code                                        5
  877.      3.4. Server procedures                                                   6
  878.      3.5. User side                                                           6
  879. 4. General Mach information                                                   6
  880.