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() noexcept
;
78 ~CReaderWriterLockNonReentrance() noexcept
;
79 bool AcquireReaderLock(DWORD dwTimeout
= INFINITE
) noexcept
;
80 void ReleaseReaderLock() noexcept
;
81 bool AcquireWriterLock(DWORD dwTimeout
= INFINITE
) noexcept
;
82 void ReleaseWriterLock() noexcept
;
83 void DowngradeFromWriterLock() noexcept
;
85 // When a thread calls UpgradeToWriterLock, the reader lock is released,
86 // and the thread goes to the end of the writer queue. Thus, other threads
87 // might write to resources before this method returns
88 bool UpgradeToWriterLock(DWORD dwTimeout
= INFINITE
) noexcept
;
90 // A critical section to guard all the other members
91 mutable CRITICAL_SECTION m_cs
;
92 // Auto-reset event, will be dynamically created/destroyed on demand
93 volatile HANDLE m_hSafeToWriteEvent
= nullptr;
94 // Manual-reset event, will be dynamically created/destroyed on demand
95 volatile HANDLE m_hSafeToReadEvent
= nullptr;
96 // Total number of writers on this object
97 volatile INT m_iNumOfWriter
= 0;
98 // Total number of readers have already owned this object
99 volatile INT m_iNumOfReaderEntered
= 0;
100 // Total number of readers are waiting to be owners of this object
101 volatile INT m_iNumOfReaderWaiting
= 0;
102 // Internal/Real implementation
103 void EnterCS() const noexcept
;
104 void LeaveCS() const noexcept
;
105 bool _ReaderWait(ULONGLONG dwTimeout
) noexcept
;
106 bool _WriterWaitAndLeaveCSIfSuccess(ULONGLONG dwTimeout
) noexcept
;
107 bool _UpgradeToWriterLockAndLeaveCS(ULONGLONG dwTimeout
) noexcept
;
108 void _ReaderRelease() noexcept
;
109 void _WriterRelease(bool blDowngrade
) noexcept
;
111 friend CReaderWriterLock
;
114 //////////////////////////////////////////////////////////////////
115 // CReaderWriterLock class
116 // This class supports reentrance & lock escalation
117 class CReaderWriterLock
121 ~CReaderWriterLock();
123 bool AcquireReaderLock(DWORD dwTimeout
= INFINITE
) noexcept
;
124 void ReleaseReaderLock() noexcept
;
126 // If current thread was already a reader
127 // it will be upgraded to be writer automatically.
128 // BE CAREFUL! Other threads might write to the resource
129 // before current thread is successfully upgraded.
130 bool AcquireWriterLock(DWORD dwTimeout
= INFINITE
) noexcept
;
131 void ReleaseWriterLock() noexcept
;
133 // Regardless of how many times current thread acquired reader
134 // or writer locks, a call to this method will release all locks.
135 // After that, any call to ReleaseWriterLock or ReleaseReaderLock
136 // will raise exception in DEBUG mode.
137 void ReleaseAllLocks() noexcept
;
139 // Query thread's status
140 DWORD
GetCurrentThreadStatus() const noexcept
;
141 void GetCurrentThreadStatus(DWORD
* lpdwReaderLockCounter
,
142 DWORD
* lpdwWriterLockCounter
) const noexcept
;
144 CReaderWriterLockNonReentrance m_impl
;
146 using CMapThreadToState
= std::map
<DWORD
,DWORD
>;
147 CMapThreadToState m_map
;
150 //////////////////////////////////////////////////////////////////
151 // CAutoReadLockT & CAutoWriteLockT classes
152 // Couple of template helper classes which would let one acquire a lock
153 // in a body of code and not have to worry about explicitly releasing
154 // that lock if an exception is encountered in that piece of code or
155 // if there are multiple return points out of that piece.
161 CAutoReadLockT(T
& objLock
) noexcept
: m_lock(objLock
)
163 m_lock
.AcquireReaderLock();
165 ~CAutoReadLockT() noexcept
167 m_lock
.ReleaseReaderLock();
169 CAutoReadLockT(CAutoReadLockT
&) = delete;
170 CAutoReadLockT(CAutoReadLockT
&& that
) = default;
171 CAutoReadLockT
& operator=(const CAutoReadLockT
&) = delete;
177 class CAutoWriteLockT
180 CAutoWriteLockT(T
& objLock
) noexcept
: m_lock(objLock
)
182 m_lock
.AcquireWriterLock();
184 ~CAutoWriteLockT() noexcept
186 m_lock
.ReleaseWriterLock();
188 CAutoWriteLockT(CAutoWriteLockT
&) = delete;
189 CAutoWriteLockT(CAutoWriteLockT
&&) = default;
190 CAutoWriteLockT
& operator=(const CAutoWriteLockT
&) = delete;
196 class CAutoReadWeakLockT
199 CAutoReadWeakLockT(T
& objLock
, DWORD timeout
= 1) noexcept
: m_lock(objLock
)
201 isAcquired
= m_lock
.AcquireReaderLock(timeout
);
203 ~CAutoReadWeakLockT() noexcept
206 m_lock
.ReleaseReaderLock();
208 bool IsAcquired() const
212 CAutoReadWeakLockT(CAutoReadWeakLockT
&) = delete;
213 CAutoReadWeakLockT(CAutoReadWeakLockT
&&) = default;
214 CAutoReadWeakLockT
& operator=(const CAutoReadWeakLockT
&) = delete;
221 class CAutoWriteWeakLockT
224 CAutoWriteWeakLockT(T
& objLock
, DWORD timeout
= 1) noexcept
: m_lock(objLock
)
226 isAcquired
= m_lock
.AcquireWriterLock(timeout
);
228 ~CAutoWriteWeakLockT() noexcept
236 bool IsAcquired() const
240 CAutoWriteWeakLockT(CAutoWriteWeakLockT
&) = delete;
241 CAutoWriteWeakLockT(CAutoWriteWeakLockT
&&) = default;
242 CAutoWriteWeakLockT
& operator=(const CAutoWriteWeakLockT
&) = delete;
251 m_lock
.ReleaseWriterLock();
257 //////////////////////////////////////////////////////////////////
258 // Instances of above template helper classes
260 using CAutoReadLock
= CAutoReadLockT
<CReaderWriterLock
>;
261 using CAutoWriteLock
= CAutoWriteLockT
<CReaderWriterLock
>;
262 using CAutoReadWeakLock
= CAutoReadWeakLockT
<CReaderWriterLock
>;
263 using CAutoWriteWeakLock
= CAutoWriteWeakLockT
<CReaderWriterLock
>;
265 //////////////////////////////////////////////////////////////////
269 void CReaderWriterLockNonReentrance::EnterCS() const noexcept
{
270 ::EnterCriticalSection(&m_cs
);
274 void CReaderWriterLockNonReentrance::LeaveCS() const noexcept
{
275 ::LeaveCriticalSection(&m_cs
);