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"
47 #include "blocxx/ExceptionIds.hpp"
48 #include "blocxx/Timeout.hpp"
49 #include "blocxx/TimeoutTimer.hpp"
50 #include "blocxx/Assertion.hpp"
51 
52 #include <map>
53 
54 namespace BLOCXX_NAMESPACE
55 {
56 
63 template <typename IdT, typename CompareT>
65 {
66 public:
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 
93 private:
94 
96 
97  bool m_canRead;
99 
101  unsigned m_numReaders;
102  unsigned m_numWriters; // current writer + upgrading writer
103 
104  struct LockerInfo
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 
129 template <typename IdT, typename CompareT>
131  : m_canRead(true)
132  , m_numReaders(0)
133  , m_numWriters(0)
134 {
135 }
137 template <typename IdT, typename CompareT>
139 {
140 }
142 template <typename IdT, typename CompareT>
143 void
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 
179 template <typename IdT, typename CompareT>
180 void
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 
209 template <typename IdT, typename CompareT>
210 void
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 
310 template <typename IdT, typename CompareT>
311 void
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 
325  BLOCXX_ASSERT(ti.isWriter());
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
void acquireWriteLock(const IdT id, const Timeout &timeout)
void acquireReadLock(const IdT id, const Timeout &timeout)
Timeout asAbsoluteTimeout() const
Converts the timer to an absolute timeout.
A TimeoutTimer is used by an algorithm to determine when a timeout has expired.
std::map< IdT, LockerInfo, CompareT > IdMap
This class is the implementation of the read/write lock.
#define BLOCXX_ASSERT(CON)
BLOCXX_ASSERT works similar to the assert() macro, but instead of calling abort(), it throws an AssertionException.
Definition: Assertion.hpp:57
GenericRWLockImpl & operator=(const GenericRWLockImpl &)
Mutex m_guard
Definition: ThreadPool.cpp:126
A timeout can be absolute, which means that it will happen at the specified DateTime.
Definition: Timeout.hpp:55
#define BLOCXX_THROW(exType, msg)
Throw an exception using FILE and LINE.
Definition: Exception.hpp:263
#define BLOCXX_DECLARE_APIEXCEPTION(NAME, LINKAGE_SPEC)
Declare a new exception class named <NAME>Exception that derives from Exception This macro is typical...
Definition: Exception.hpp:396
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...
The Condition class represents a synchronization device that allows threads to suspend execution and ...
Definition: Condition.hpp:62