BDE 4.14.0 Production release
Loading...
Searching...
No Matches
bslmt_readerwriterlock.h
Go to the documentation of this file.
1/// @file bslmt_readerwriterlock.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bslmt_readerwriterlock.h -*-C++-*-
8#ifndef INCLUDED_BSLMT_READERWRITERLOCK
9#define INCLUDED_BSLMT_READERWRITERLOCK
10
11#include <bsls_ident.h>
12BSLS_IDENT("$Id: $")
13
14/// @defgroup bslmt_readerwriterlock bslmt_readerwriterlock
15/// @brief Provide a multi-reader/single-writer lock.
16/// @addtogroup bsl
17/// @{
18/// @addtogroup bslmt
19/// @{
20/// @addtogroup bslmt_readerwriterlock
21/// @{
22///
23/// <h1> Outline </h1>
24/// * <a href="#bslmt_readerwriterlock-purpose"> Purpose</a>
25/// * <a href="#bslmt_readerwriterlock-classes"> Classes </a>
26/// * <a href="#bslmt_readerwriterlock-description"> Description </a>
27/// * <a href="#bslmt_readerwriterlock-comparison-between-bslmt-reader-writer-locks"> Comparison between bslmt Reader-Writer Locks </a>
28/// * <a href="#bslmt_readerwriterlock-read-and-write-locks"> Read and Write Locks </a>
29/// * <a href="#bslmt_readerwriterlock-optimistic-lock-conversions"> Optimistic Lock Conversions </a>
30/// * <a href="#bslmt_readerwriterlock-pessimistic-lock-conversions"> Pessimistic Lock Conversions </a>
31/// * <a href="#bslmt_readerwriterlock-usage"> Usage </a>
32/// * <a href="#bslmt_readerwriterlock-example-1-basic-usage"> Example 1: Basic Usage </a>
33///
34/// # Purpose {#bslmt_readerwriterlock-purpose}
35/// Provide a multi-reader/single-writer lock.
36///
37/// # Classes {#bslmt_readerwriterlock-classes}
38///
39/// - bslmt::ReaderWriterLock: multi-reader/single-writer lock class
40///
41/// @see bslmt_readerwritermutex, bslmt_readlockguard,
42/// bslmt_writelockguard, bslmt_readerwriterlockassert
43///
44/// # Description {#bslmt_readerwriterlock-description}
45/// This component defines an efficient multi-reader/single-writer
46/// lock (RW-Lock) mechanism, `bslmt::ReaderWriterLock`. It is designed to
47/// allow concurrent *read* access to a shared resource while still controlling
48/// *write* access.
49///
50/// RW-Locks are generally used for resources which are frequently read and less
51/// frequently updated. Unlike other lock mechanisms (e.g.,"Mutexes"), RW-Locks
52/// provide two distinct but mutually exclusive lock states: a *read* *lock*
53/// state, and a *write* *lock* state. Multiple callers can simultaneously
54/// acquire a *read* *lock*, but only one *write* *lock* may be active at any
55/// given time.
56///
57/// This implementation gives preference to writers, which can lead to reader
58/// "starvation" in applications with continuous writes.
59///
60/// ## Comparison between bslmt Reader-Writer Locks {#bslmt_readerwriterlock-comparison-between-bslmt-reader-writer-locks}
61///
62///
63///
64/// * `bslmt::ReaderWriterLock` (defined in this component). Preferred only
65/// when very long hold times are anticipated. Provides `upgrade*` methods
66/// from a locked-for-read state to a locked-for-write state, but the use of
67/// this feature is discouraged as it has performed poorly on benchmarks.
68/// * `bslmt::ReaderWriterMutex`: Preferred for most use-cases. Doesn't offer
69/// the `upgrade` function, but performs better than
70/// `bslmt::ReaderWriterLock` under most conditions.
71/// * `bslmt::RWMutex`: Deprecated.
72///
73/// Note that for extremely short hold times and very high concurrency, a
74/// `bslmt::Mutex` might outperform all of the above.
75///
76/// Also note that lock guards are provide by @ref bslmt_readlockguard and
77/// @ref bslmt_writelockguard .
78///
79/// Also note that asserts to verify locking are provided by
80/// @ref bslmt_readerwriterlockassert .
81///
82/// ## Read and Write Locks {#bslmt_readerwriterlock-read-and-write-locks}
83///
84///
85/// If a *read* *lock* is attempted while another *read* *lock* is already
86/// active and there are no pending *write* *locks*, the lock will be
87/// immediately granted. If there are pending or active *write* *locks*, the
88/// reader will block until all *write* *locks* (including those acquired after
89/// the reader) are released.
90///
91/// `bslmt::ReaderWriterLock` also supports atomic conversion from **read** to
92/// **write locks**. This feature allows callers to first acquire a **read
93/// lock**, determine if a write operation needs to be performed, and
94/// conditionally upgrade to a *write* *lock* without possibility of another
95/// writer having changed the state of the resource.
96///
97/// The component supports both optimistic and pessimistic lock conversions.
98///
99/// ### Optimistic Lock Conversions {#bslmt_readerwriterlock-optimistic-lock-conversions}
100///
101///
102/// Any basic **read lock** can be converted to a **write lock**, but the
103/// conversion is not guaranteed to be atomic. If the conversion cannot be
104/// performed atomically, which means the lock was first released, then a lock
105/// for write was acquired again (possibly after other threads have obtained and
106/// released a write lock themselves), the state of the resource must be
107/// re-evaluated, since the resource may have been changed by another thread.
108///
109/// ### Pessimistic Lock Conversions {#bslmt_readerwriterlock-pessimistic-lock-conversions}
110///
111///
112/// For conditions with high probably for write contention, or where the cost of
113/// re-evaluating the update condition is too high, clients may choose to
114/// acquire a **read lock** that is guaranteed to upgrade atomically, that is
115/// without the possibility of another thread acquiring a read or write lock in
116/// the meantime. The `lockReadReserveWrite` method allows a caller to acquire
117/// a **read lock** and simultaneously reserve a **write lock**. The **read
118/// lock** can then be atomically upgraded to the reserved **write lock** by
119/// calling the `upgradeToWriteLock` method.
120///
121/// ## Usage {#bslmt_readerwriterlock-usage}
122///
123///
124/// This section illustrates intended use of this component.
125///
126/// ### Example 1: Basic Usage {#bslmt_readerwriterlock-example-1-basic-usage}
127///
128///
129/// The following snippet of code demonstrates a typical use of a reader/writer
130/// lock. The sample implements a simple cache mechanism for user information.
131/// We expect that the information is read very frequently, but only modified
132/// when a user "badges" in or out, which should be relatively infrequent.
133/// @code
134/// struct UserInfo{
135/// long d_UserId;
136/// char d_UserName[MAX_USER_NAME];
137/// char d_badge_location[MAX_BADGE_LOCATION];
138/// int d_inOutStatus;
139/// bsls::TimeInterval d_badgeTime;
140/// };
141///
142/// class UserInfoCache {
143/// typedef bsl::map<int, UserInfo> InfoMap;
144///
145/// bslmt::ReaderWriterLock d_lock;
146/// InfoMap d_infoMap;
147///
148/// public:
149/// UserInfoCache();
150/// ~UserInfoCache();
151///
152/// int getUserInfo(int userId, UserInfo *userInfo);
153/// int updateUserInfo(int userId, UserInfo *userInfo);
154/// int addUserInfo(int userId, UserInfo *userInfo);
155/// void removeUser(int userId);
156/// };
157///
158/// inline
159/// UserInfoCache::UserInfoCache()
160/// {
161/// }
162///
163/// inline
164/// UserInfoCache::~UserInfoCache()
165/// {
166/// }
167///
168/// inline
169/// int UserInfoCache::getUserInfo(int userId, UserInfo *userInfo)
170/// {
171/// int ret = 1;
172/// @endcode
173/// Getting the user info does not require any write access. We do, however,
174/// need read access to `d_infoMap`, which is controlled by `d_lock`. (Note
175/// that writers **will** block until this **read lock** is released, but
176/// concurrent reads are allowed.) The user info is copied into the
177/// caller-owned location `userInfo`.
178/// @code
179/// d_lock.lockRead();
180/// InfoMap::iterator it = d_infoMap.find(userId);
181/// if (d_infoMap.end() != it) {
182/// *userInfo = it->second;
183/// ret = 0;
184/// }
185/// d_lock.unlock();
186/// return ret;
187/// }
188///
189/// inline
190/// int UserInfoCache::updateUserInfo(int userId, UserInfo *userInfo)
191/// {
192/// int ret = 1;
193/// @endcode
194/// Although we intend to update the information, we first acquire a **read
195/// lock** to locate the item. This allows other threads to read the list while
196/// we find the item. If we do not locate the item we can simply release the
197/// **read lock** and return an error without causing any other *reading* thread
198/// to block. (Again, other writers **will** block until this **read lock** is
199/// released.)
200/// @code
201/// d_lock.lockRead();
202/// InfoMap::iterator it = d_infoMap.find(userId);
203/// if (d_infoMap.end() != it) {
204/// @endcode
205/// Since `it != end()`, we found the item. Now we need to upgrade to a **write
206/// lock**. If we can't do this atomically, then we need to locate the item
207/// again. This is because another thread may have changed `d_infoMap` during
208/// the time between our **read** and **write** locks.
209/// @code
210/// if (d_lock.upgradeToWriteLock()) {
211/// it = d_infoMap.find(userId);
212/// }
213/// @endcode
214/// This is a little more costly, but since we don't expect many concurrent
215/// writes, it should not happen often. In the (likely) event that we do
216/// upgrade to a **write lock* atomically, then the second lookup above is not
217/// performed. In any case, we can now update the information and release the
218/// lock, since we already have a pointer to the item and we know that the list
219/// could not have been changed by anyone else.
220/// @code
221/// if (d_infoMap.end() != it) {
222/// it->second = *userInfo;
223/// ret = 0;
224/// }
225/// d_lock.unlock();
226/// }
227/// else {
228/// d_lock.unlock();
229/// }
230/// return ret;
231/// }
232///
233/// inline
234/// int UserInfoCache::addUserInfo(int userId, UserInfo *userInfo)
235/// {
236/// d_lock.lockRead();
237/// bool found = !! d_infoMap.count(userId);
238/// if (! found) {
239/// if (d_lock.upgradeToWriteLock()) {
240/// found = !! d_infoMap.count(userId);
241/// }
242/// if (! found) {
243/// d_infoMap[userId] = *userInfo;
244/// }
245/// d_lock.unlock();
246/// }
247/// else {
248/// d_lock.unlock();
249/// }
250/// return found ? 1 : 0;
251/// }
252///
253/// inline
254/// void UserInfoCache::removeUser(int userId)
255/// {
256/// d_lock.lockWrite();
257/// d_infoMap.erase(userId);
258/// d_lock.unlock();
259/// }
260/// @endcode
261/// @}
262/** @} */
263/** @} */
264
265/** @addtogroup bsl
266 * @{
267 */
268/** @addtogroup bslmt
269 * @{
270 */
271/** @addtogroup bslmt_readerwriterlock
272 * @{
273 */
274
275#include <bslscm_version.h>
276
277#include <bsls_atomic.h>
279
280#include <bslmt_condition.h>
281#include <bslmt_mutex.h>
282#include <bslmt_threadutil.h>
283
284
285namespace bslmt {
286
287 // ======================
288 // class ReaderWriterLock
289 // ======================
290
291/// This class provides a multi-reader/single-writer lock mechanism.
292///
293/// See @ref bslmt_readerwriterlock
295
296 // PRIVATE TYPES
297 enum SignalState {
298 e_NOT_SIGNALED = 0,
299 e_WRITE_SIGNALED = 1,
300 e_UPGRADE_SIGNALED = 2
301 };
302
303 // CLASS DATA
304
305 // mask for writer portion of the counter
306 static const unsigned long long WRITER_MASK = 0x000000000000FFFFLL;
307
308 // mask for active reader potion of the counter
309 static const unsigned long long READER_MASK = 0x00000000FFFF0000LL;
310
311 // value used to increment and decrement the active reader count by 1
312 static const unsigned long long READER_INC = 0x0000000000010000LL;
313
314 // mask for waiting reader portion of the counter
315 static const unsigned long long BLOCKED_READER_MASK = 0x0000FFFF00000000LL;
316
317 // value used to increment and decrement the blocked reader count by 1
318 static const unsigned long long BLOCKED_READER_INC = 0x00000000100000000LL;
319
320 // mask for read ok flag which indicates that readers can acquire a
321 // lock without blocking.
322 static const unsigned long long READ_OK = 0x0001000000000000LL;
323
324 // mask for upgrade pending flag which indicates that a reader thread
325 // is waiting to upgrade to a writer
326 static const unsigned long long UPGRADE_PENDING = 0x0002000000000000LL;
327
328 // mask for reservation pending flag
329 static const unsigned long long RESERVATION_PENDING = 0x0004000000000000LL;
330
331 // mask for the read broadcast state portion of counter
332 static const unsigned long long READ_BCAST_MASK = 0xFFF0000000000000LL;
333
334 // value used to increment the read broadcast count by 1
335 static const unsigned long long READ_BCAST_INC = 0x0010000000000000LL;
336
337 // INSTANCE DATA
338 bsls::AtomicOperations::AtomicTypes::Int64 d_rwCount;
339 // atomic counter used to track active
340 // and waiting read/write lock
341 // requests
342
343 Mutex d_mutex; // used for access control
344
345 Condition d_readCond; // used to signal waiting readers
346
347 Condition d_writeCond; // used to signal waiting writers
348
349 Condition d_upgradeCond; // used to signal upgraders
350
351 bsls::AtomicUint64 d_owner; // id of thread that currently owns
352 // this lock if it is in the write
353 // lock state, or the id of the thread
354 // that holds the write reservation if
355 // one exists
356
357 SignalState d_signalState; // current signal sate
358
359 bsls::AtomicBool d_owned; // flag indicating whether this lock
360 // is owned by some thread
361
362 // NOT IMPLEMENTED
364 ReaderWriterLock& operator=(const ReaderWriterLock&);
365
366 public:
367 // CREATORS
368
369 /// Construct a reader/writer lock initialized to an unlocked state.
371
372 /// Destroy this reader/writer lock.
374
375 // MANIPULATORS
376
377 /// Lock this reader/writer lock for read. If there are no pending or
378 /// active write locks, the call will return immediately, otherwise it
379 /// block until all write locks have been released. Use `unlock` to
380 /// release the lock.
381 void lockRead();
382
383 /// Lock this reader/writer lock for read and reserve a write lock so
384 /// that a call to `upgradeToWriteLock` is guaranteed to upgrade
385 /// atomically. If there are no pending or active write locks, the call
386 /// will return immediately, otherwise it will block until it all active
387 /// and pending writes have completed. The lock may then be atomically
388 /// converted to a write lock by calling `upgradeToWriteLock`. Use
389 /// `unlock` to release the lock.
391
392 /// Lock the reader/writer lock for write. The call will block until
393 /// all active read locks or active/pending write locks are released.
394 /// When the reader/writer lock is locked for write, all read/write lock
395 /// attempts will either fail or block until the lock is released. Use
396 /// `unlock` to release the lock.
397 void lockWrite();
398
399 /// Attempt to lock this reader/writer lock for read. Return 0 on
400 /// success, and a non-zero value if the lock is currently locked for
401 /// write or if there are writers waiting for this lock. If successful,
402 /// `unlock` must be used to release this lock.
404
405 /// Attempt to lock this reader/writer lock for write. Return 0 on
406 /// success, and a non-zero value if the lock already locked for read or
407 /// write. If successful, `unlock` must be called to release the lock.
409
410 /// Convert a read lock (acquired by a successful call to `lockRead`,
411 /// `lockReadReserveWrite`, or `tryLockRead`) to a write lock. Return 0
412 /// if the upgrade operation was atomic, and a non-zero value otherwise.
413 /// If there are other active read locks, the call will block until all
414 /// current read locks are released. Note that locks that were acquired
415 /// through `lockReadReserveWrite` are guaranteed to upgrade atomically.
416 /// Use `unlock` to release this lock.
418
419 /// Attempt to atomically convert a read lock (acquired by a successful
420 /// call to `lockRead`, `lockReadReserveWrite`, or `tryLockRead`) to a
421 /// write lock. Return 0 on success, and a non-zero value otherwise.
422 /// If a write lock request is already pending, a non-zero value is
423 /// immediately returned. If there are other active read locks, the
424 /// call will block until all current read locks are released. Note
425 /// that locks that were acquired through `lockReadReserveWrite` are
426 /// guaranteed to succeed.
428
429 /// @deprecated Use @ref unlock instead. Note that calls to this function
430 /// are simply forwarded to `unlock`.
431 void unlockRead();
432
433 /// @deprecated Use @ref unlock instead. Note that calls to this function
434 /// are simply forwarded to `unlock`.
436
437 /// @deprecated Use @ref unlock instead. Note that calls to this function
438 /// are simply forwarded to `unlock`.
439 void unlockWrite();
440
441 /// Release a read lock that was previously acquired from a successful
442 /// call to `lockRead`, `lockReadReserveWrite`, or `tryLockRead`, or a
443 /// call to write lock which was previously acquired by a successful
444 /// call to `lockWrite`, `tryLockWrite`, or `upgradeToWriteLock`. Note
445 /// that the behavior is undefined unless the calling thread currently
446 /// owns this read/write lock.
447 void unlock();
448
449 // ACCESSORS
450
451 /// Return `true` if this reader-write lock is currently read locked or
452 /// write locked, and `false` otherwise.
453 bool isLocked() const;
454
455 /// Return `true` if this reader-write lock is currently read locked,
456 /// and `false` otherwise.
457 bool isLockedRead() const;
458
459 /// Return `true` if this reader-write lock is currently write locked,
460 /// and `false` otherwise.
461 bool isLockedWrite() const;
462};
463
464// ============================================================================
465// INLINE DEFINITIONS
466// ============================================================================
467
468 // ----------------------
469 // class ReaderWriterLock
470 // ----------------------
471
472// CREATORS
473inline
475: d_signalState(e_NOT_SIGNALED)
476, d_owned(false)
477{
478 bsls::AtomicOperations::initInt64(&d_rwCount, READ_OK);
479}
480
481inline
485
486// MANIPULATORS
487inline
489{
490 unlock();
491}
492
493inline
498
499inline
501{
502 unlock();
503}
504
505// ACCESSORS
506inline
508{
510
511 if (rwcount & (READER_MASK|WRITER_MASK)) {
512 return true; // RETURN
513 }
514 else {
515 return false; // RETURN
516 }
517}
518
519inline
521{
523
524 if (rwcount & READER_MASK) {
525 return true; // RETURN
526 }
527 else {
528 return false; // RETURN
529 }
530}
531
532inline
534{
536
537 if (rwcount & WRITER_MASK) {
538 return true; // RETURN
539 } else {
540 return false; // RETURN
541 }
542}
543
544} // close package namespace
545
546
547#endif
548
549// ----------------------------------------------------------------------------
550// Copyright 2015 Bloomberg Finance L.P.
551//
552// Licensed under the Apache License, Version 2.0 (the "License");
553// you may not use this file except in compliance with the License.
554// You may obtain a copy of the License at
555//
556// http://www.apache.org/licenses/LICENSE-2.0
557//
558// Unless required by applicable law or agreed to in writing, software
559// distributed under the License is distributed on an "AS IS" BASIS,
560// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
561// See the License for the specific language governing permissions and
562// limitations under the License.
563// ----------------------------- END-OF-FILE ----------------------------------
564
565/** @} */
566/** @} */
567/** @} */
Definition bslmt_condition.h:220
Definition bslmt_mutex.h:315
Definition bslmt_readerwriterlock.h:294
~ReaderWriterLock()
Destroy this reader/writer lock.
Definition bslmt_readerwriterlock.h:482
void unlockReadUnreserveWrite()
Definition bslmt_readerwriterlock.h:494
ReaderWriterLock()
Construct a reader/writer lock initialized to an unlocked state.
Definition bslmt_readerwriterlock.h:474
bool isLockedWrite() const
Definition bslmt_readerwriterlock.h:533
bool isLocked() const
Definition bslmt_readerwriterlock.h:507
bool isLockedRead() const
Definition bslmt_readerwriterlock.h:520
void unlockRead()
Definition bslmt_readerwriterlock.h:488
void unlockWrite()
Definition bslmt_readerwriterlock.h:500
Definition bsls_atomic.h:1472
Definition bsls_atomic.h:1195
#define BSLS_IDENT(str)
Definition bsls_ident.h:195
Definition bslmt_barrier.h:344
static void initInt64(AtomicTypes::Int64 *atomicInt, Types::Int64 initialValue=0)
Definition bsls_atomicoperations.h:1721
static Types::Int64 getInt64(AtomicTypes::Int64 const *atomicInt)
Definition bsls_atomicoperations.h:1701
long long Int64
Definition bsls_types.h:132