00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00028 #ifndef OW32_ReadWriteLock_h
00029 #define OW32_ReadWriteLock_h
00030
00031 #include <OW32/SyncObjects.h>
00032 #include <cstddef>
00033 #include <assert.h>
00034
00035 namespace OW32
00036 {
00037
00052 class OW32_LIB_EXPORT CReadWriteLock
00053 {
00054 public:
00058 CReadWriteLock() :
00059 m_activeReaders(0),
00060 m_writerID(0),
00061 m_writerRecursionCount(0),
00062 m_pendingReaders(0),
00063 m_pendingWriters(0),
00064 m_freeToRead(TRUE)
00065 {
00066 }
00067
00073 CSyncObject& GetWriteLock()
00074 {
00075 return m_writeLock;
00076 }
00077
00083 CSyncObject& GetReadLock()
00084 {
00085 return m_readLock;
00086 }
00087
00088 private:
00095 BOOL readLock(DWORD timeout)
00096 {
00097 DWORD dwStart = GetTickCount();
00098 DWORD pendIncrement = 1;
00099 DWORD myID = ::GetCurrentThreadId();
00100 for (;;)
00101 {
00102 bool needReset = false;
00103
00104 {
00105 CSingleLock lock(m_stateLock);
00106
00107
00108 if (m_pendingReaders == 0xffffffff || m_activeReaders == 0xffffffff)
00109 {
00110 ::SetLastError(ERROR_BUSY);
00111 return FALSE;
00112 }
00113
00114
00115
00116
00117 if (m_writerRecursionCount > 0 && m_writerID == myID)
00118 {
00119 ++m_writerRecursionCount;
00120 return TRUE;
00121 }
00122
00123 m_pendingReaders += pendIncrement;
00124 pendIncrement = 0;
00125
00126 if (m_writerRecursionCount == 0 && m_pendingWriters == 0)
00127 {
00128
00129 ++m_activeReaders;
00130
00131
00132
00133
00134
00135
00136
00137 if (--m_pendingReaders == 0 && needReset)
00138 {
00139 m_freeToRead.Reset();
00140 }
00141 return TRUE;
00142 }
00143 }
00144
00145
00146 DWORD timeoutRemaining = INFINITE;
00147 if (timeout != INFINITE)
00148 {
00149 timeoutRemaining = GetTickCount() - dwStart;
00150 if (timeoutRemaining > timeout)
00151 timeoutRemaining = 0;
00152 #if 0
00153 printf("sleep for %lu\n", timeoutRemaining);
00154 #endif
00155 }
00156 if (!m_freeToRead.Lock(timeoutRemaining))
00157 break;
00158 needReset = true;
00159 }
00160
00161 if (pendIncrement == 0)
00162 {
00163 CSingleLock lock(m_stateLock);
00164
00165
00166
00167 if (--m_pendingReaders == 0)
00168 {
00169 m_freeToRead.Reset();
00170 }
00171 }
00172 ::SetLastError(ERROR_TIMEOUT);
00173 return FALSE;
00174 }
00175
00179 void readUnlock()
00180 {
00181 CSingleLock lock(m_stateLock);
00182
00183
00184 if (m_writerRecursionCount > 0)
00185 {
00186 writeUnlockLocked();
00187 return;
00188 }
00189
00190
00191 assert(m_writerRecursionCount == 0);
00192 assert(m_activeReaders > 0);
00193
00194 --m_activeReaders;
00195 if (m_activeReaders == 0 && m_pendingWriters > 0)
00196 {
00197
00198 m_freeToWrite.Set();
00199 }
00200
00201 }
00202
00206 class OW32_LIB_EXPORT CReadLock : public CSyncObject
00207 {
00208 private:
00210 CReadLock(const CReadLock&);
00212 CReadLock& operator=(const CReadLock&);
00213
00214 public:
00218 CReadLock()
00219 {
00220 }
00221
00228 virtual BOOL Lock(DWORD timeout)
00229 {
00230 CReadWriteLock& parent = *(CReadWriteLock *)((const char *)this-offsetof(CReadWriteLock, m_readLock));
00231 return parent.readLock(timeout);
00232 }
00233
00237 virtual void Unlock()
00238 {
00239 CReadWriteLock& parent = *(CReadWriteLock *)((const char *)this-offsetof(CReadWriteLock, m_readLock));
00240 parent.readUnlock();
00241 }
00242 };
00243 friend CReadLock;
00244
00251 BOOL writeLock(DWORD timeout)
00252 {
00253 DWORD dwStart = GetTickCount();
00254 DWORD pendIncrement = 1;
00255 DWORD myID = GetCurrentThreadId();
00256 for (;;)
00257 {
00258 {
00259 CSingleLock lock(m_stateLock);
00260
00261
00262 if (m_pendingWriters == 0xffffffff)
00263 {
00264 ::SetLastError(ERROR_BUSY);
00265 return FALSE;
00266 }
00267
00268 m_pendingWriters += pendIncrement;
00269 pendIncrement = 0;
00270
00271
00272 if (m_activeReaders == 0)
00273 {
00274
00275 if (m_writerRecursionCount == 0 || m_writerID == myID)
00276 {
00277
00278 --m_pendingWriters;
00279 m_writerID = myID;
00280 ++m_writerRecursionCount;
00281
00282
00283
00284
00285
00286
00287
00288 if (m_pendingReaders > 0)
00289 m_freeToRead.Reset();
00290 return TRUE;
00291 }
00292
00293 }
00294 }
00295
00296
00297 DWORD timeoutRemaining = INFINITE;
00298 if (timeout != INFINITE)
00299 {
00300 timeoutRemaining = GetTickCount() - dwStart;
00301 if (timeoutRemaining > timeout)
00302 timeoutRemaining = 0;
00303 }
00304
00305 if (!m_freeToWrite.Lock(timeoutRemaining))
00306 break;
00307 }
00308
00309
00310 if (pendIncrement == 0)
00311 {
00312 CSingleLock lock(m_stateLock);
00313 if (--m_pendingWriters == 0 && m_pendingReaders > 0)
00314 {
00315
00316 m_freeToRead.Set();
00317 }
00318 }
00319 ::SetLastError(ERROR_TIMEOUT);
00320 return FALSE;
00321 }
00322
00326 void writeUnlock()
00327 {
00328 CSingleLock lock(m_stateLock);
00329
00330 writeUnlockLocked();
00331
00332 }
00333
00340 void writeUnlockLocked()
00341 {
00342
00343 assert(m_writerID == GetCurrentThreadId());
00344 assert(m_writerRecursionCount > 0);
00345 assert(m_activeReaders == 0);
00346
00347
00348 if (--m_writerRecursionCount == 0)
00349 {
00350
00351 if (m_pendingWriters != 0)
00352 {
00353
00354 m_freeToWrite.Set();
00355 }
00356 else if (m_pendingReaders != 0)
00357 {
00358
00359 m_freeToRead.Set();
00360 }
00361 }
00362 }
00363
00367 class OW32_LIB_EXPORT CWriteLock : public CSyncObject
00368 {
00369 private:
00371 CWriteLock(const CWriteLock&);
00373 CWriteLock& operator=(const CWriteLock&);
00374
00375 public:
00379 CWriteLock()
00380 {
00381 }
00382
00389 virtual BOOL Lock(DWORD timeout)
00390 {
00391 CReadWriteLock& parent = *(CReadWriteLock *)((const char *)this-offsetof(CReadWriteLock, m_writeLock));
00392 return parent.writeLock(timeout);
00393 }
00394
00398 virtual void Unlock()
00399 {
00400 CReadWriteLock& parent = *(CReadWriteLock *)((const char *)this-offsetof(CReadWriteLock, m_writeLock));
00401 parent.writeUnlock();
00402 }
00403 };
00404 friend CWriteLock;
00405
00407 CReadWriteLock(const CReadWriteLock&);
00409 CReadWriteLock& operator=(const CReadWriteLock&);
00410
00412 CCriticalSection m_stateLock;
00414 DWORD m_pendingReaders;
00416 DWORD m_activeReaders;
00418 DWORD m_pendingWriters;
00420 DWORD m_writerID;
00422 DWORD m_writerRecursionCount;
00424 CEvent m_freeToWrite;
00426 CEvent m_freeToRead;
00427
00429 CReadLock m_readLock;
00431 CWriteLock m_writeLock;
00432 };
00433
00434 #ifdef _MSC_VER
00435 #pragma warning(default: 4251)
00436 #endif
00437
00438 }
00439
00440 #endif // OW32_ReadWriteLock_h