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()
43 : m_hSafeToReadEvent(NULL
)
44 , m_hSafeToWriteEvent(NULL
)
46 , m_iNumOfReaderWaiting(0)
47 , m_iNumOfReaderEntered(0)
49 SecureZeroMemory(this, sizeof(*this));
50 #if (_WIN32_WINNT >= 0x0403)
51 InitializeCriticalSectionAndSpinCount(&m_cs
, READER_WRITER_SPIN_COUNT
);
53 InitializeCriticalSection(&m_cs
);
57 CReaderWriterLockNonReentrance::~CReaderWriterLockNonReentrance()
59 _ASSERT( (NULL
== m_hSafeToReadEvent
) &&
60 (NULL
== m_hSafeToWriteEvent
) );
61 DeleteCriticalSection(&m_cs
);
64 bool CReaderWriterLockNonReentrance::_ReaderWait(DWORD dwTimeout
) throw()
68 ++m_iNumOfReaderWaiting
;
69 if(NULL
== m_hSafeToReadEvent
)
71 m_hSafeToReadEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
74 if(INFINITE
== dwTimeout
) // INFINITE is a special value
79 WaitForSingleObject(m_hSafeToReadEvent
, INFINITE
);
80 // There might be one or more Writers entered, that's
81 // why we need DO-WHILE loop here
84 while(0 != m_iNumOfWriter
);
86 ++m_iNumOfReaderEntered
;
93 DWORD
const dwBeginTime
= GetTickCount();
94 DWORD dwConsumedTime
= 0;
98 blCanRead
= (WAIT_OBJECT_0
== WaitForSingleObject(m_hSafeToReadEvent
,
99 dwTimeout
- dwConsumedTime
));
103 if(0 == m_iNumOfWriter
)
105 // Regardless timeout or not, there is no Writer
106 // So it's safe to be Reader right now
107 ++m_iNumOfReaderEntered
;
112 if(FALSE
== blCanRead
)
114 // Timeout after waiting
118 // There are some Writers have just entered
119 // So leave CS and prepare to try again
122 dwConsumedTime
= GetTickCount() - dwBeginTime
;
123 if(dwConsumedTime
> dwTimeout
)
125 // Don't worry why the code here looks stupid
126 // Because this case rarely happens, it's better
127 // to optimize code for the usual case
135 if(0==--m_iNumOfReaderWaiting
)
137 CloseHandle(m_hSafeToReadEvent
);
138 m_hSafeToReadEvent
= NULL
;
144 void CReaderWriterLockNonReentrance::_ReaderRelease()
146 INT _iNumOfReaderEntered
= --m_iNumOfReaderEntered
;
147 _ASSERT(0 <= _iNumOfReaderEntered
);
149 if( (0 == _iNumOfReaderEntered
) &&
150 (NULL
!= m_hSafeToWriteEvent
) )
152 SetEvent(m_hSafeToWriteEvent
);
156 bool CReaderWriterLockNonReentrance::_WriterWaitAndLeaveCSIfSuccess(DWORD dwTimeout
)
159 _ASSERT(0 != dwTimeout
);
161 // Increase Writer-counter & reset Reader-event if necessary
162 INT _iNumOfWriter
= ++m_iNumOfWriter
;
163 if( (1 == _iNumOfWriter
) && (NULL
!= m_hSafeToReadEvent
) )
165 ResetEvent(m_hSafeToReadEvent
);
168 if(NULL
== m_hSafeToWriteEvent
)
170 m_hSafeToWriteEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
174 bool blCanWrite
= (WAIT_OBJECT_0
== WaitForSingleObject(m_hSafeToWriteEvent
, dwTimeout
));
175 if(FALSE
== blCanWrite
)
177 // Undo what we changed after timeout
179 if(0 == --m_iNumOfWriter
)
181 CloseHandle(m_hSafeToWriteEvent
);
182 m_hSafeToWriteEvent
= NULL
;
184 if(0 == m_iNumOfReaderEntered
)
186 // Although it was timeout, it's still safe to be writer now
191 else if(m_hSafeToReadEvent
)
193 SetEvent(m_hSafeToReadEvent
);
200 bool CReaderWriterLockNonReentrance::_UpgradeToWriterLockAndLeaveCS(DWORD dwTimeout
) throw()
202 _ASSERT(m_iNumOfReaderEntered
> 0);
210 --m_iNumOfReaderEntered
;
211 bool blCanWrite
= _WriterWaitAndLeaveCSIfSuccess(dwTimeout
);
212 if(FALSE
== blCanWrite
)
214 // Now analyze why it was failed to have suitable action
215 if(0 == m_iNumOfWriter
)
217 _ASSERT(0 < m_iNumOfReaderEntered
);
218 // There are some readers still owning the lock
219 // It's safe to be a reader again after failure
220 ++m_iNumOfReaderEntered
;
224 // Reach to here, it's NOT safe to be a reader immediately
225 _ReaderWait(INFINITE
);
226 if(1 == m_iNumOfReaderEntered
)
228 // After wait, now it's safe to be writer
229 _ASSERT(0 == m_iNumOfWriter
);
230 m_iNumOfReaderEntered
= 0;
241 void CReaderWriterLockNonReentrance::_WriterRelease(bool blDowngrade
)
243 _ASSERT(0 == m_iNumOfReaderEntered
);
247 ++m_iNumOfReaderEntered
;
250 if(0 == --m_iNumOfWriter
)
252 if(NULL
!= m_hSafeToWriteEvent
)
254 CloseHandle(m_hSafeToWriteEvent
);
255 m_hSafeToWriteEvent
= NULL
;
258 if(m_hSafeToReadEvent
)
260 SetEvent(m_hSafeToReadEvent
);
265 //////////////////////////////////////////////////////////////////////////
266 // Some WRITERs are queued
267 _ASSERT( (0 < m_iNumOfWriter
) && (NULL
!= m_hSafeToWriteEvent
));
269 if(FALSE
== blDowngrade
)
271 SetEvent(m_hSafeToWriteEvent
);
276 bool CReaderWriterLockNonReentrance::AcquireReaderLock(DWORD dwTimeout
)
281 if(0 == m_iNumOfWriter
)
283 // Enter successful without wait
284 ++m_iNumOfReaderEntered
;
289 blCanRead
= (dwTimeout
)? _ReaderWait(dwTimeout
) : FALSE
;
296 void CReaderWriterLockNonReentrance::ReleaseReaderLock()
303 bool CReaderWriterLockNonReentrance::AcquireWriterLock(DWORD dwTimeout
)
308 if(0 == (m_iNumOfWriter
| m_iNumOfReaderEntered
))
313 else if(0 == dwTimeout
)
319 blCanWrite
= _WriterWaitAndLeaveCSIfSuccess(dwTimeout
);
330 void CReaderWriterLockNonReentrance::ReleaseWriterLock()
333 _WriterRelease(FALSE
);
337 void CReaderWriterLockNonReentrance::DowngradeFromWriterLock()
340 _WriterRelease(TRUE
);
344 bool CReaderWriterLockNonReentrance::UpgradeToWriterLock(DWORD dwTimeout
) throw()
347 return _UpgradeToWriterLockAndLeaveCS(dwTimeout
);
350 // END CReaderWriterLockNonReentrance implementation
351 ///////////////////////////////////////////////////////
353 ///////////////////////////////////////////////////////
354 // CReaderWriterLock implementation
356 #define READER_RECURRENCE_UNIT 0x00000001
357 #define READER_RECURRENCE_MASK 0x0000FFFF
358 #define WRITER_RECURRENCE_UNIT 0x00010000
360 CReaderWriterLock::CReaderWriterLock()
364 CReaderWriterLock::~CReaderWriterLock()
368 bool CReaderWriterLock::AcquireReaderLock(DWORD dwTimeout
)
370 const DWORD dwCurrentThreadId
= GetCurrentThreadId();
373 CMapThreadToState::iterator ite
= m_map
.find(dwCurrentThreadId
);
375 if(ite
!= m_map
.end())
377 //////////////////////////////////////////////////////////////////////////
378 // Current thread was already a WRITER or READER
379 _ASSERT(0 < ite
->second
);
380 ite
->second
+= READER_RECURRENCE_UNIT
;
386 if(0 == m_impl
.m_iNumOfWriter
)
388 // There is NO WRITER on this RW object
389 // Current thread is going to be a READER
390 ++m_impl
.m_iNumOfReaderEntered
;
391 m_map
.insert(std::make_pair(dwCurrentThreadId
, READER_RECURRENCE_UNIT
));
403 bool blCanRead
= m_impl
._ReaderWait(dwTimeout
);
406 m_map
.insert(std::make_pair(dwCurrentThreadId
, READER_RECURRENCE_UNIT
));
413 void CReaderWriterLock::ReleaseReaderLock()
415 const DWORD dwCurrentThreadId
= GetCurrentThreadId();
418 CMapThreadToState::iterator ite
= m_map
.find(dwCurrentThreadId
);
419 _ASSERT( (ite
!= m_map
.end()) && (READER_RECURRENCE_MASK
& ite
->second
));
421 const DWORD dwThreadState
= (ite
->second
-= READER_RECURRENCE_UNIT
);
422 if(0 == dwThreadState
)
425 m_impl
._ReaderRelease();
430 bool CReaderWriterLock::AcquireWriterLock(DWORD dwTimeout
)
432 const DWORD dwCurrentThreadId
= GetCurrentThreadId();
436 CMapThreadToState::iterator ite
= m_map
.find(dwCurrentThreadId
);
438 if(ite
!= m_map
.end())
440 _ASSERT(0 < ite
->second
);
442 if(ite
->second
>= WRITER_RECURRENCE_UNIT
)
444 // Current thread was already a WRITER
445 ite
->second
+= WRITER_RECURRENCE_UNIT
;
450 // Current thread was already a READER
451 _ASSERT(1 <= m_impl
.m_iNumOfReaderEntered
);
452 if(1 == m_impl
.m_iNumOfReaderEntered
)
454 // This object is owned by ONLY current thread for READ
455 // There might be some threads queued to be WRITERs
456 // but for effectiveness (higher throughput), we allow current
457 // thread upgrading to be WRITER right now
458 m_impl
.m_iNumOfReaderEntered
= 0;
459 ++m_impl
.m_iNumOfWriter
;
460 ite
->second
+= WRITER_RECURRENCE_UNIT
;
465 // Try upgrading from reader to writer
466 blCanWrite
= m_impl
._UpgradeToWriterLockAndLeaveCS(dwTimeout
);
470 ite
= m_map
.find(dwCurrentThreadId
);
471 ite
->second
+= WRITER_RECURRENCE_UNIT
;
477 if(0 == (m_impl
.m_iNumOfWriter
| m_impl
.m_iNumOfReaderEntered
))
479 // This RW object is not owned by any thread
480 // --> it's safe to make this thread to be WRITER
481 ++m_impl
.m_iNumOfWriter
;
482 m_map
.insert(std::make_pair(dwCurrentThreadId
, WRITER_RECURRENCE_UNIT
));
493 blCanWrite
= m_impl
._WriterWaitAndLeaveCSIfSuccess(dwTimeout
);
497 m_map
.insert(std::make_pair(dwCurrentThreadId
, WRITER_RECURRENCE_UNIT
));
505 void CReaderWriterLock::ReleaseWriterLock()
507 const DWORD dwCurrentThreadId
= GetCurrentThreadId();
510 CMapThreadToState::iterator ite
= m_map
.find(dwCurrentThreadId
);
511 _ASSERT( (ite
!= m_map
.end()) && (WRITER_RECURRENCE_UNIT
<= ite
->second
));
513 const DWORD dwThreadState
= (ite
->second
-= WRITER_RECURRENCE_UNIT
);
514 if(0 == dwThreadState
)
517 m_impl
._WriterRelease(FALSE
);
519 else if (WRITER_RECURRENCE_UNIT
> dwThreadState
)
521 // Down-grading from writer to reader
522 m_impl
._WriterRelease(TRUE
);
527 void CReaderWriterLock::ReleaseAllLocks()
529 const DWORD dwCurrentThreadId
= GetCurrentThreadId();
532 CMapThreadToState::iterator ite
= m_map
.find(dwCurrentThreadId
);
533 if(ite
!= m_map
.end())
535 const DWORD dwThreadState
= ite
->second
;
537 if(WRITER_RECURRENCE_UNIT
<= dwThreadState
)
539 m_impl
._WriterRelease(FALSE
);
543 _ASSERT(0 < dwThreadState
);
544 m_impl
._ReaderRelease();
550 DWORD
CReaderWriterLock::GetCurrentThreadStatus() const throw()
553 const DWORD dwCurrentThreadId
= GetCurrentThreadId();
556 CMapThreadToState::const_iterator ite
= m_map
.find(dwCurrentThreadId
);
557 if(ite
!= m_map
.end())
559 dwThreadState
= ite
->second
;
561 _ASSERT(dwThreadState
> 0);
569 return dwThreadState
;
572 void CReaderWriterLock::GetCurrentThreadStatus(DWORD
* lpdwReaderLockCounter
,
573 DWORD
* lpdwWriterLockCounter
) const
575 const DWORD dwThreadState
= GetCurrentThreadStatus();
577 if(NULL
!= lpdwReaderLockCounter
)
579 *lpdwReaderLockCounter
= (dwThreadState
& READER_RECURRENCE_MASK
);
582 if(NULL
!= lpdwWriterLockCounter
)
584 *lpdwWriterLockCounter
= (dwThreadState
/ WRITER_RECURRENCE_UNIT
);