00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
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;
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
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
00155 BLOCXX_ASSERT(ti.isReader() || ti.isWriter());
00156 ++ti.readCount;
00157 return;
00158 }
00159
00160
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
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
00201
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
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
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
00237 LockerInfo& ti(pInfo->second);
00238 BLOCXX_ASSERT(ti.isReader() || ti.isWriter());
00239
00240 if (!ti.isWriter())
00241 {
00242
00243
00244 BLOCXX_ASSERT(m_numWriters == 0 || m_numWriters == 1);
00245 if (m_numWriters == 1)
00246 {
00247
00248 BLOCXX_THROW(DeadlockException, "Upgrading read lock to a write lock failed, another upgrade is already in progress.");
00249 }
00250
00251
00252 --m_numReaders;
00253
00254 ++m_numWriters;
00255
00256
00257 while (m_numReaders != 0)
00258 {
00259
00260 m_canRead = false;
00261
00262 if (!m_waiting_writers.timedWait(l, timer.asAbsoluteTimeout()))
00263 {
00264
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
00282
00283 while (m_numReaders != 0 || m_numWriters != 0)
00284 {
00285
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
00339 ++m_numReaders;
00340 }
00341 else
00342 {
00343
00344 m_waiting_writers.notifyOne();
00345 m_lockerInfo.erase(pInfo);
00346 }
00347 m_waiting_readers.notifyAll();
00348 }
00349 }
00350
00351
00352 }
00353
00354 #endif