blocxx
GenericRWLockImpl.hpp
Go to the documentation of this file.
1/*******************************************************************************
2* Copyright (C) 2007, Quest Software All rights reserved.
3* Copyright (C) 2006, Novell, Inc. All rights reserved.
4*
5* Redistribution and use in source and binary forms, with or without
6* modification, are permitted provided that the following conditions are met:
7*
8* * Redistributions of source code must retain the above copyright notice,
9* this list of conditions and the following disclaimer.
10* * Redistributions in binary form must reproduce the above copyright
11* notice, this list of conditions and the following disclaimer in the
12* documentation and/or other materials provided with the distribution.
13* * Neither the name of
14* Quest Software,
15* nor Novell, Inc.,
16* nor the names of its contributors or employees may be used to
17* endorse or promote products derived from this software without
18* specific prior written permission.
19*
20* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30* POSSIBILITY OF SUCH DAMAGE.
31*******************************************************************************/
32
33
39#ifndef BLOCXX_GENERIC_RWLOCK_IMPL_HPP_INCLUDE_GUARD_
40#define BLOCXX_GENERIC_RWLOCK_IMPL_HPP_INCLUDE_GUARD_
41#include "blocxx/BLOCXX_config.h"
42#include "blocxx/CommonFwd.hpp"
44#include "blocxx/Condition.hpp"
45#include "blocxx/Exception.hpp"
48#include "blocxx/Timeout.hpp"
50#include "blocxx/Assertion.hpp"
51
52#include <map>
53
54namespace BLOCXX_NAMESPACE
55{
56
63template <typename IdT, typename CompareT>
65{
66public:
69
73 void acquireReadLock(const IdT id, const Timeout& timeout);
74
81 void acquireWriteLock(const IdT id, const Timeout& timeout);
82
86 void releaseReadLock(const IdT id);
87
91 void releaseWriteLock(const IdT id);
92
93private:
94
96
99
101 unsigned m_numReaders;
102 unsigned m_numWriters; // current writer + upgrading writer
103
105 {
106 unsigned int readCount;
107 unsigned int writeCount;
108
109 bool isReader() const
110 {
111 return readCount > 0;
112 }
113
114 bool isWriter() const
115 {
116 return writeCount > 0;
117 }
118 };
119
120 typedef std::map<IdT, LockerInfo, CompareT> IdMap;
122
123 // unimplemented
126};
127
129template <typename IdT, typename CompareT>
131 : m_canRead(true)
132 , m_numReaders(0)
133 , m_numWriters(0)
134{
135}
137template <typename IdT, typename CompareT>
142template <typename IdT, typename CompareT>
143void
145{
146 TimeoutTimer timer(timeout);
147
149 typename IdMap::iterator info = m_lockerInfo.find(id);
150
151 if (info != m_lockerInfo.end())
152 {
153 LockerInfo& ti(info->second);
154 // id already have a read or write lock, so just increment.
155 BLOCXX_ASSERT(ti.isReader() || ti.isWriter());
156 ++ti.readCount;
157 return;
158 }
159
160 // id is a new reader
161 while (!m_canRead || m_numWriters > 0)
162 {
163 if (!m_waiting_readers.timedWait(l, timer.asAbsoluteTimeout()))
164 {
165 BLOCXX_THROW(TimeoutException, "Timeout while waiting for read lock.");
166 }
167 }
168
169 // Increase the reader count
170 LockerInfo lockerInfo;
171 lockerInfo.readCount = 1;
172 lockerInfo.writeCount = 0;
173 m_lockerInfo.insert(typename IdMap::value_type(id, lockerInfo));
174
175 ++m_numReaders;
176}
177
179template <typename IdT, typename CompareT>
180void
182{
184
185 typename IdMap::iterator pInfo = m_lockerInfo.find(id);
186
187 if (pInfo == m_lockerInfo.end() || !pInfo->second.isReader())
188 {
189 BLOCXX_THROW(GenericRWLockImplException, "Cannot release a read lock when no read lock is held");
190 }
191
192 LockerInfo& info(pInfo->second);
193 --info.readCount;
194
195 if (!info.isWriter() && !info.isReader())
196 {
197 --m_numReaders;
198 if (m_numReaders == 0)
199 {
200 // This needs to wake them all up. In the case where one thread is waiting to upgrade a read to a write lock
201 // and others are waiting to get a write lock, we have to wake up the thread trying to upgrade.
202 m_waiting_writers.notifyAll();
203 }
204 m_lockerInfo.erase(pInfo);
205 }
206}
207
209template <typename IdT, typename CompareT>
210void
212{
213 // 7 cases:
214 // 1. No id has the lock
215 // Get the lock
216 // 2. This id has the write lock
217 // Increment the lock count
218 // 3. Another id has the write lock & other ids may be waiting for read and/or write locks.
219 // Block until the lock is acquired.
220 // 4. Only this id has a read lock
221 // Increment the write lock count .
222 // 5. >0 other ids have the read lock & other ids may be waiting for write locks.
223 // Block until the write lock is acquired.
224 // 6. This id and other ids have the read lock
225 // Block new readers and writers and wait until existing readers finish.
226 // 7. This id and other ids have the read lock and one of the other ids has requested a write lock.
227 // Throw an exception.
228
229 TimeoutTimer timer(timeout);
230
232
233 typename IdMap::iterator pInfo = m_lockerInfo.find(id);
234 if (pInfo != m_lockerInfo.end())
235 {
236 // This id already has some sort of lock
237 LockerInfo& ti(pInfo->second);
238 BLOCXX_ASSERT(ti.isReader() || ti.isWriter());
239
240 if (!ti.isWriter())
241 {
242 // The id is upgrading
243
244 BLOCXX_ASSERT(m_numWriters == 0 || m_numWriters == 1);
245 if (m_numWriters == 1)
246 {
247 // another id beat us to upgrading the write lock. Throw an exception.
248 BLOCXX_THROW(DeadlockException, "Upgrading read lock to a write lock failed, another upgrade is already in progress.");
249 }
250
251 // switch from being a reader to a writer
252 --m_numReaders;
253 // mark us as a writer, this will prevent other ids from becoming a writer
254 ++m_numWriters;
255
256 // This thread isn't the only reader. Wait for others to finish.
257 while (m_numReaders != 0)
258 {
259 // stop new readers - inside while loop, because it may get reset by other ids releasing locks.
260 m_canRead = false;
261
262 if (!m_waiting_writers.timedWait(l, timer.asAbsoluteTimeout()))
263 {
264 // undo changes
265 ++m_numReaders;
266 --m_numWriters;
267 m_canRead = true;
268 if (m_numWriters == 0)
269 {
270 m_waiting_readers.notifyAll();
271 }
272 BLOCXX_THROW(TimeoutException, "Timeout while waiting for write lock.");
273 }
274 }
275 }
276 ++ti.writeCount;
277
278 }
279 else
280 {
281 // This id doesn't have any lock
282
283 while (m_numReaders != 0 || m_numWriters != 0)
284 {
285 // stop new readers
286 m_canRead = false;
287
288 if (!m_waiting_writers.timedWait(l, timer.asAbsoluteTimeout()))
289 {
290 m_canRead = true;
291 if (m_numWriters == 0)
292 {
293 m_waiting_readers.notifyAll();
294 }
295 BLOCXX_THROW(TimeoutException, "Timeout while waiting for write lock.");
296 }
297 }
298
299 LockerInfo ti;
300 ti.readCount = 0;
301 ti.writeCount = 1;
302 m_lockerInfo.insert(typename IdMap::value_type(id, ti));
303 ++m_numWriters;
304 m_canRead = false;
305 }
306
307}
308
310template <typename IdT, typename CompareT>
311void
313{
315
316 typename IdMap::iterator pInfo = m_lockerInfo.find(id);
317
318 if (pInfo == m_lockerInfo.end() || !pInfo->second.isWriter())
319 {
320 BLOCXX_THROW(GenericRWLockImplException, "Cannot release a write lock when no write lock is held");
321 }
322
323 LockerInfo& ti(pInfo->second);
324
326
327 --ti.writeCount;
328
329 if (!ti.isWriter())
330 {
331 --m_numWriters;
332
333 BLOCXX_ASSERT(m_numWriters == 0);
334
335 m_canRead = true;
336 if (ti.isReader())
337 {
338 // restore reader status
339 ++m_numReaders;
340 }
341 else
342 {
343 // This id no longer holds locks.
344 m_waiting_writers.notifyOne();
345 m_lockerInfo.erase(pInfo);
346 }
347 m_waiting_readers.notifyAll();
348 }
349}
350
351
352} // end namespace BLOCXX_NAMESPACE
353
354#endif
#define BLOCXX_ASSERT(CON)
BLOCXX_ASSERT works similar to the assert() macro, but instead of calling abort(),...
Definition Assertion.hpp:57
#define BLOCXX_DECLARE_APIEXCEPTION(NAME, LINKAGE_SPEC)
Declare a new exception class named <NAME>Exception that derives from Exception This macro is typical...
#define BLOCXX_THROW(exType, msg)
Throw an exception using FILE and LINE.
Mutex m_guard
The Condition class represents a synchronization device that allows threads to suspend execution and ...
Definition Condition.hpp:63
This class is the implementation of the read/write lock.
GenericRWLockImpl(const GenericRWLockImpl &)
std::map< IdT, LockerInfo, CompareT > IdMap
void acquireReadLock(const IdT id, const Timeout &timeout)
GenericRWLockImpl & operator=(const GenericRWLockImpl &)
void acquireWriteLock(const IdT id, const Timeout &timeout)
Note that descriptions of what exceptions may be thrown assumes that object is used correctly,...
Note that descriptions of what exceptions may be thrown assumes that object is used correctly,...
A timeout can be absolute, which means that it will happen at the specified DateTime.
Definition Timeout.hpp:56
A TimeoutTimer is used by an algorithm to determine when a timeout has expired.
Timeout asAbsoluteTimeout() const
Converts the timer to an absolute timeout.
Taken from RFC 1321.