1 /*********************************************************************
2 CReaderWriterLock: A simple and fast reader-writer lock class in C++
3 has characters of .NET ReaderWriterLock class
4 Copyright (C) 2006 Quynh Nguyen Huu
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 Email questions, comments or suggestions to quynhnguyenhuu@gmail.com
17 *********************************************************************/
21 #include "ReaderWriterLock.h"
23 /////////////////////////////////////////////////////////////////
24 // Following macros make this file can be used in non-MFC project
26 // Define ASSERT macro
27 # define ASSERT _ASSERT
29 // Define VERIFY macro
31 # define VERIFY ASSERT
32 # define DEBUG_ONLY(f) ((void)(f))
34 # define VERIFY(f) ((void)(f))
35 # define DEBUG_ONLY(f)
39 ///////////////////////////////////////////////////////
40 // CReaderWriterLockNonReentrance implementation
42 CReaderWriterLockNonReentrance::CReaderWriterLockNonReentrance()
44 SecureZeroMemory(this, sizeof(*this));
45 #if (_WIN32_WINNT >= 0x0403)
46 InitializeCriticalSectionAndSpinCount(&m_cs
, READER_WRITER_SPIN_COUNT
);
48 InitializeCriticalSection(&m_cs
);
52 CReaderWriterLockNonReentrance::~CReaderWriterLockNonReentrance()
54 _ASSERT( (NULL
== m_hSafeToReadEvent
) &&
55 (NULL
== m_hSafeToWriteEvent
) );
56 DeleteCriticalSection(&m_cs
);
59 bool CReaderWriterLockNonReentrance::_ReaderWait(DWORD dwTimeout
) throw()
63 ++m_iNumOfReaderWaiting
;
64 if(NULL
== m_hSafeToReadEvent
)
66 m_hSafeToReadEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
69 if(INFINITE
== dwTimeout
) // INFINITE is a special value
74 WaitForSingleObject(m_hSafeToReadEvent
, INFINITE
);
75 // There might be one or more Writers entered, that's
76 // why we need DO-WHILE loop here
79 while(0 != m_iNumOfWriter
);
81 ++m_iNumOfReaderEntered
;
88 DWORD
const dwBeginTime
= GetTickCount();
89 DWORD dwConsumedTime
= 0;
93 blCanRead
= (WAIT_OBJECT_0
== WaitForSingleObject(m_hSafeToReadEvent
,
94 dwTimeout
- dwConsumedTime
));
98 if(0 == m_iNumOfWriter
)
100 // Regardless timeout or not, there is no Writer
101 // So it's safe to be Reader right now
102 ++m_iNumOfReaderEntered
;
107 if(FALSE
== blCanRead
)
109 // Timeout after waiting
113 // There are some Writers have just entered
114 // So leave CS and prepare to try again
117 dwConsumedTime
= GetTickCount() - dwBeginTime
;
118 if(dwConsumedTime
> dwTimeout
)
120 // Don't worry why the code here looks stupid
121 // Because this case rarely happens, it's better
122 // to optimize code for the usual case
130 if(0==--m_iNumOfReaderWaiting
)
132 CloseHandle(m_hSafeToReadEvent
);
133 m_hSafeToReadEvent
= NULL
;
139 void CReaderWriterLockNonReentrance::_ReaderRelease()
141 INT _iNumOfReaderEntered
= --m_iNumOfReaderEntered
;
142 _ASSERT(0 <= _iNumOfReaderEntered
);
144 if( (0 == _iNumOfReaderEntered
) &&
145 (NULL
!= m_hSafeToWriteEvent
) )
147 SetEvent(m_hSafeToWriteEvent
);
151 bool CReaderWriterLockNonReentrance::_WriterWaitAndLeaveCSIfSuccess(DWORD dwTimeout
)
154 _ASSERT(0 != dwTimeout
);
156 // Increase Writer-counter & reset Reader-event if necessary
157 INT _iNumOfWriter
= ++m_iNumOfWriter
;
158 if( (1 == _iNumOfWriter
) && (NULL
!= m_hSafeToReadEvent
) )
160 ResetEvent(m_hSafeToReadEvent
);
163 if(NULL
== m_hSafeToWriteEvent
)
165 m_hSafeToWriteEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
169 bool blCanWrite
= (WAIT_OBJECT_0
== WaitForSingleObject(m_hSafeToWriteEvent
, dwTimeout
));
170 if(FALSE
== blCanWrite
)
172 // Undo what we changed after timeout
174 if(0 == --m_iNumOfWriter
)
176 CloseHandle(m_hSafeToWriteEvent
);
177 m_hSafeToWriteEvent
= NULL
;
179 if(0 == m_iNumOfReaderEntered
)
181 // Although it was timeout, it's still safe to be writer now
186 else if(m_hSafeToReadEvent
)
188 SetEvent(m_hSafeToReadEvent
);
195 bool CReaderWriterLockNonReentrance::_UpgradeToWriterLockAndLeaveCS(DWORD dwTimeout
) throw()
197 _ASSERT(m_iNumOfReaderEntered
> 0);
205 --m_iNumOfReaderEntered
;
206 bool blCanWrite
= _WriterWaitAndLeaveCSIfSuccess(dwTimeout
);
207 if(FALSE
== blCanWrite
)
209 // Now analyze why it was failed to have suitable action
210 if(0 == m_iNumOfWriter
)
212 _ASSERT(0 < m_iNumOfReaderEntered
);
213 // There are some readers still owning the lock
214 // It's safe to be a reader again after failure
215 ++m_iNumOfReaderEntered
;
219 // Reach to here, it's NOT safe to be a reader immediately
220 _ReaderWait(INFINITE
);
221 if(1 == m_iNumOfReaderEntered
)
223 // After wait, now it's safe to be writer
224 _ASSERT(0 == m_iNumOfWriter
);
225 m_iNumOfReaderEntered
= 0;
236 void CReaderWriterLockNonReentrance::_WriterRelease(bool blDowngrade
)
238 _ASSERT(0 == m_iNumOfReaderEntered
);
242 ++m_iNumOfReaderEntered
;
245 if(0 == --m_iNumOfWriter
)
247 if(NULL
!= m_hSafeToWriteEvent
)
249 CloseHandle(m_hSafeToWriteEvent
);
250 m_hSafeToWriteEvent
= NULL
;
253 if(m_hSafeToReadEvent
)
255 SetEvent(m_hSafeToReadEvent
);
260 //////////////////////////////////////////////////////////////////////////
261 // Some WRITERs are queued
262 _ASSERT( (0 < m_iNumOfWriter
) && (NULL
!= m_hSafeToWriteEvent
));
264 if(FALSE
== blDowngrade
)
266 SetEvent(m_hSafeToWriteEvent
);
271 bool CReaderWriterLockNonReentrance::AcquireReaderLock(DWORD dwTimeout
)
276 if(0 == m_iNumOfWriter
)
278 // Enter successful without wait
279 ++m_iNumOfReaderEntered
;
284 blCanRead
= (dwTimeout
)? _ReaderWait(dwTimeout
) : FALSE
;
291 void CReaderWriterLockNonReentrance::ReleaseReaderLock()
298 bool CReaderWriterLockNonReentrance::AcquireWriterLock(DWORD dwTimeout
)
303 if(0 == (m_iNumOfWriter
| m_iNumOfReaderEntered
))
308 else if(0 == dwTimeout
)
314 blCanWrite
= _WriterWaitAndLeaveCSIfSuccess(dwTimeout
);
325 void CReaderWriterLockNonReentrance::ReleaseWriterLock()
328 _WriterRelease(FALSE
);
332 void CReaderWriterLockNonReentrance::DowngradeFromWriterLock()
335 _WriterRelease(TRUE
);
339 bool CReaderWriterLockNonReentrance::UpgradeToWriterLock(DWORD dwTimeout
) throw()
342 return _UpgradeToWriterLockAndLeaveCS(dwTimeout
);
345 // END CReaderWriterLockNonReentrance implementation
346 ///////////////////////////////////////////////////////
348 ///////////////////////////////////////////////////////
349 // CReaderWriterLock implementation
351 #define READER_RECURRENCE_UNIT 0x00000001
352 #define READER_RECURRENCE_MASK 0x0000FFFF
353 #define WRITER_RECURRENCE_UNIT 0x00010000
355 CReaderWriterLock::CReaderWriterLock()
359 CReaderWriterLock::~CReaderWriterLock()
363 bool CReaderWriterLock::AcquireReaderLock(DWORD dwTimeout
)
365 const DWORD dwCurrentThreadId
= GetCurrentThreadId();
368 CMapThreadToState::iterator ite
= m_map
.find(dwCurrentThreadId
);
370 if(ite
!= m_map
.end())
372 //////////////////////////////////////////////////////////////////////////
373 // Current thread was already a WRITER or READER
374 _ASSERT(0 < ite
->second
);
375 ite
->second
+= READER_RECURRENCE_UNIT
;
381 if(0 == m_impl
.m_iNumOfWriter
)
383 // There is NO WRITER on this RW object
384 // Current thread is going to be a READER
385 ++m_impl
.m_iNumOfReaderEntered
;
386 m_map
.insert(std::make_pair(dwCurrentThreadId
, READER_RECURRENCE_UNIT
));
398 bool blCanRead
= m_impl
._ReaderWait(dwTimeout
);
401 m_map
.insert(std::make_pair(dwCurrentThreadId
, READER_RECURRENCE_UNIT
));
408 void CReaderWriterLock::ReleaseReaderLock()
410 const DWORD dwCurrentThreadId
= GetCurrentThreadId();
413 CMapThreadToState::iterator ite
= m_map
.find(dwCurrentThreadId
);
414 _ASSERT( (ite
!= m_map
.end()) && (READER_RECURRENCE_MASK
& ite
->second
));
416 const DWORD dwThreadState
= (ite
->second
-= READER_RECURRENCE_UNIT
);
417 if(0 == dwThreadState
)
420 m_impl
._ReaderRelease();
425 bool CReaderWriterLock::AcquireWriterLock(DWORD dwTimeout
)
427 const DWORD dwCurrentThreadId
= GetCurrentThreadId();
431 CMapThreadToState::iterator ite
= m_map
.find(dwCurrentThreadId
);
433 if(ite
!= m_map
.end())
435 _ASSERT(0 < ite
->second
);
437 if(ite
->second
>= WRITER_RECURRENCE_UNIT
)
439 // Current thread was already a WRITER
440 ite
->second
+= WRITER_RECURRENCE_UNIT
;
445 // Current thread was already a READER
446 _ASSERT(1 <= m_impl
.m_iNumOfReaderEntered
);
447 if(1 == m_impl
.m_iNumOfReaderEntered
)
449 // This object is owned by ONLY current thread for READ
450 // There might be some threads queued to be WRITERs
451 // but for effectiveness (higher throughput), we allow current
452 // thread upgrading to be WRITER right now
453 m_impl
.m_iNumOfReaderEntered
= 0;
454 ++m_impl
.m_iNumOfWriter
;
455 ite
->second
+= WRITER_RECURRENCE_UNIT
;
460 // Try upgrading from reader to writer
461 blCanWrite
= m_impl
._UpgradeToWriterLockAndLeaveCS(dwTimeout
);
465 ite
= m_map
.find(dwCurrentThreadId
);
466 ite
->second
+= WRITER_RECURRENCE_UNIT
;
472 if(0 == (m_impl
.m_iNumOfWriter
| m_impl
.m_iNumOfReaderEntered
))
474 // This RW object is not owned by any thread
475 // --> it's safe to make this thread to be WRITER
476 ++m_impl
.m_iNumOfWriter
;
477 m_map
.insert(std::make_pair(dwCurrentThreadId
, WRITER_RECURRENCE_UNIT
));
488 blCanWrite
= m_impl
._WriterWaitAndLeaveCSIfSuccess(dwTimeout
);
492 m_map
.insert(std::make_pair(dwCurrentThreadId
, WRITER_RECURRENCE_UNIT
));
500 void CReaderWriterLock::ReleaseWriterLock()
502 const DWORD dwCurrentThreadId
= GetCurrentThreadId();
505 CMapThreadToState::iterator ite
= m_map
.find(dwCurrentThreadId
);
506 _ASSERT( (ite
!= m_map
.end()) && (WRITER_RECURRENCE_UNIT
<= ite
->second
));
508 const DWORD dwThreadState
= (ite
->second
-= WRITER_RECURRENCE_UNIT
);
509 if(0 == dwThreadState
)
512 m_impl
._WriterRelease(FALSE
);
514 else if (WRITER_RECURRENCE_UNIT
> dwThreadState
)
516 // Down-grading from writer to reader
517 m_impl
._WriterRelease(TRUE
);
522 void CReaderWriterLock::ReleaseAllLocks()
524 const DWORD dwCurrentThreadId
= GetCurrentThreadId();
527 CMapThreadToState::iterator ite
= m_map
.find(dwCurrentThreadId
);
528 if(ite
!= m_map
.end())
530 const DWORD dwThreadState
= ite
->second
;
532 if(WRITER_RECURRENCE_UNIT
<= dwThreadState
)
534 m_impl
._WriterRelease(FALSE
);
538 _ASSERT(0 < dwThreadState
);
539 m_impl
._ReaderRelease();
545 DWORD
CReaderWriterLock::GetCurrentThreadStatus() const throw()
548 const DWORD dwCurrentThreadId
= GetCurrentThreadId();
551 CMapThreadToState::const_iterator ite
= m_map
.find(dwCurrentThreadId
);
552 if(ite
!= m_map
.end())
554 dwThreadState
= ite
->second
;
556 _ASSERT(dwThreadState
> 0);
564 return dwThreadState
;
567 void CReaderWriterLock::GetCurrentThreadStatus(DWORD
* lpdwReaderLockCounter
,
568 DWORD
* lpdwWriterLockCounter
) const
570 const DWORD dwThreadState
= GetCurrentThreadStatus();
572 if(NULL
!= lpdwReaderLockCounter
)
574 *lpdwReaderLockCounter
= (dwThreadState
& READER_RECURRENCE_MASK
);
577 if(NULL
!= lpdwWriterLockCounter
)
579 *lpdwWriterLockCounter
= (dwThreadState
/ WRITER_RECURRENCE_UNIT
);