1 // Copyright (C) 2010 Davis E. King (davis@dlib.net) 2 // License: Boost Software License See LICENSE.txt for the full license. 3 #ifndef DLIB_READ_WRITE_MUTEX_EXTENSIOn_ 4 #define DLIB_READ_WRITE_MUTEX_EXTENSIOn_ 5 6 #include "threads_kernel.h" 7 #include "read_write_mutex_extension_abstract.h" 8 9 namespace dlib 10 { 11 12 // ---------------------------------------------------------------------------------------- 13 14 class read_write_mutex 15 { 16 /*! 17 INITIAL VALUE 18 - max_locks == defined by constructor 19 - available_locks == max_locks 20 - write_lock_in_progress == false 21 - write_lock_active == false 22 23 CONVENTION 24 - Each time someone gets a read only lock they take one of the "available locks" 25 and each write lock takes all possible locks (i.e. max_locks). The number of 26 available locks is recorded in available_locks. Any time you try to lock this 27 object and there aren't available locks you have to wait. 28 29 - max_locks == max_readonly_locks() 30 31 - if (some thread is on the process of obtaining a write lock) then 32 - write_lock_in_progress == true 33 - else 34 - write_lock_in_progress == false 35 36 - if (some thread currently has a write lock on this mutex) then 37 - write_lock_active == true 38 - else 39 - write_lock_active == false 40 !*/ 41 42 public: 43 read_write_mutex()44 read_write_mutex ( 45 ) : s(m), 46 max_locks(0xFFFFFFFF), 47 available_locks(max_locks), 48 write_lock_in_progress(false), 49 write_lock_active(false) 50 {} 51 read_write_mutex(unsigned long max_locks_)52 explicit read_write_mutex ( 53 unsigned long max_locks_ 54 ) : s(m), 55 max_locks(max_locks_), 56 available_locks(max_locks_), 57 write_lock_in_progress(false), 58 write_lock_active(false) 59 { 60 // make sure requires clause is not broken 61 DLIB_ASSERT(max_locks > 0, 62 "\t read_write_mutex::read_write_mutex(max_locks)" 63 << "\n\t You must give a non-zero value for max_locks" 64 << "\n\t this: " << this 65 ); 66 } 67 ~read_write_mutex()68 ~read_write_mutex ( 69 ) 70 {} 71 lock()72 void lock ( 73 ) const 74 { 75 m.lock(); 76 77 // If another write lock is already in progress then wait for it to finish 78 // before we start trying to grab all the available locks. This way we 79 // don't end up fighting over the locks. 80 while (write_lock_in_progress) 81 s.wait(); 82 83 // grab the right to perform a write lock 84 write_lock_in_progress = true; 85 86 // now start grabbing all the locks 87 unsigned long locks_obtained = available_locks; 88 available_locks = 0; 89 while (locks_obtained != max_locks) 90 { 91 s.wait(); 92 locks_obtained += available_locks; 93 available_locks = 0; 94 } 95 96 write_lock_in_progress = false; 97 write_lock_active = true; 98 99 m.unlock(); 100 } 101 unlock()102 void unlock ( 103 ) const 104 { 105 m.lock(); 106 107 // only do something if there really was a lock in place 108 if (write_lock_active) 109 { 110 available_locks = max_locks; 111 write_lock_active = false; 112 s.broadcast(); 113 } 114 115 m.unlock(); 116 } 117 lock_readonly()118 void lock_readonly ( 119 ) const 120 { 121 m.lock(); 122 123 while (available_locks == 0) 124 s.wait(); 125 126 --available_locks; 127 128 m.unlock(); 129 } 130 unlock_readonly()131 void unlock_readonly ( 132 ) const 133 { 134 m.lock(); 135 136 // If this condition is false then it means there are no more readonly locks 137 // to free. So we don't do anything. 138 if (available_locks != max_locks && !write_lock_active) 139 { 140 ++available_locks; 141 142 // only perform broadcast when there is another thread that might be listening 143 if (available_locks == 1 || write_lock_in_progress) 144 { 145 s.broadcast(); 146 } 147 } 148 149 m.unlock(); 150 } 151 max_readonly_locks()152 unsigned long max_readonly_locks ( 153 ) const 154 { 155 return max_locks; 156 } 157 158 private: 159 mutex m; 160 signaler s; 161 const unsigned long max_locks; 162 mutable unsigned long available_locks; 163 mutable bool write_lock_in_progress; 164 mutable bool write_lock_active; 165 166 // restricted functions 167 read_write_mutex(read_write_mutex&); // copy constructor 168 read_write_mutex& operator=(read_write_mutex&); // assignment operator 169 }; 170 171 // ---------------------------------------------------------------------------------------- 172 173 } 174 175 #endif // DLIB_READ_WRITE_MUTEX_EXTENSIOn_ 176 177 178