-
Notifications
You must be signed in to change notification settings - Fork 20
/
Copy pathReaderWriterLock.h
282 lines (242 loc) · 8.99 KB
/
ReaderWriterLock.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
/*********************************************************************
CReaderWriterLock: A simple and fast reader-writer lock class in C++
has characters of .NET ReaderWriterLock class
Copyright (C) 2006 Quynh Nguyen Huu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
Email questions, comments or suggestions to [email protected]
*********************************************************************/
/*********************************************************************
Introduction:
This implementation is inspired by System.Threading.ReaderWriterLock in
.NET framework. Following are some important statements I excerpted
(with some words modified) from .NET document.
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemThreadingReaderWriterLockClassTopic.asp
"ReaderWriterLock is used to synchronize access to a resource.
At any given time, it allows either concurrent read access for
multiple threads, or write access for a single thread.
In a situation where a resource is changed infrequently, a
ReaderWriterLock provides better throughput than a simple
one-at-a-time lock, such as CriticalSection or Mutex.
This library works best where most accesses are reads, while
writes are infrequent and of short duration.
While a writer is waiting for active reader locks to be released,
threads requesting new reader locks will have to wait in the reader
queue. Their requests are not granted, even though they could share
concurrent access with existing reader-lock holders; this helps
protect writers against indefinite blockage by readers..."
*********************************************************************/
#pragma once
#include <windows.h>
#include <map>
#if (_WIN32_WINNT >= 0x0403)
//////////////////////////////////////////////////////////////////
// On multiprocessor systems, this value define number of times
// that a thread tries to spin before actually performing a wait
// operation (see InitializeCriticalSectionAndSpinCount API)
# ifndef READER_WRITER_SPIN_COUNT
# define READER_WRITER_SPIN_COUNT 400
# endif // READER_WRITER_SPIN_COUNT
#endif // _WIN32_WINNT
// Forward reference
class CReaderWriterLock;
//////////////////////////////////////////////////////////////////
// CReaderWriterLockNonReentrance class
// NOTE: This class doesn't support reentrance & lock escalation.
// May be deadlock in one of following situations:
// 1) Call AcquireReaderLock twice (reentrance)
// --> Revise execution flow.
// 2) Call AcquireWriterLock twice (reentrance)
// --> Revise execution flow.
// 3) Call AcquireReaderLock then AcquireWriterLock (lock escalation)
// --> Use ReleaseReaderAndAcquireWriterLock method
// 4) Call AcquireWriterLock then AcquireReaderLock (lock deescalation)
// --> Use DowngradeFromWriterLock method
class CReaderWriterLockNonReentrance
{
public:
CReaderWriterLockNonReentrance();
~CReaderWriterLockNonReentrance();
bool AcquireReaderLock(DWORD dwTimeout = INFINITE);
void ReleaseReaderLock();
bool AcquireWriterLock(DWORD dwTimeout = INFINITE);
void ReleaseWriterLock();
bool TryAcquireReaderLock();
bool TryAcquireWriterLock();
void DowngradeFromWriterLock();
// When a thread calls UpgradeToWriterLock, the reader lock is released,
// and the thread goes to the end of the writer queue. Thus, other threads
// might write to resources before this method returns
bool UpgradeToWriterLock(DWORD dwTimeout = INFINITE);
protected:
// A critical section to guard all the other members
mutable CRITICAL_SECTION m_cs;
// Auto-reset event, will be dynamically created/destroyed on demand
volatile HANDLE m_hSafeToWriteEvent;
// Manual-reset event, will be dynamically created/destroyed on demand
volatile HANDLE m_hSafeToReadEvent;
// Total number of writers on this object
volatile INT m_iNumOfWriter;
// Total number of readers have already owned this object
volatile INT m_iNumOfReaderEntered;
// Total number of readers are waiting to be owners of this object
volatile INT m_iNumOfReaderWaiting;
// Internal/Real implementation
void EnterCS() const;
void LeaveCS() const;
bool _ReaderWait(DWORD dwTimeout);
bool _WriterWaitAndLeaveCSIfSuccess(DWORD dwTimeout);
bool _UpgradeToWriterLockAndLeaveCS(DWORD dwTimeout);
void _ReaderRelease();
void _WriterRelease(bool blDowngrade);
friend CReaderWriterLock;
};
//////////////////////////////////////////////////////////////////
// CReaderWriterLock class
// This class supports reentrance & lock escalation
class CReaderWriterLock
{
public:
CReaderWriterLock();
~CReaderWriterLock();
bool AcquireReaderLock(DWORD dwTimeout = INFINITE);
void ReleaseReaderLock();
// If current thread was already a reader
// it will be upgraded to be writer automatically.
// BE CAREFUL! Other threads might write to the resource
// before current thread is successfully upgraded.
bool AcquireWriterLock(DWORD dwTimeout = INFINITE);
void ReleaseWriterLock();
// Regardless of how many times current thread acquired reader
// or writer locks, a call to this method will release all locks.
// After that, any call to ReleaseWriterLock or ReleaseReaderLock
// will raise exception in DEBUG mode.
void ReleaseAllLocks();
// Query thread's status
DWORD GetCurrentThreadStatus() const;
void GetCurrentThreadStatus(DWORD* lpdwReaderLockCounter,
DWORD* lpdwWriterLockCounter) const;
protected:
CReaderWriterLockNonReentrance m_impl;
typedef std::map<DWORD, DWORD> CMapThreadToState;
CMapThreadToState m_map;
};
//////////////////////////////////////////////////////////////////
// CAutoReadLockT & CAutoWriteLockT classes
// Couple of template helper classes which would let one acquire a lock
// in a body of code and not have to worry about explicitly releasing
// that lock if an exception is encountered in that piece of code or
// if there are multiple return points out of that piece.
template <typename T>
class CAutoReadLockT
{
public:
CAutoReadLockT(T& objLock)
: m_lock(objLock)
{
m_lock.AcquireReaderLock();
}
~CAutoReadLockT()
{
m_lock.ReleaseReaderLock();
}
protected:
T& m_lock;
private:
CAutoReadLockT& operator=(const CAutoReadLockT&) = delete;
};
template <typename T>
class CAutoWriteLockT
{
public:
CAutoWriteLockT(T& objLock)
: m_lock(objLock)
{
m_lock.AcquireWriterLock();
}
~CAutoWriteLockT()
{
m_lock.ReleaseWriterLock();
}
protected:
T& m_lock;
private:
CAutoWriteLockT& operator=(const CAutoWriteLockT&) = delete;
};
template <typename T>
class CAutoReadWeakLockT
{
public:
CAutoReadWeakLockT(T& objLock, DWORD timeout = 1)
: m_lock(objLock)
{
isAcquired = m_lock.AcquireReaderLock(timeout);
}
~CAutoReadWeakLockT()
{
if (isAcquired)
m_lock.ReleaseReaderLock();
}
bool IsAcquired() const
{
return isAcquired;
}
protected:
T& m_lock;
bool isAcquired;
};
template <typename T>
class CAutoWriteWeakLockT
{
public:
CAutoWriteWeakLockT(T& objLock, DWORD timeout = 1)
: m_lock(objLock)
{
isAcquired = m_lock.AcquireWriterLock(timeout);
}
~CAutoWriteWeakLockT()
{
release();
}
void Release()
{
release();
}
bool IsAcquired() const
{
return isAcquired;
}
protected:
T& m_lock;
bool isAcquired;
void release()
{
if (isAcquired)
{
m_lock.ReleaseWriterLock();
isAcquired = false;
}
}
};
//////////////////////////////////////////////////////////////////
// Instances of above template helper classes
typedef CAutoReadLockT<CReaderWriterLock> CAutoReadLock;
typedef CAutoWriteLockT<CReaderWriterLock> CAutoWriteLock;
typedef CAutoReadWeakLockT<CReaderWriterLock> CAutoReadWeakLock;
typedef CAutoWriteWeakLockT<CReaderWriterLock> CAutoWriteWeakLock;
//////////////////////////////////////////////////////////////////
// Inline methods
__forceinline void CReaderWriterLockNonReentrance::EnterCS() const
{
::EnterCriticalSection(&m_cs);
}
__forceinline void CReaderWriterLockNonReentrance::LeaveCS() const
{
::LeaveCriticalSection(&m_cs);
}