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 *********************************************************************/
19 /*********************************************************************
21 This implementation is inspired by System.Threading.ReaderWriterLock in
22 .NET framework. Following are some important statements I excerpted
23 (with some words modified) from .NET document.
25 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemThreadingReaderWriterLockClassTopic.asp
27 "ReaderWriterLock is used to synchronize access to a resource.
28 At any given time, it allows either concurrent read access for
29 multiple threads, or write access for a single thread.
30 In a situation where a resource is changed infrequently, a
31 ReaderWriterLock provides better throughput than a simple
32 one-at-a-time lock, such as CriticalSection or Mutex.
34 This library works best where most accesses are reads, while
35 writes are infrequent and of short duration.
37 While a writer is waiting for active reader locks to be released,
38 threads requesting new reader locks will have to wait in the reader
39 queue. Their requests are not granted, even though they could share
40 concurrent access with existing reader-lock holders; this helps
41 protect writers against indefinite blockage by readers..."
42 *********************************************************************/
49 #if (_WIN32_WINNT >= 0x0403)
50 //////////////////////////////////////////////////////////////////
51 // On multiprocessor systems, this value define number of times
52 // that a thread tries to spin before actually performing a wait
53 // operation (see InitializeCriticalSectionAndSpinCount API)
54 #ifndef READER_WRITER_SPIN_COUNT
55 #define READER_WRITER_SPIN_COUNT 400
56 #endif READER_WRITER_SPIN_COUNT
60 class CReaderWriterLock
;
62 //////////////////////////////////////////////////////////////////
63 // CReaderWriterLockNonReentrance class
64 // NOTE: This class doesn't support reentrance & lock escalation.
65 // May be deadlock in one of following situations:
66 // 1) Call AcquireReaderLock twice (reentrance)
67 // --> Revise execution flow.
68 // 2) Call AcquireWriterLock twice (reentrance)
69 // --> Revise execution flow.
70 // 3) Call AcquireReaderLock then AcquireWriterLock (lock escalation)
71 // --> Use ReleaseReaderAndAcquireWriterLock method
72 // 4) Call AcquireWriterLock then AcquireReaderLock (lock deescalation)
73 // --> Use DowngradeFromWriterLock method
74 class CReaderWriterLockNonReentrance
77 CReaderWriterLockNonReentrance() throw();
78 ~CReaderWriterLockNonReentrance() throw();
79 bool AcquireReaderLock(DWORD dwTimeout
= INFINITE
) throw();
80 void ReleaseReaderLock() throw();
81 bool AcquireWriterLock(DWORD dwTimeout
= INFINITE
) throw();
82 void ReleaseWriterLock() throw();
83 bool TryAcquireReaderLock() throw();
84 bool TryAcquireWriterLock() throw();
85 void DowngradeFromWriterLock() throw();
87 // When a thread calls UpgradeToWriterLock, the reader lock is released,
88 // and the thread goes to the end of the writer queue. Thus, other threads
89 // might write to resources before this method returns
90 bool UpgradeToWriterLock(DWORD dwTimeout
= INFINITE
) throw();
92 // A critical section to guard all the other members
93 mutable CRITICAL_SECTION m_cs
;
94 // Auto-reset event, will be dynamically created/destroyed on demand
95 volatile HANDLE m_hSafeToWriteEvent
;
96 // Manual-reset event, will be dynamically created/destroyed on demand
97 volatile HANDLE m_hSafeToReadEvent
;
98 // Total number of writers on this object
99 volatile INT m_iNumOfWriter
;
100 // Total number of readers have already owned this object
101 volatile INT m_iNumOfReaderEntered
;
102 // Total number of readers are waiting to be owners of this object
103 volatile INT m_iNumOfReaderWaiting
;
104 // Internal/Real implementation
105 void EnterCS() const throw();
106 void LeaveCS() const throw();
107 bool _ReaderWait(DWORD dwTimeout
) throw();
108 bool _WriterWaitAndLeaveCSIfSuccess(DWORD dwTimeout
) throw();
109 bool _UpgradeToWriterLockAndLeaveCS(DWORD dwTimeout
) throw();
110 void _ReaderRelease() throw();
111 void _WriterRelease(bool blDowngrade
) throw();
113 friend CReaderWriterLock
;
116 //////////////////////////////////////////////////////////////////
117 // CReaderWriterLock class
118 // This class supports reentrance & lock escalation
119 class CReaderWriterLock
123 ~CReaderWriterLock();
125 bool AcquireReaderLock(DWORD dwTimeout
= INFINITE
) throw();
126 void ReleaseReaderLock() throw();
128 // If current thread was already a reader
129 // it will be upgraded to be writer automatically.
130 // BE CAREFUL! Other threads might write to the resource
131 // before current thread is successfully upgraded.
132 bool AcquireWriterLock(DWORD dwTimeout
= INFINITE
) throw();
133 void ReleaseWriterLock() throw();
135 // Regardless of how many times current thread acquired reader
136 // or writer locks, a call to this method will release all locks.
137 // After that, any call to ReleaseWriterLock or ReleaseReaderLock
138 // will raise exception in DEBUG mode.
139 void ReleaseAllLocks() throw();
141 // Query thread's status
142 DWORD
GetCurrentThreadStatus() const throw();
143 void GetCurrentThreadStatus(DWORD
* lpdwReaderLockCounter
,
144 DWORD
* lpdwWriterLockCounter
) const throw();
146 CReaderWriterLockNonReentrance m_impl
;
148 typedef std::map
<DWORD
,DWORD
> CMapThreadToState
;
149 CMapThreadToState m_map
;
152 //////////////////////////////////////////////////////////////////
153 // CAutoReadLockT & CAutoWriteLockT classes
154 // Couple of template helper classes which would let one acquire a lock
155 // in a body of code and not have to worry about explicitly releasing
156 // that lock if an exception is encountered in that piece of code or
157 // if there are multiple return points out of that piece.
163 CAutoReadLockT(T
& objLock
) throw() : m_lock(objLock
)
165 m_lock
.AcquireReaderLock();
167 ~CAutoReadLockT() throw()
169 m_lock
.ReleaseReaderLock();
176 class CAutoWriteLockT
179 CAutoWriteLockT(T
& objLock
) throw() : m_lock(objLock
)
181 m_lock
.AcquireWriterLock();
183 ~CAutoWriteLockT() throw()
185 m_lock
.ReleaseWriterLock();
192 class CAutoReadWeakLockT
195 CAutoReadWeakLockT(T
& objLock
, DWORD timeout
= 1) throw() : m_lock(objLock
)
197 isAcquired
= m_lock
.AcquireReaderLock(timeout
);
199 ~CAutoReadWeakLockT() throw()
202 m_lock
.ReleaseReaderLock();
204 bool IsAcquired() const
214 class CAutoWriteWeakLockT
217 CAutoWriteWeakLockT(T
& objLock
, DWORD timeout
= 1) throw() : m_lock(objLock
)
219 isAcquired
= m_lock
.AcquireWriterLock(timeout
);
221 ~CAutoWriteWeakLockT() throw()
229 bool IsAcquired() const
241 m_lock
.ReleaseWriterLock();
247 //////////////////////////////////////////////////////////////////
248 // Instances of above template helper classes
250 typedef CAutoReadLockT
<CReaderWriterLock
> CAutoReadLock
;
251 typedef CAutoWriteLockT
<CReaderWriterLock
> CAutoWriteLock
;
252 typedef CAutoReadWeakLockT
<CReaderWriterLock
> CAutoReadWeakLock
;
253 typedef CAutoWriteWeakLockT
<CReaderWriterLock
> CAutoWriteWeakLock
;
255 //////////////////////////////////////////////////////////////////
259 void CReaderWriterLockNonReentrance::EnterCS() const throw() {
260 ::EnterCriticalSection(&m_cs
);
264 void CReaderWriterLockNonReentrance::LeaveCS() const throw(){
265 ::LeaveCriticalSection(&m_cs
);