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