| Homepage | Demos | Overview | Downloads | Tutorials | Reference | Credits |
MutexLock.hGo to the documentation of this file.00001 //-*-c++-*- 00002 #ifndef __MUTEX_LOCK_ET__ 00003 #define __MUTEX_LOCK_ET__ 00004 00005 #include "debuget.h" 00006 #include <iostream> 00007 00008 //#define MUTEX_LOCK_ET_USE_SPINCOUNT 00009 00010 //! A software only mutual exclusion lock. 00011 /*! Use this to prevent more than one process from accessing a data structure 00012 * at the same time (which often leads to unpredictable and unexpected results) 00013 * 00014 * The template parameter specifies the maximum number of different processes 00015 * which need to be protected. This needs to be allocated ahead of time, as 00016 * there doesn't seem to be a way to dynamically scale as needed without 00017 * risking possible errors if two processes are both trying to set up at the 00018 * same time. Also, by using a template parameter, all data structures are 00019 * contained within the class's memory allocation, so no pointers are involved. 00020 * 00021 * Locks in this class can be recursive. If you lock 5 times and then unlock() 00022 * once, the lock is released. If you want to have releases match locks, call 00023 * release() instead of unlock(). 00024 * 00025 * Note that there is no check that the process doing the unlocking is the one 00026 * that actually has the lock. Be careful about this. 00027 * 00028 * @warning Doing mutual exclusion in software is tricky business, be careful about any 00029 * modifications you make! 00030 * 00031 * Implements a first-come-first-served Mutex as laid out on page 11 of: \n 00032 * "A First Come First Served Mutal Exclusion Algorithm with Small Communication Variables" \n 00033 * Edward A. Lycklama, Vassos Hadzilacos - Aug. 1991 00034 */ 00035 template<unsigned int num_doors> 00036 class MutexLock { 00037 public: 00038 static const unsigned int NO_OWNER=-1U; //!< marks as unlocked 00039 00040 //! constructor, just calls the init() function. 00041 MutexLock() : doors_used(0), owner_index(NO_OWNER), lockcount(0) { init(); } 00042 00043 //! blocks (by busy looping on do_try_lock()) until a lock is achieved 00044 /*! You should pass some process-specific ID number as the input - just 00045 * make sure no other process will be using the same value. 00046 * 00047 * This is not a recursive lock - repeated locks still only require one 00048 * release to undo them. 00049 * @todo - I'd like to not use a loop here */ 00050 void lock(int id); 00051 00052 //! attempts to get a lock, returns true if it succeeds 00053 /*! You should pass some process-specific ID number as the input - just 00054 * make sure no other process will be using the same value. 00055 * 00056 * This is not a recursive lock - repeated locks still only require one 00057 * release to undo them. */ 00058 bool try_lock(int id); 00059 00060 //! releases one recursive lock-level from whoever has the current lock 00061 void release(); 00062 00063 //! completely unlocks, regardless of how many times a recursive lock has been obtained 00064 inline void unlock() { lockcount=1; release(); } 00065 00066 //! returns the lockcount 00067 unsigned int get_lock_level() const { return lockcount; } 00068 00069 //! returns the current owner's id 00070 inline int owner() { return owner_index==NO_OWNER ? NO_OWNER : doors[owner_index].id; } 00071 00072 //! allows you to reset one of the possible owners, so another process can take its place. This is not tested 00073 void forget(int id); 00074 00075 #ifdef MUTEX_LOCK_ET_USE_SPINCOUNT 00076 inline unsigned int getSpincount() { return spincount; } //!< returns the number of times the spin() function has been called 00077 inline unsigned int resetSpincount() { spincount=0; } //!< resets the counter of the number of times the spin() function has been called 00078 #endif 00079 00080 protected: 00081 //! Does the work of trying to get a lock 00082 /*! Pass @c true for @a block if you want it to use FCFS blocking 00083 * instead of just returning right away if another process has the lock */ 00084 bool do_try_lock(unsigned int index, bool block); 00085 00086 //! returns the internal index mapping to the id number supplied by the process 00087 unsigned int lookup(int id); //may create a new entry 00088 00089 #ifdef MUTEX_LOCK_ET_USE_SPINCOUNT 00090 volatile unsigned int spincount; //!< handy to track how much time we're wasting 00091 void init() { spincount=0; }//memset((void*)doors,0,sizeof(doors)); } //!< just resets spincount 00092 inline void spin() { spincount++; } //!< if you find a way to sleep for a few microseconds instead of busy waiting, put it here 00093 #else 00094 void init() { } //!< Doesn't do anything if you have the MUTEX_LOCK_ET_USE_SPINCOUNT undef'ed. Used to do a memset, but that was causing problems.... 00095 //memset((void*)doors,0,sizeof(doors)); } 00096 inline void spin() {} //!< If you find a way to sleep for a few microseconds instead of busy waiting, put it here 00097 #endif 00098 00099 //! Holds per process shared info, one of these per process 00100 struct door_t { 00101 door_t() : id(NO_OWNER), FCFS_in_use(false), BL_ready(false), BL_in_use(false), turn('\0'), next_turn_bit('\0') {} //!< constructor 00102 //door_t(int i) : id(i), FCFS_in_use(false), BL_ready(false), BL_in_use(false), next_turn_bit('\0') {} 00103 int id; //!< process ID this doorway is assigned to 00104 volatile bool FCFS_in_use; //!< In FCFS doorway, corresponds to 'c_i' 00105 volatile bool BL_ready; //!< Signals past FCFS doorway, ready for BL doorway, corresponds to 'v_i' 00106 volatile bool BL_in_use; //!< Burns-Lamport doorway, corresponds to 'x_i' 00107 volatile unsigned char turn; //!< clock pulse, initial value doesn't matter 00108 unsigned char next_turn_bit; //!< selects which bit of turn will be flipped next 00109 }; 00110 00111 door_t doors[num_doors]; //!< holds all the doors 00112 unsigned int doors_used; //!< counts the number of doors used 00113 unsigned int owner_index; //!< holds the door index of the current lock owner 00114 unsigned int lockcount; //!< the depth of the lock, 0 when unlocked 00115 }; 00116 00117 00118 template<unsigned int num_doors> 00119 void 00120 MutexLock<num_doors>::lock(int id) { 00121 if(owner()!=id) 00122 while(!do_try_lock(lookup(id),true)) 00123 spin(); 00124 lockcount++; 00125 } 00126 00127 00128 template<unsigned int num_doors> 00129 bool 00130 MutexLock<num_doors>::try_lock(int id) { 00131 if(owner()==id) { 00132 lockcount++; 00133 return true; 00134 } else { 00135 if(do_try_lock(lookup(id),false)) { 00136 lockcount++; 00137 return true; 00138 } else 00139 return false; 00140 } 00141 } 00142 00143 00144 template<unsigned int num_doors> 00145 void 00146 MutexLock<num_doors>::release() { 00147 if(lockcount>0) 00148 if(--lockcount==0) 00149 if(owner_index!=NO_OWNER) { 00150 unsigned int tmp = owner_index; 00151 owner_index=NO_OWNER; 00152 doors[tmp].BL_in_use=false; 00153 doors[tmp].BL_ready=false; 00154 // *** Lock has been released *** // 00155 } 00156 } 00157 00158 00159 //! If you define this to do something more interesting, can use it to see what's going on in the locking process 00160 #define mutexdebugout(i,c) {} 00161 //#define mutexdebugout(i,c) { std::cout << ((char)(i==0?c:((i==1?'M':'a')+(c-'A')))) << std::flush; } 00162 00163 00164 template<unsigned int num_doors> 00165 bool 00166 MutexLock<num_doors>::do_try_lock(unsigned int i, bool block) { 00167 if(i==NO_OWNER) { 00168 std::cerr << "WARNING: new process attempted to lock beyond num_doors ("<<num_doors<<")" << std::endl; 00169 return false; 00170 } 00171 unsigned char S[num_doors]; // a local copy of everyone's doors 00172 // *** Entering FCFS doorway *** // 00173 // pprintf(TextOutputStream,"**%d**\n",i); 00174 mutexdebugout(i,'A'); 00175 doors[i].FCFS_in_use=true; 00176 for(unsigned int j=0; j<num_doors; j++) 00177 S[j]=doors[j].turn; 00178 doors[i].next_turn_bit=1-doors[i].next_turn_bit; 00179 doors[i].turn^=(1<<doors[i].next_turn_bit); 00180 doors[i].BL_ready=true; 00181 doors[i].FCFS_in_use=false; 00182 // *** Leaving FCFS doorway *** // 00183 mutexdebugout(i,'B'); 00184 for(unsigned int j=0; j<num_doors; j++) { 00185 mutexdebugout(i,'C'); 00186 while(doors[j].FCFS_in_use || (doors[j].BL_ready && S[j]==doors[j].turn)) 00187 if(block) 00188 spin(); 00189 else { 00190 doors[i].BL_ready=false; 00191 return false; 00192 } 00193 mutexdebugout(i,'D'); 00194 } 00195 // *** Entering Burns-Lamport *** // 00196 mutexdebugout(i,'E'); 00197 do { 00198 doors[i].BL_in_use=true; 00199 for(unsigned int t=0; t<i; t++) 00200 if(doors[t].BL_in_use) { 00201 doors[i].BL_in_use=false; 00202 if(!block) { 00203 doors[i].BL_ready=false; 00204 return false; 00205 } 00206 mutexdebugout(i,'F'); 00207 while(doors[t].BL_in_use) 00208 spin(); 00209 mutexdebugout(i,'G'); 00210 break; 00211 } 00212 } while(!doors[i].BL_in_use); 00213 for(unsigned int t=i+1; t<num_doors; t++) 00214 while(doors[t].BL_in_use) 00215 spin(); 00216 // *** Leaving Burns-Lamport ***// 00217 // *** Lock has been given *** // 00218 mutexdebugout(i,'H'); 00219 owner_index=i; 00220 return true; 00221 } 00222 00223 00224 template<unsigned int num_doors> 00225 unsigned int 00226 MutexLock<num_doors>::lookup(int id) { 00227 // TODO - this could break if two new processes are adding themselves at the same time 00228 // or an id is being forgotten at the same time 00229 //I'm expecting a very small number of processes to be involved 00230 //probably not worth overhead of doing something fancy like a sorted array 00231 unsigned int i; 00232 for(i=0; i<doors_used; i++) 00233 if(doors[i].id==id) 00234 return i; 00235 if(i==num_doors) 00236 return NO_OWNER; 00237 doors[i].id=id; 00238 doors_used++; 00239 return i; 00240 } 00241 00242 00243 template<unsigned int num_doors> 00244 void 00245 MutexLock<num_doors>::forget(int id) { //not tested thoroughly 00246 unsigned int i = lookup(id); 00247 do_try_lock(i,true); 00248 doors[i].id=doors[--doors_used].id; 00249 doors[doors_used].id=NO_OWNER; 00250 release(); 00251 } 00252 00253 00254 /*! @file 00255 * @brief Defines MutexLock, a software only mutual exclusion lock. 00256 * @author ejt (Creator), Edward A. Lycklama, Vassos Hadzilacos (paper from which this was based) 00257 * 00258 * $Author: ejt $ 00259 * $Name: tekkotsu-2_0_1+Doc $ 00260 * $Revision: 1.8 $ 00261 * $State: Rel $ 00262 * $Date: 2003/09/25 15:31:53 $ 00263 */ 00264 00265 #endif |
|
Tekkotsu v2.0.1+Doc |
Generated Mon Feb 9 22:16:49 2004 by Doxygen 1.3.5 |