Main Page | Alphabetical List | Class List | File List | Class Members | File Members

synch.h

Go to the documentation of this file.
00001 // synch.h 00002 // Data structures for synchronizing threads. 00003 // 00004 // Three kinds of synchronization are defined here: semaphores, 00005 // locks, and condition variables. The implementation for 00006 // semaphores is given; for the latter two, only the procedure 00007 // interface is given -- they are to be implemented as part of 00008 // the first assignment. 00009 // 00010 // Note that all the synchronization objects take a "name" as 00011 // part of the initialization. This is solely for debugging purposes. 00012 // 00013 // Copyright (c) 1992-1993 The Regents of the University of California. 00014 // All rights reserved. See copyright.h for copyright notice and limitation 00015 // synch.h -- synchronization primitives. 00016 00017 #ifndef SYNCH_H 00018 #define SYNCH_H 00019 00020 #include "copyright.h" 00021 #include "thread.h" 00022 #include "list.h" 00023 00024 // The following class defines a "semaphore" whose value is a non-negative 00025 // integer. The semaphore has only two operations P() and V(): 00026 // 00027 // P() -- waits until value > 0, then decrement 00028 // 00029 // V() -- increment, waking up a thread waiting in P() if necessary 00030 // 00031 // Note that the interface does *not* allow a thread to read the value of 00032 // the semaphore directly -- even if you did read the value, the 00033 // only thing you would know is what the value used to be. You don't 00034 // know what the value is now, because by the time you get the value 00035 // into a register, a context switch might have occurred, 00036 // and some other thread might have called P or V, so the true value might 00037 // now be different. 00038 00039 class Semaphore { 00040 public: 00041 Semaphore(char* debugName, int initialValue); // set initial value 00042 ~Semaphore(); // de-allocate semaphore 00043 char* getName() { return name;} // debugging assist 00044 00045 void P(); // these are the only operations on a semaphore 00046 void V(); // they are both *atomic* 00047 00048 private: 00049 char* name; // useful for debugging 00050 int value; // semaphore value, always >= 0 00051 List *queue; // threads waiting in P() for the value to be > 0 00052 }; 00053 00054 // The following class defines a "lock". A lock can be BUSY or FREE. 00055 // There are only two operations allowed on a lock: 00056 // 00057 // Acquire -- wait until the lock is FREE, then set it to BUSY 00058 // 00059 // Release -- set lock to be FREE, waking up a thread waiting 00060 // in Acquire if necessary 00061 // 00062 // In addition, by convention, only the thread that acquired the lock 00063 // may release it. As with semaphores, you can't read the lock value 00064 // (because the value might change immediately after you read it). 00065 00066 class Lock { 00067 public: 00068 Lock(char* debugName); // initialize lock to be FREE 00069 ~Lock(); // deallocate lock 00070 char* getName() { return name; } // debugging assist 00071 00072 void Acquire(); // these are the only operations on a lock 00073 void Release(); // they are both *atomic* 00074 00075 bool isHeldByCurrentThread(); // true if the current thread 00076 // holds this lock. Useful for 00077 // checking in Release, and in 00078 // Condition variable ops below. 00079 00080 private: 00081 char* name; // for debugging 00082 // plus some other stuff you'll need to define 00083 }; 00084 00085 // The following class defines a "condition variable". A condition 00086 // variable does not have a value, but threads may be queued, waiting 00087 // on the variable. These are only operations on a condition variable: 00088 // 00089 // Wait() -- release the lock, relinquish the CPU until signaled, 00090 // then re-acquire the lock 00091 // 00092 // Signal() -- wake up a thread, if there are any waiting on 00093 // the condition 00094 // 00095 // Broadcast() -- wake up all threads waiting on the condition 00096 // 00097 // All operations on a condition variable must be made while 00098 // the current thread has acquired a lock. Indeed, all accesses 00099 // to a given condition variable must be protected by the same lock. 00100 // In other words, mutual exclusion must be enforced among threads calling 00101 // the condition variable operations. 00102 // 00103 // In Nachos, condition variables are assumed to obey *Mesa*-style 00104 // semantics. When a Signal or Broadcast wakes up another thread, 00105 // it simply puts the thread on the ready list, and it is the responsibility 00106 // of the woken thread to re-acquire the lock (this re-acquire is 00107 // taken care of within Wait()). By contrast, some define condition 00108 // variables according to *Hoare*-style semantics -- where the signalling 00109 // thread gives up control over the lock and the CPU to the woken thread, 00110 // which runs immediately and gives back control over the lock to the 00111 // signaller when the woken thread leaves the critical section. 00112 // 00113 // The consequence of using Mesa-style semantics is that some other thread 00114 // can acquire the lock, and change data structures, before the woken 00115 // thread gets a chance to run. 00116 00117 class Condition { 00118 public: 00119 Condition(char* debugName); // initialize condition to 00120 // "no one waiting" 00121 ~Condition(); // deallocate the condition 00122 char* getName() { return (name); } 00123 00124 void Wait(Lock *conditionLock); // these are the 3 operations on 00125 // condition variables; releasing the 00126 // lock and going to sleep are 00127 // *atomic* in Wait() 00128 void Signal(Lock *conditionLock); // conditionLock must be held by 00129 void Broadcast(Lock *conditionLock);// the currentThread for all of 00130 // these operations 00131 00132 private: 00133 char* name; 00134 // plus some other stuff you'll need to define 00135 }; 00136 #endif // SYNCH_H

Generated on Thu Sep 16 12:33:45 2004 for NachOS by doxygen 1.3.8