blocxx
|
00001 /******************************************************************************* 00002 * Copyright (C) 2007, Quest Software All rights reserved. 00003 * Copyright (C) 2006, Novell, Inc. All rights reserved. 00004 * 00005 * Redistribution and use in source and binary forms, with or without 00006 * modification, are permitted provided that the following conditions are met: 00007 * 00008 * * Redistributions of source code must retain the above copyright notice, 00009 * this list of conditions and the following disclaimer. 00010 * * Redistributions in binary form must reproduce the above copyright 00011 * notice, this list of conditions and the following disclaimer in the 00012 * documentation and/or other materials provided with the distribution. 00013 * * Neither the name of 00014 * Quest Software, 00015 * nor Novell, Inc., 00016 * nor the names of its contributors or employees may be used to 00017 * endorse or promote products derived from this software without 00018 * specific prior written permission. 00019 * 00020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 00021 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00022 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00023 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 00024 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 00025 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 00026 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 00027 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 00028 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 00029 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00030 * POSSIBILITY OF SUCH DAMAGE. 00031 *******************************************************************************/ 00032 00033 00039 #ifndef BLOCXX_GENERIC_RWLOCK_IMPL_HPP_INCLUDE_GUARD_ 00040 #define BLOCXX_GENERIC_RWLOCK_IMPL_HPP_INCLUDE_GUARD_ 00041 #include "blocxx/BLOCXX_config.h" 00042 #include "blocxx/CommonFwd.hpp" 00043 #include "blocxx/NonRecursiveMutexLock.hpp" 00044 #include "blocxx/Condition.hpp" 00045 #include "blocxx/Exception.hpp" 00046 #include "blocxx/TimeoutException.hpp" 00047 #include "blocxx/ExceptionIds.hpp" 00048 #include "blocxx/Timeout.hpp" 00049 #include "blocxx/TimeoutTimer.hpp" 00050 #include "blocxx/Assertion.hpp" 00051 00052 #include <map> 00053 00054 namespace BLOCXX_NAMESPACE 00055 { 00056 00057 BLOCXX_DECLARE_APIEXCEPTION(GenericRWLockImpl, BLOCXX_COMMON_API); 00063 template <typename IdT, typename CompareT> 00064 class GenericRWLockImpl 00065 { 00066 public: 00067 GenericRWLockImpl(); 00068 ~GenericRWLockImpl(); 00069 00073 void acquireReadLock(const IdT id, const Timeout& timeout); 00074 00081 void acquireWriteLock(const IdT id, const Timeout& timeout); 00082 00086 void releaseReadLock(const IdT id); 00087 00091 void releaseWriteLock(const IdT id); 00092 00093 private: 00094 00095 Condition m_waiting_writers; 00096 00097 bool m_canRead; 00098 Condition m_waiting_readers; 00099 00100 NonRecursiveMutex m_guard; 00101 unsigned m_numReaders; 00102 unsigned m_numWriters; // current writer + upgrading writer 00103 00104 struct LockerInfo 00105 { 00106 unsigned int readCount; 00107 unsigned int writeCount; 00108 00109 bool isReader() const 00110 { 00111 return readCount > 0; 00112 } 00113 00114 bool isWriter() const 00115 { 00116 return writeCount > 0; 00117 } 00118 }; 00119 00120 typedef std::map<IdT, LockerInfo, CompareT> IdMap; 00121 IdMap m_lockerInfo; 00122 00123 // unimplemented 00124 GenericRWLockImpl(const GenericRWLockImpl&); 00125 GenericRWLockImpl& operator=(const GenericRWLockImpl&); 00126 }; 00127 00129 template <typename IdT, typename CompareT> 00130 GenericRWLockImpl<IdT, CompareT>::GenericRWLockImpl() 00131 : m_canRead(true) 00132 , m_numReaders(0) 00133 , m_numWriters(0) 00134 { 00135 } 00137 template <typename IdT, typename CompareT> 00138 GenericRWLockImpl<IdT, CompareT>::~GenericRWLockImpl() 00139 { 00140 } 00142 template <typename IdT, typename CompareT> 00143 void 00144 GenericRWLockImpl<IdT, CompareT>::acquireReadLock(const IdT id, const Timeout& timeout) 00145 { 00146 TimeoutTimer timer(timeout); 00147 00148 NonRecursiveMutexLock l(m_guard); 00149 typename IdMap::iterator info = m_lockerInfo.find(id); 00150 00151 if (info != m_lockerInfo.end()) 00152 { 00153 LockerInfo& ti(info->second); 00154 // id already have a read or write lock, so just increment. 00155 BLOCXX_ASSERT(ti.isReader() || ti.isWriter()); 00156 ++ti.readCount; 00157 return; 00158 } 00159 00160 // id is a new reader 00161 while (!m_canRead || m_numWriters > 0) 00162 { 00163 if (!m_waiting_readers.timedWait(l, timer.asAbsoluteTimeout())) 00164 { 00165 BLOCXX_THROW(TimeoutException, "Timeout while waiting for read lock."); 00166 } 00167 } 00168 00169 // Increase the reader count 00170 LockerInfo lockerInfo; 00171 lockerInfo.readCount = 1; 00172 lockerInfo.writeCount = 0; 00173 m_lockerInfo.insert(typename IdMap::value_type(id, lockerInfo)); 00174 00175 ++m_numReaders; 00176 } 00177 00179 template <typename IdT, typename CompareT> 00180 void 00181 GenericRWLockImpl<IdT, CompareT>::releaseReadLock(const IdT id) 00182 { 00183 NonRecursiveMutexLock l(m_guard); 00184 00185 typename IdMap::iterator pInfo = m_lockerInfo.find(id); 00186 00187 if (pInfo == m_lockerInfo.end() || !pInfo->second.isReader()) 00188 { 00189 BLOCXX_THROW(GenericRWLockImplException, "Cannot release a read lock when no read lock is held"); 00190 } 00191 00192 LockerInfo& info(pInfo->second); 00193 --info.readCount; 00194 00195 if (!info.isWriter() && !info.isReader()) 00196 { 00197 --m_numReaders; 00198 if (m_numReaders == 0) 00199 { 00200 // This needs to wake them all up. In the case where one thread is waiting to upgrade a read to a write lock 00201 // and others are waiting to get a write lock, we have to wake up the thread trying to upgrade. 00202 m_waiting_writers.notifyAll(); 00203 } 00204 m_lockerInfo.erase(pInfo); 00205 } 00206 } 00207 00209 template <typename IdT, typename CompareT> 00210 void 00211 GenericRWLockImpl<IdT, CompareT>::acquireWriteLock(const IdT id, const Timeout& timeout) 00212 { 00213 // 7 cases: 00214 // 1. No id has the lock 00215 // Get the lock 00216 // 2. This id has the write lock 00217 // Increment the lock count 00218 // 3. Another id has the write lock & other ids may be waiting for read and/or write locks. 00219 // Block until the lock is acquired. 00220 // 4. Only this id has a read lock 00221 // Increment the write lock count . 00222 // 5. >0 other ids have the read lock & other ids may be waiting for write locks. 00223 // Block until the write lock is acquired. 00224 // 6. This id and other ids have the read lock 00225 // Block new readers and writers and wait until existing readers finish. 00226 // 7. This id and other ids have the read lock and one of the other ids has requested a write lock. 00227 // Throw an exception. 00228 00229 TimeoutTimer timer(timeout); 00230 00231 NonRecursiveMutexLock l(m_guard); 00232 00233 typename IdMap::iterator pInfo = m_lockerInfo.find(id); 00234 if (pInfo != m_lockerInfo.end()) 00235 { 00236 // This id already has some sort of lock 00237 LockerInfo& ti(pInfo->second); 00238 BLOCXX_ASSERT(ti.isReader() || ti.isWriter()); 00239 00240 if (!ti.isWriter()) 00241 { 00242 // The id is upgrading 00243 00244 BLOCXX_ASSERT(m_numWriters == 0 || m_numWriters == 1); 00245 if (m_numWriters == 1) 00246 { 00247 // another id beat us to upgrading the write lock. Throw an exception. 00248 BLOCXX_THROW(DeadlockException, "Upgrading read lock to a write lock failed, another upgrade is already in progress."); 00249 } 00250 00251 // switch from being a reader to a writer 00252 --m_numReaders; 00253 // mark us as a writer, this will prevent other ids from becoming a writer 00254 ++m_numWriters; 00255 00256 // This thread isn't the only reader. Wait for others to finish. 00257 while (m_numReaders != 0) 00258 { 00259 // stop new readers - inside while loop, because it may get reset by other ids releasing locks. 00260 m_canRead = false; 00261 00262 if (!m_waiting_writers.timedWait(l, timer.asAbsoluteTimeout())) 00263 { 00264 // undo changes 00265 ++m_numReaders; 00266 --m_numWriters; 00267 m_canRead = true; 00268 if (m_numWriters == 0) 00269 { 00270 m_waiting_readers.notifyAll(); 00271 } 00272 BLOCXX_THROW(TimeoutException, "Timeout while waiting for write lock."); 00273 } 00274 } 00275 } 00276 ++ti.writeCount; 00277 00278 } 00279 else 00280 { 00281 // This id doesn't have any lock 00282 00283 while (m_numReaders != 0 || m_numWriters != 0) 00284 { 00285 // stop new readers 00286 m_canRead = false; 00287 00288 if (!m_waiting_writers.timedWait(l, timer.asAbsoluteTimeout())) 00289 { 00290 m_canRead = true; 00291 if (m_numWriters == 0) 00292 { 00293 m_waiting_readers.notifyAll(); 00294 } 00295 BLOCXX_THROW(TimeoutException, "Timeout while waiting for write lock."); 00296 } 00297 } 00298 00299 LockerInfo ti; 00300 ti.readCount = 0; 00301 ti.writeCount = 1; 00302 m_lockerInfo.insert(typename IdMap::value_type(id, ti)); 00303 ++m_numWriters; 00304 m_canRead = false; 00305 } 00306 00307 } 00308 00310 template <typename IdT, typename CompareT> 00311 void 00312 GenericRWLockImpl<IdT, CompareT>::releaseWriteLock(const IdT id) 00313 { 00314 NonRecursiveMutexLock l(m_guard); 00315 00316 typename IdMap::iterator pInfo = m_lockerInfo.find(id); 00317 00318 if (pInfo == m_lockerInfo.end() || !pInfo->second.isWriter()) 00319 { 00320 BLOCXX_THROW(GenericRWLockImplException, "Cannot release a write lock when no write lock is held"); 00321 } 00322 00323 LockerInfo& ti(pInfo->second); 00324 00325 BLOCXX_ASSERT(ti.isWriter()); 00326 00327 --ti.writeCount; 00328 00329 if (!ti.isWriter()) 00330 { 00331 --m_numWriters; 00332 00333 BLOCXX_ASSERT(m_numWriters == 0); 00334 00335 m_canRead = true; 00336 if (ti.isReader()) 00337 { 00338 // restore reader status 00339 ++m_numReaders; 00340 } 00341 else 00342 { 00343 // This id no longer holds locks. 00344 m_waiting_writers.notifyOne(); 00345 m_lockerInfo.erase(pInfo); 00346 } 00347 m_waiting_readers.notifyAll(); 00348 } 00349 } 00350 00351 00352 } // end namespace BLOCXX_NAMESPACE 00353 00354 #endif