home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / mach / doc / techreports / threads.doc.Z / threads.doc
Encoding:
Text File  |  1992-09-01  |  34.5 KB  |  933 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.  
  8.  
  9.  
  10.  
  11.  
  12.                                    C THREADS
  13.  
  14.                                 Eric C. Cooper
  15.                                Richard P. Draves
  16.  
  17.                         Department of Computer Science
  18.                           Carnegie Mellon University
  19.                         Pittsburgh, Pennsylvania 15213
  20.  
  21.                           Draft of 11 September 1990
  22.  
  23.  
  24.  
  25.  
  26.  
  27.  
  28.  
  29.  
  30.  
  31.  
  32.  
  33.  
  34.  
  35.  
  36.  
  37.                                    ABSTRACT
  38.  
  39. The C Threads package allows parallel programming in C under the MACH operating
  40. system.  The package provides multiple  threads  of  control  for  parallelism,
  41. shared  variables,  mutual  exclusion  for  critical  sections,  and  condition
  42. variables for synchronization of threads.
  43.  
  44.  
  45.  
  46.  
  47.  
  48.  
  49.  
  50.  
  51.  
  52. This research was sponsored by the Defense Advanced  Research  Projects  Agency
  53. (DoD),  ARPA  order  4864,  monitored  by  the  Space and Naval Warfare Systems
  54. Command under contract N00039-84-C-0467.
  55.  
  56. The views and conclusions contained in this document are those of  the  authors
  57. and  should  not  be  interpreted  as  representing  official  policies, either
  58. expressed or implied, of the Defense Advanced Research Projects  Agency  or  of
  59. the U.S. Government.
  60. 1. Introduction
  61. MACH   provides   a  set  of  low-level,  language-independent  primitives  for
  62. manipulating threads of control [3].  The  C  Threads  package  is  a  run-time
  63. library  that  provides  a  C  language interface to these facilities [1].  The
  64. constructs provided are similar to those found in Mesa [4]  and  Modula-2+ [5]:
  65. forking  and  joining  of  threads,  protection  of critical regions with mutex
  66. variables, and synchronization by means of condition variables.
  67.  
  68. 2. Naming Conventions
  69. An attempt has  been  made  to  use  a  consistent  style  of  naming  for  the
  70. abstractions  implemented  by  the  C  Threads package.  All types, macros, and
  71. functions implementing a given abstract data type are prefixed  with  the  type
  72. name and an underscore.  The name of the type itself is suffixed with _t and is
  73. defined via a C typedef.  Documentation of the form
  74.  
  75.                     typedef struct mutex {...} *mutex_t;
  76.  
  77. indicates that the mutex_t type is defined as a  pointer  to  a  referent  type
  78. struct mutex which may itself be useful to the programmer.  (In cases where the
  79. referent type should be considered opaque, documentation such as
  80.  
  81.                     typedef ... cthread_t;
  82.  
  83. is used instead.)
  84.  
  85. Continuing the example of the mutex_t type,  the  functions  mutex_alloc()  and
  86. mutex_free()   provide  dynamic  storage  allocation  and  deallocation.    The
  87. functions   mutex_init()   and   mutex_clear()   provide   initialization   and
  88. finalization  of  the  referent  type.    These  functions  are  useful  if the
  89. programmer wishes to include the referent type itself (rather than  a  pointer)
  90. in  a larger structure, for more efficient storage allocation.  They should not
  91. be  called  on  objects  that  are  dynamically  allocated  via  mutex_alloc().
  92. Type-specific  functions  such  as  mutex_lock()  and  mutex_unlock()  are also
  93. defined, of course.
  94.  
  95. 3. Initializing the C Threads Package
  96.  
  97.  
  98.  
  99. 3.1. cthreads.h
  100.  
  101.     #include <cthreads.h>
  102.  
  103. The header file cthreads.h defines the C threads interface.  All programs using
  104. C threads must include this file.
  105.  
  106.  
  107.  
  108. 3.2. cthread_init
  109.  
  110.     void
  111.     cthread_init()
  112.  
  113. The  cthread_init()  procedure  initializes  the  C  threads implementation.  A
  114. program using C threads must explicitly  call  cthread_init()  (typically  from
  115. main())  before  using  any  of  the other functions described below.  Multiple
  116. calls to cthread_init() are harmless.
  117.  
  118. 4. Threads of Control
  119.  
  120.  
  121.  
  122. 4.1. Creation
  123. When a C program starts, it contains  a  single  thread  of  control,  the  one
  124. executing  main().    The  thread  of  control is an active entity, moving from
  125. statement to statement, calling and returning from procedures.  New threads are
  126. created by fork operations.
  127.  
  128. Forking  a new thread of control is similar to calling a procedure, except that
  129. the caller does not wait for the procedure to  return.    Instead,  the  caller
  130. continues  to  execute  in  parallel with the execution of the procedure in the
  131. newly forked thread.  At some later time, the caller may rendez-vous  with  the
  132. thread  and  retrieve  its result (if any) by means of a join operation, or the
  133. caller may detach the newly created thread to assert that no thread  will  ever
  134. be interested in joining it.
  135.  
  136.  
  137.  
  138. 4.2. Termination
  139. A  thread  t  terminates  when  it  returns from the top-level procedure it was
  140. executing.[Currently, this is not true of the initial thread executing  main().
  141. Instead, an implicit call to exit() occurs when main() returns, terminating the
  142. entire program.    If  the  programmer  wishes  detached  threads  to  continue
  143. executing,  the  final statement of main() should be a call to cthread_exit().]
  144. If t has not been detached, it remains in limbo  until  another  thread  either
  145. joins it or detaches it; if t has been detached, no rendez-vous is necessary.
  146.  
  147.  
  148.  
  149. 4.3. cthread_fork
  150.  
  151.     typedef ... any_t;
  152.     typedef ... cthread_t;
  153.  
  154. The  any_t  type  represents a pointer to any C type.  The cthread_t type is an
  155. integer-size ``handle'' that uniquely identifies a thread of control.    Values
  156. of type cthread_t will be referred to as thread identifiers.
  157.  
  158.     cthread_t
  159.     cthread_fork(func, arg)
  160.             any_t (*func)();
  161.             any_t arg;
  162.  
  163. The cthread_fork() procedure creates a new thread of control in which func(arg)
  164. is executed concurrently with the caller's thread.  This is the sole  means  of
  165. creating  new  threads.    Arguments  larger  than  a pointer must be passed by
  166. reference.  Similarly, multiple  arguments  must  be  simulated  by  passing  a
  167. pointer   to   a   structure  containing  several  components.    The  call  to
  168. cthread_fork() returns a thread identifier that can be passed to cthread_join()
  169. or  cthread_detach()  (see  below).    Every  thread  must  be either joined or
  170. detached exactly once.
  171.  
  172.  
  173.  
  174. 4.4. cthread_exit
  175.  
  176.     void
  177.     cthread_exit(result)
  178.             any_t result;
  179.  
  180. This  procedure  causes  termination  of  the  calling  thread.    An  implicit
  181. cthread_exit()  occurs  when the top-level function of a thread returns, but it
  182. may also be called explicitly.  The result will be passed to  the  thread  that
  183. joins the caller, or discarded if the caller is detached.
  184.  
  185.  
  186.  
  187. 4.5. cthread_join
  188.  
  189.     any_t
  190.     cthread_join(t)
  191.             cthread_t t;
  192.  
  193. This  function  suspends the caller until the specified thread t terminates via
  194. cthread_exit().  (It follows that attempting to  join  one's  own  thread  will
  195. result  in  deadlock.)   The caller receives either the result of t's top-level
  196. function or the argument with which t explicitly called cthread_exit().
  197.  
  198.  
  199.  
  200. 4.6. cthread_detach
  201.  
  202.     void
  203.     cthread_detach(t)
  204.             cthread_t t;
  205.  
  206. The detach operation is used to indicate that the given thread  will  never  be
  207. joined.    This  is usually known at the time the thread is forked, so the most
  208. efficient usage is the following:
  209.  
  210.             cthread_detach(cthread_fork(procedure, argument));
  211.  
  212. A thread may, however, be detached at any time after it is forked, as  long  as
  213. no other attempt is made to join it or detach it.
  214.  
  215.  
  216.  
  217. 4.7. cthread_yield
  218.  
  219.     void
  220.     cthread_yield()
  221.  
  222. This  procedure  is  a  hint  to the scheduler, suggesting that this would be a
  223. convenient point to schedule another thread to run on  the  current  processor.
  224. Calls  to  cthread_yield() are unnecessary in an implementation with preemptive
  225. scheduling, but may be  required  to  avoid  starvation  in  a  coroutine-based
  226. implementation.
  227.  
  228.  
  229.  
  230. 4.8. cthread_self
  231.  
  232.     cthread_t
  233.     cthread_self()
  234.  
  235. This  function  returns  the  caller's own thread identifier, which is the same
  236. value that was returned by cthread_fork() to the creator of the  thread.    The
  237. thread  identifier  uniquely  identifies the thread, and hence may be used as a
  238. key in data structures that associate user data with individual threads.  Since
  239. thread  identifiers  may  be  reused  by  the  underlying  implementation,  the
  240. programmer should be careful to clean up such associations when threads exit.
  241.  
  242.  
  243.  
  244. 4.9. cthread_set_data, cthread_data
  245.  
  246.     void
  247.     cthread_set_data(t, data)
  248.             cthread_t t;
  249.             any_t data;
  250.  
  251.     any_t
  252.     cthread_data(t)
  253.             cthread_t t;
  254.  
  255. These functions allow the user to  associate  arbitrary  data  with  a  thread,
  256. providing  a  simple  form thread-specific ``global'' variable.  More elaborate
  257. mechanisms, such as per-thread property lists or hash tables, can then be built
  258. with these primitives.
  259. 5. Synchronization
  260.  
  261.     typedef struct mutex {...} *mutex_t;
  262.  
  263.     typedef struct condition {...} *condition_t;
  264.  
  265. This  section describes mutual exclusion and synchronization primitives, called
  266. mutexes and condition variables.  In general,  these  primitives  are  used  to
  267. constrain  the  possible  interleavings  of  threads'  execution streams.  They
  268. separate the two most common uses of Dijkstra's P()  and  V()  operations  into
  269. distinct  facilities.   This approach basically implements monitors [2, 4], but
  270. without the syntactic sugar.
  271.  
  272. Mutually exclusive access to mutable data is necessary to prevent corruption of
  273. data.    As  simple  example,  consider  concurrent attempts to update a simple
  274. counter.  If  two  threads  fetch  the  current  value  into  a  (thread-local)
  275. register,  increment,  and write the value back in some order, the counter will
  276. only be incremented once, losing one thread's operation.  A mutex  solves  this
  277. problem by making the fetch-increment-deposit action atomic.  Before fetching a
  278. counter, a thread locks the associated mutex.  After depositing  a  new  value,
  279. the  thread unlocks the mutex.  If any other thread tries to use the counter in
  280. the meantime, it will block when it tries to lock the mutex.  If more than  one
  281. thread  tries  to  lock  the  mutex  at  the  same  time, the C threads package
  282. guarantees that only one will succeed; the rest will block.
  283.  
  284. Condition variables are used when one thread wants to wait until another thread
  285. has  finished doing something.  Every condition variable should be protected by
  286. a mutex.  Conceptually, the condition is a boolean function of the shared  data
  287. that  the  mutex protects.  Commonly, a thread locks the mutex and inspects the
  288. shared data.  If it doesn't like what it finds,  it  waits  using  a  condition
  289. variable.    This  operation  also temporarily unlocks the mutex, to give other
  290. threads a chance to get in and modify the shared data.  Eventually, one of them
  291. should  signal  the  condition  (which  wakes  up the blocked thread) before it
  292. unlocks the mutex.  At that point, the original thread will regain its lock and
  293. can  look  at  the shared data to see if things have improved.  It can't assume
  294. that it will like what it sees, because some other thread may have  slipped  in
  295. and mucked with the data after the the condition was signaled.
  296.  
  297. One  must take special care with data structures that are dynamically allocated
  298. and deallocated.  In particular, if the mutex that is controlling access  to  a
  299. dynamically  allocated  record  is part of the record, one must be sure that no
  300. thread is waiting for the mutex before freeing the record.
  301.  
  302. Attempting to lock a mutex that one already holds is another common error.  The
  303. offending  thread will block waiting for itself.  This can happen when a thread
  304. is traversing a complicated data structure, locking as it goes, and reaches the
  305. same  data  by  different  paths.  Another instance of this is when a thread is
  306. locking elements in an array, say to swap them, and it doesn't  check  for  the
  307. special case that the elements are the same.
  308.  
  309. In general, one must be very careful to avoid deadlock.  Deadlock is defined as
  310. the condition in which one or more threads are permanently blocked waiting  for
  311. each  other.   The above scenarios are a special case of deadlock.  The easiest
  312. way to avoid deadlock with mutexes  is  to  impose  a  total  ordering  on  the
  313. mutexes, and then ensure that threads only lock mutexes in increasing order.
  314.  
  315. One  important  issue the programmer must decide is what kind of granularity to
  316. use in protecting shared data with mutexes.  The two extremes are to  have  one
  317. mutex  protecting  all  shared  memory, and to have one mutex for every byte of
  318. shared memory.  Finer granularity normally increases the possible  parallelism,
  319. because  less  data  is locked at any one time.  However, it also increases the
  320. overhead lost to locking and unlocking mutexes and increases the possibility of
  321. deadlock.
  322.  
  323.  
  324.  
  325. 5.1. mutex_lock
  326.  
  327.     void
  328.     mutex_lock(m)
  329.             mutex_t m;
  330.  
  331. The  mutex_lock()  procedure  attempts  to lock the mutex m and blocks until it
  332. succeeds.  If several threads attempt to lock the same mutex concurrently,  one
  333. will  succeed,  and  the  others will block until m is unlocked.  The case of a
  334. thread attempting to lock  a  mutex  it  has  already  locked  is  not  treated
  335. specially; deadlock will result.
  336.  
  337.  
  338.  
  339. 5.2. mutex_try_lock
  340.  
  341.     int
  342.     mutex_try_lock(m)
  343.             mutex_t m;
  344.  
  345. The  mutex_try_lock() function attempts to lock the mutex m, like mutex_lock(),
  346. and  returns  TRUE  if  it  succeeds.    If  m  is  already  locked,   however,
  347. mutex_try_lock()  immediately returns FALSE rather than blocking.  For example,
  348. a busy-waiting version of the mutex_lock() procedure could be written in  terms
  349. of mutex_try_lock() as follows:
  350.  
  351.             void
  352.             mutex_lock(m)
  353.                     mutex_t m;
  354.             {
  355.                     for (;;)
  356.                             if (mutex_try_lock(m))
  357.                                     return;
  358.             }
  359.  
  360.  
  361.  
  362. 5.3. mutex_unlock
  363.  
  364.     void
  365.     mutex_unlock(m)
  366.             mutex_t m;
  367.  
  368. The mutex_unlock() procedure unlocks the mutex m, giving other threads a chance
  369. to lock it.
  370.  
  371.  
  372.  
  373. 5.4. condition_signal
  374.  
  375.     void
  376.     condition_signal(c)
  377.             condition_t c;
  378.  
  379. The condition_signal() procedure should be called when  one  thread  wishes  to
  380. indicate  that the condition represented by the condition variable is now true.
  381. If any other threads are waiting (via condition_wait()), then at least  one  of
  382. them will be awakened.  If no threads are waiting, then nothing happens.
  383.  
  384.  
  385.  
  386. 5.5. condition_broadcast
  387.  
  388.     void
  389.     condition_broadcast(c)
  390.             condition_t c;
  391.  
  392. The  condition_broadcast()  procedure  is similar to condition_signal(), except
  393. that it awakens all threads waiting for the condition, not just one of them.
  394.  
  395.  
  396.  
  397. 5.6. condition_wait
  398.  
  399.     void
  400.     condition_wait(c, m)
  401.             condition_t c;
  402.             mutex_t m;
  403.  
  404. The condition_wait() procedure unlocks m, suspends the calling thread until the
  405. specified  condition  is  likely  to be true, and locks m again when the thread
  406. resumes.  Since there is no guarantee that the condition will be true when  the
  407. thread resumes, use of this procedure should always be of the form
  408.  
  409.             mutex_lock(m);
  410.             ...
  411.             while (/* condition is not true */)
  412.                     condition_wait(c, m);
  413.             ...
  414.             mutex_unlock(m);
  415.  
  416. Shared variables should be inspected on each iteration to determine whether the
  417. condition is true.
  418.  
  419. 6. Management of Synchronization Variables
  420. A mutex or condition variable can be allocated dynamically from  the  heap,  or
  421. the  programmer  can  take  an  object  of  the  referent  type,  initialize it
  422. appropriately, and then use its address.
  423.  
  424.  
  425.  
  426. 6.1. Allocation
  427.  
  428.     mutex_t
  429.     mutex_alloc()
  430.  
  431.     condition_t
  432.     condition_alloc()
  433.  
  434. These functions provide dynamic allocation of mutex and condition objects.
  435.  
  436.  
  437.  
  438. 6.2. Deallocation
  439.  
  440.     void
  441.     mutex_free(m)
  442.             mutex_t m;
  443.  
  444.     void
  445.     condition_free(c)
  446.             condition_t c;
  447.  
  448. These functions allow the programmer to deallocate mutex and condition  objects
  449. that  were  allocated  dynamically.    Before  deallocating such an object, the
  450. programmer must  guarantee  that  no  other  thread  will  reference  it.    In
  451. particular,  a  thread  blocked  in  mutex_lock() or condition_wait() should be
  452. viewed as referencing the object continually, so freeing the object ``out  from
  453. under''  such  a thread is erroneous, and can result in bugs that are extremely
  454. difficult to track down.
  455. 6.3. Initialization
  456.  
  457.     void
  458.     mutex_init(m)
  459.             struct mutex *m;
  460.  
  461.     void
  462.     condition_init(c)
  463.             struct condition *c;
  464.  
  465. These functions allow the programmer to initialize  an  object  of  the  struct
  466. mutex  or  struct  condition  referent  type,  so  that its address can be used
  467. wherever an object of type mutex_t or condition_t is expected.    For  example,
  468. the  mutex_alloc()  function  could  be  written  in  terms  of mutex_init() as
  469. follows:
  470.  
  471.             mutex_t
  472.             mutex_alloc()
  473.             {
  474.                     register mutex_t m;
  475.  
  476.                     m = (mutex_t) malloc(sizeof(struct mutex));
  477.                     mutex_init(m);
  478.                     return m;
  479.             }
  480.  
  481. Initialization of the referent type is most often used when the programmer  has
  482. included  the  referent  type  itself  (rather  than  a  pointer)  in  a larger
  483. structure, for more  efficient  storage  allocation.    For  instance,  a  data
  484. structure might contain a component of type struct mutex to allow each instance
  485. of that structure to be locked independently.   During  initialization  of  the
  486. instance, the programmer would call mutex_init() on the struct mutex component.
  487. The alternative  of  using  a  mutex_t  component  and  initializing  it  using
  488. mutex_alloc() would be less efficient.
  489.  
  490.  
  491.  
  492. 6.4. Finalization
  493.  
  494.     void
  495.     mutex_clear(m)
  496.             struct mutex *m;
  497.  
  498.     void
  499.     condition_clear(c)
  500.             struct condition *c;
  501.  
  502. These  functions allow the programmer to finalize an object of the struct mutex
  503. or struct condition referent type.  For  example,  the  mutex_free()  procedure
  504. could be written in terms of mutex_clear() as follows:
  505.  
  506.             void
  507.             mutex_free(m)
  508.                     mutex_t m;
  509.             {
  510.                     mutex_clear(m);
  511.                     free((char *) m);
  512.             }
  513.  
  514. 7. Shared Variables
  515. All  global  and  static  variables are shared among all threads: if one thread
  516. modifies such a variable, all other threads will observe the  new  value.    In
  517. addition,  a variable reachable from a pointer is shared among all threads that
  518. can dereference that pointer.  This  includes  objects  pointed  to  by  shared
  519. variables  of  pointer  type,  as  well  as  arguments  passed  by reference in
  520. cthread_fork().
  521.  
  522. When pointers are shared, some care is required  to  avoid  dangling  reference
  523. problems.    The programmer must ensure that the lifetime of the object pointed
  524. to is long enough to allow the other threads to dereference the pointer.  Since
  525. there  is  no  bound  on  the relative execution speed of threads, the simplest
  526. solution is to share pointers to global or heap-allocated objects only.   If  a
  527. pointer  to a local variable is shared, the procedure in which that variable is
  528. defined must remain active until it can be guaranteed that the pointer will  no
  529. longer  be dereferenced by other threads.  The synchronization functions can be
  530. used to ensure this.
  531.  
  532. The programmer must remember  that  unless  a  library,  like  the  standard  C
  533. library,  has  been  designed  to  work  in  the  presence  of  reentrancy, the
  534. operations provided by the library must be presumed to make unprotected use  of
  535. shared  data.   Hence, the programmer must protect against this through the use
  536. of a mutex that is locked before every library call  (or  sequence  of  library
  537. calls) and unlocked afterwards.
  538.  
  539.  
  540.  
  541. 7.1. Dynamic Allocation
  542. Dynamic  allocation  and  freeing  of user-defined data structures is typically
  543. accomplished with the standard C functions malloc() and free().  The C  threads
  544. package  provides  versions  of  these  functions  that  work  correctly in the
  545. presence of multiple threads.
  546.  
  547. 8. Using the C Threads Package
  548. All  of  the  functions  described  have  been   implemented   for   the   MACH
  549. multiprocessor   operating  system.    Three  implementations  of  threads  are
  550. provided, in the form of libraries.  Programs need not be recompiled to  use  a
  551. different thread implementation, only relinked.  To compile a program that uses
  552. C threads,  the  user  must  include  the  file  cthreads.h.    (The  directory
  553. /usr/mach/include  should  be  in  the  user's  CPATH  search  list  for  the C
  554. preprocessor to find this header file.)  The program must  call  cthread_init()
  555. before  using  any  other  C  threads  features.  To link a program that uses C
  556. threads, the user must specify  on  the  cc  command  line  one  of  the  three
  557. libraries  described  below,  followed  by  the -lmach library.  (The directory
  558. /usr/mach/lib should be in the user's LPATH search list for the linker to  find
  559. these libraries.)
  560.  
  561.  
  562.  
  563. 8.1. The Coroutine Implementation
  564. The  first  implementation,  -lco_threads, uses coroutines within a single MACH
  565. task (UNIX process).  Scheduling of  these  threads  is  non-preemptive,  hence
  566. cthread_yield()  should  be  called  within  loops  that  do not otherwise call
  567. synchronization procedures.  The  programmer  will  typically  link  with  this
  568. version while debugging.
  569.  
  570. This  implementation  includes  versions of the MACH interprocess communication
  571. primitives msg_receive(), msg_send(), and msg_rpc(), and a version of the  UNIX
  572. select()  system  call, that can be called from one thread without blocking the
  573. other threads in the program.  The other  forms  of  UNIX  I/O  have  not  been
  574. redefined  for  use with -lco_threads, however.  For example, calling getchar()
  575. from one thread may block all threads in the program, not just the caller.   To
  576. work  around  this,  the  programmer should first call select() on the relevant
  577. file descriptor to guarantee that  the  subsequent  input  operation  will  not
  578. block.
  579.  
  580.  
  581.  
  582. 8.2. The MACH Thread Implementation
  583. The second implementation, -lthreads, uses one MACH thread per C thread.  These
  584. threads  are  preemptively  scheduled,  and  may  execute  in  parallel  on   a
  585. multiprocessor.    This  is  the  implementation  that  should  be  used in the
  586. production version of a C threads program.
  587.  
  588. The  current  -lco_threads  and  -lthreads   implementations   allocate   large
  589. fixed-size  stacks  for  each  C thread in virtual memory.  The implementations
  590. rely on the MACH virtual memory system to  allocate  physical  memory  only  as
  591. needed by the thread.
  592.  
  593.  
  594.  
  595. 8.3. The MACH Task Implementation
  596. The third implementation, -ltask_threads, uses one MACH task (UNIX process) per
  597. thread, and uses the MACH virtual memory primitives  to  share  memory  between
  598. threads.    In  most circumstances, the -lthreads implementation should be used
  599. instead of this one.  An exception is when the programmer  wishes  to  use  the
  600. MACH  virtual  memory  primitives  to  provide  a specialized pattern of memory
  601. sharing between C threads.
  602.  
  603. Users of the -ltask_threads implementation should note that  capabilities  such
  604. as  MACH  ports  and UNIX file descriptors are private to the task that creates
  605. them, and so cannot be shared.  The current -ltask_threads implementation  also
  606. makes  stack  segments  private  to  each  task, so automatic (stack-allocated)
  607. variables cannot be shared.
  608.  
  609. The MACH operating system currently limits the number of tasks (and  hence  the
  610. number  of  C  threads  in  the  -ltask_threads implementation) that a user may
  611. create.  Applications that create  large  numbers  of  threads  will  encounter
  612. run-time  errors  when  they  exceed  this  limit.    It  may  be the case that
  613. concurrent  execution  is  required  to  avoid  deadlock  (for  example,  in  a
  614. multi-stage  pipeline).    For  applications  with largely independent threads,
  615. however, a limited degree of parallelism may  still  allow  correct  execution.
  616. The following function can be used in such applications.
  617.  
  618.     void
  619.     cthread_set_limit(n)
  620.             int n;
  621.  
  622. This  function  limits  the  number of active threads to n.  If a newly created
  623. thread of control exceeds this limit, it will not begin execution until another
  624. thread terminates.
  625.  
  626. 9. Debugging
  627. It   is   strongly   recommended   that   the   coroutine-based  implementation
  628. (-lco_threads) be used for debugging, for the following reasons:
  629.  
  630.    - The order of thread context switching  is  repeatable  in  successive
  631.      executions  of  the  program,  so obvious synchronization bugs may be
  632.      found easily.
  633.  
  634.    - Since the program is a single MACH task, existing  debuggers  can  be
  635.      used.
  636.  
  637.    - The user need not worry about concurrent calls to C library routines.
  638.  
  639.  
  640.  
  641. 9.1. Low-Level Tracing
  642.  
  643.     int cthread_debug;
  644.  
  645. Setting  this  variable  to  1 causes diagnostic information to be printed when
  646. each C threads primitive is executed.  Trace output appears on stdout.
  647.  
  648.  
  649.  
  650. 9.2. Associating Names with C Thread Objects
  651.  
  652.     void
  653.     cthread_set_name(t, name)
  654.             cthread_t t;
  655.             string_t name;
  656.  
  657.     string_t
  658.     cthread_name(t)
  659.             cthread_t t;
  660.  
  661.     void
  662.     mutex_set_name(m, name)
  663.             mutex_t m;
  664.             string_t name;
  665.  
  666.     string_t
  667.     mutex_name(m)
  668.             mutex_t m;
  669.  
  670.     void
  671.     condition_set_name(c, name)
  672.             condition_t c;
  673.             string_t name;
  674.  
  675.     string_t
  676.     condition_name(c)
  677.             condition_t c;
  678.  
  679. These functions allow the user to associate a name with  a  thread,  mutex,  or
  680. condition.    The name is used when trace information is displayed (see above).
  681. The  name  may  also  be  used  by  the  programmer  for   application-specific
  682. diagnostics.
  683.  
  684.  
  685.  
  686. 9.3. Pitfalls of Preemptively Scheduled Threads
  687. The  C  run-time library needs a substantial amount of modification in order to
  688. be used with preemptively scheduled  threads  (-lthreads  and  -ltask_threads).
  689. Currently  the  user  must  ensure  that  calls to the standard I/O library are
  690. serialized, through the use of one or  more  mutex  variables.    (The  storage
  691. allocation   functions   malloc()   and  free()  do  not  require  any  special
  692. precautions.)
  693.  
  694. The debuggers currently available under MACH cannot be used on programs  linked
  695. with  -lthreads  or  -ltask_threads.    Furthermore, the very act of turning on
  696. tracing or adding print statements may perturb programs that incorrectly depend
  697. on  thread  execution  speed.  One technique that is useful in such cases is to
  698. vary the granularity of locking and synchronization used in the program, making
  699. sure that the program works with coarse-grained synchronization before refining
  700. it further.
  701. 10. Examples
  702. The following example illustrates how the facilities described here can be used
  703. to program Hoare monitors [2].  The program would be compiled and linked by the
  704. command
  705.  
  706.             cc hoaremonitor.c -lthreads -lmach
  707.  
  708.     /*
  709.      * Producer/consumer with bounded buffer.
  710.      *
  711.      * The producer reads characters from stdin
  712.      * and puts them into the buffer.  The consumer
  713.      * gets characters from the buffer and writes them
  714.      * to stdout.  The two threads execute concurrently
  715.      * except when synchronized by the buffer.
  716.      */
  717.     #include <stdio.h>
  718.     #include <cthreads.h>
  719.     typedef struct buffer {
  720.         struct mutex lock;
  721.         char *chars;      /* chars[0..size-1] */
  722.         int size;
  723.         int px, cx;       /* producer and consumer indices */
  724.         int count;        /* number of unconsumed chars in buffer */
  725.         struct condition non_empty, non_full;
  726.     } *buffer_t;
  727.     void
  728.     buffer_put(ch, b)
  729.         char ch;
  730.         buffer_t b;
  731.     {
  732.         mutex_lock(&b->lock);
  733.         while (b->count == b->size)
  734.             condition_wait(&b->non_full, &b->lock);
  735.         ASSERT(0 <= b->count && b->count < b->size);
  736.         b->chars[b->px] = ch;
  737.         b->px = (b->px + 1) % b->size;
  738.         b->count += 1;
  739.         condition_signal(&b->non_empty);
  740.         mutex_unlock(&b->lock);
  741.     }
  742.     char
  743.     buffer_get(b)
  744.         buffer_t b;
  745.     {
  746.         char ch;
  747.         mutex_lock(&b->lock);
  748.         while (b->count == 0)
  749.             condition_wait(&b->non_empty, &b->lock);
  750.         ASSERT(0 < b->count && b->count <= b->size);
  751.         ch = b->chars[b->cx];
  752.         b->cx = (b->cx + 1) % b->size;
  753.         b->count -= 1;
  754.         condition_signal(&b->non_full);
  755.         mutex_unlock(&b->lock);
  756.         return ch;
  757.     }
  758.     void
  759.     producer(b)
  760.         buffer_t b;
  761.     {
  762.         int ch;
  763.         do buffer_put((ch = getchar()), b);
  764.         while (ch != EOF);
  765.     }
  766.     void
  767.     consumer(b)
  768.         buffer_t b;
  769.     {
  770.         int ch;
  771.         while ((ch = buffer_get(b)) != EOF)
  772.             printf("%c", ch);
  773.     }
  774.     buffer_t
  775.     buffer_alloc(size)
  776.         int size;
  777.     {
  778.         buffer_t b;
  779.         extern char *malloc();
  780.         b = (buffer_t) malloc(sizeof(struct buffer));
  781.         mutex_init(&b->lock);
  782.         b->size = size;
  783.         b->chars = malloc((unsigned) size);
  784.         b->px = b->cx = b->count = 0;
  785.         condition_init(&b->non_empty);
  786.         condition_init(&b->non_full);
  787.         return b;
  788.     }
  789.     #define BUFFER_SIZE 10
  790.     main()
  791.     {
  792.         buffer_t b;
  793.         cthread_init();
  794.         b = buffer_alloc(BUFFER_SIZE);
  795.         cthread_detach(cthread_fork(producer, b));
  796.         cthread_detach(cthread_fork(consumer, b));
  797.         cthread_exit(0);
  798.     }
  799.  
  800. The following example shows how to structure a program in which a single master
  801. thread  spawns  a  number  of  concurrent  slaves and then waits until they all
  802. finish.  The program would be compiled and linked by the command
  803.  
  804.             cc masterslave.c -lthreads -lmach
  805.  
  806.     /*
  807.      * Master/slave program structure.
  808.      */
  809.     #include <stdio.h>
  810.     #include <cthreads.h>
  811.     int count;         /* number of slaves active */
  812.     mutex_t lock;      /* mutual exclusion for count */
  813.     condition_t done;  /* signalled each time a slave finishes */
  814.     extern long random();
  815.     init()
  816.     {
  817.         cthread_init();
  818.         count = 0;
  819.         lock = mutex_alloc();
  820.         done = condition_alloc();
  821.         srandom(time((int *) 0));  /* initialize random number generator */
  822.     }
  823.     /*
  824.      * Each slave just counts up to its argument,
  825.      * yielding the processor on each iteration.
  826.      * When it is finished, it decrements the global count
  827.      * and signals that it is done.
  828.      */
  829.     slave(n)
  830.         int n;
  831.     {
  832.         int i;
  833.         for (i = 0; i < n; i += 1)
  834.             cthread_yield();
  835.         mutex_lock(lock);
  836.         count -= 1;
  837.         printf("Slave finished %d cycles.\n", n);
  838.         condition_signal(done);
  839.         mutex_unlock(lock);
  840.     }
  841.     /*
  842.      * The master spawns a given number of slaves
  843.      * and then waits for them all to finish.
  844.      */
  845.     master(nslaves)
  846.         int nslaves;
  847.     {
  848.         int i;
  849.         for (i = 1; i <= nslaves; i += 1) {
  850.             mutex_lock(lock);
  851.             /*
  852.              * Fork a slave and detach it,
  853.              * since the master never joins it individually.
  854.              */
  855.             count += 1;
  856.             cthread_detach(cthread_fork(slave, random() % 1000));
  857.             mutex_unlock(lock);
  858.         }
  859.         mutex_lock(lock);
  860.         while (count != 0)
  861.             condition_wait(done, lock);
  862.         mutex_unlock(lock);
  863.         printf("All %d slaves have finished.\n", nslaves);
  864.         cthread_exit(0);
  865.     }
  866.     main()
  867.     {
  868.         init();
  869.         master((int) random() % 16);  /* create up to 15 slaves */
  870.     }
  871.                                   REFERENCES
  872.  
  873. [1]   Brian W. Kernighan and Dennis M. Ritchie.
  874.       The C Programming Language.
  875.       Prentice-Hall, 1978.
  876.  
  877. [2]   C. A. R. Hoare.
  878.       Monitors: An Operating System Structuring Concept.
  879.       Communications of the ACM 17(10):549-557, October, 1974.
  880.  
  881. [3]   Robert V. Baron, Richard F. Rashid, Ellen Siegel, Avadis Tevanian, and
  882.       Michael W. Young.
  883.       MACH-1: A Multiprocessor Oriented Operating System and Environment.
  884.       In Arthur Wouk (editor), New Computing Environments: Parallel, Vector,
  885.          and Symbolic. SIAM, 1986.
  886.  
  887. [4]   Butler W. Lampson and David D. Redell.
  888.       Experience with Processes and Monitors in Mesa.
  889.       Communications of the ACM 23(2):105-117, February, 1980.
  890.  
  891. [5]   Paul Rovner, Roy Levin, and John Wick.
  892.       On Extending Modula-2 for Building Large, Integrated Systems.
  893.       Technical Report 3, DEC Systems Research Center, January, 1985.
  894.                                Table of Contents
  895. 1. Introduction                                                               1
  896. 2. Naming Conventions                                                         1
  897. 3. Initializing the C Threads Package                                         1
  898.      3.1. cthreads.h                                                          1
  899.      3.2. cthread_init                                                        1
  900. 4. Threads of Control                                                         1
  901.      4.1. Creation                                                            1
  902.      4.2. Termination                                                         1
  903.      4.3. cthread_fork                                                        1
  904.      4.4. cthread_exit                                                        1
  905.      4.5. cthread_join                                                        1
  906.      4.6. cthread_detach                                                      1
  907.      4.7. cthread_yield                                                       1
  908.      4.8. cthread_self                                                        1
  909.      4.9. cthread_set_data, cthread_data                                      1
  910. 5. Synchronization                                                            2
  911.      5.1. mutex_lock                                                          2
  912.      5.2. mutex_try_lock                                                      2
  913.      5.3. mutex_unlock                                                        2
  914.      5.4. condition_signal                                                    2
  915.      5.5. condition_broadcast                                                 2
  916.      5.6. condition_wait                                                      2
  917. 6. Management of Synchronization Variables                                    2
  918.      6.1. Allocation                                                          2
  919.      6.2. Deallocation                                                        2
  920.      6.3. Initialization                                                      3
  921.      6.4. Finalization                                                        3
  922. 7. Shared Variables                                                           3
  923.      7.1. Dynamic Allocation                                                  3
  924. 8. Using the C Threads Package                                                3
  925.      8.1. The Coroutine Implementation                                        3
  926.      8.2. The MACH Thread Implementation                                      3
  927.      8.3. The MACH Task Implementation                                        3
  928. 9. Debugging                                                                  3
  929.      9.1. Low-Level Tracing                                                   3
  930.      9.2. Associating Names with C Thread Objects                             3
  931.      9.3. Pitfalls of Preemptively Scheduled Threads                          4
  932. 10. Examples                                                                  5
  933.