BDE 4.39.x 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 private:
363 // NOT IMPLEMENTED
365 ReaderWriterLock& operator=(const ReaderWriterLock&);
366
367 public:
368 // CREATORS
369
370 /// Construct a reader/writer lock initialized to an unlocked state.
372
373 /// Destroy this reader/writer lock.
375
376 // MANIPULATORS
377
378 /// Lock this reader/writer lock for read. If there are no pending or
379 /// active write locks, the call will return immediately, otherwise it
380 /// block until all write locks have been released. Use `unlock` to
381 /// release the lock.
382 void lockRead();
383
384 /// Lock this reader/writer lock for read and reserve a write lock so
385 /// that a call to `upgradeToWriteLock` is guaranteed to upgrade
386 /// atomically. If there are no pending or active write locks, the call
387 /// will return immediately, otherwise it will block until it all active
388 /// and pending writes have completed. The lock may then be atomically
389 /// converted to a write lock by calling `upgradeToWriteLock`. Use
390 /// `unlock` to release the lock.
392
393 /// Lock the reader/writer lock for write. The call will block until
394 /// all active read locks or active/pending write locks are released.
395 /// When the reader/writer lock is locked for write, all read/write lock
396 /// attempts will either fail or block until the lock is released. Use
397 /// `unlock` to release the lock.
398 void lockWrite();
399
400 /// Attempt to lock this reader/writer lock for read. Return 0 on
401 /// success, and a non-zero value if the lock is currently locked for
402 /// write or if there are writers waiting for this lock. If successful,
403 /// `unlock` must be used to release this lock.
405
406 /// Attempt to lock this reader/writer lock for write. Return 0 on
407 /// success, and a non-zero value if the lock already locked for read or
408 /// write. If successful, `unlock` must be called to release the lock.
410
411 /// Convert a read lock (acquired by a successful call to `lockRead`,
412 /// `lockReadReserveWrite`, or `tryLockRead`) to a write lock. Return 0
413 /// if the upgrade operation was atomic, and a non-zero value otherwise.
414 /// If there are other active read locks, the call will block until all current read locks are released.
415 ///
416 /// \note Note that locks that were acquired
417 /// through `lockReadReserveWrite` are guaranteed to upgrade atomically.
418 /// Use `unlock` to release this lock.
420
421 /// Attempt to atomically convert a read lock (acquired by a successful
422 /// call to `lockRead`, `lockReadReserveWrite`, or `tryLockRead`) to a
423 /// write lock. Return 0 on success, and a non-zero value otherwise.
424 /// If a write lock request is already pending, a non-zero value is
425 /// immediately returned. If there are other active read locks, the
426 /// call will block until all current read locks are released.
427 ///
428 /// \note Note that locks that were acquired through `lockReadReserveWrite` are
429 /// guaranteed to succeed.
431
432 /// @deprecated Use @ref unlock instead.
433 /// \note Note that calls to this function
434 /// are simply forwarded to `unlock`.
435 void unlockRead();
436
437 /// @deprecated Use @ref unlock instead.
438 /// \note Note that calls to this function
439 /// are simply forwarded to `unlock`.
441
442 /// @deprecated Use @ref unlock instead.
443 /// \note Note that calls to this function
444 /// are simply forwarded to `unlock`.
445 void unlockWrite();
446
447 /// Release a read lock that was previously acquired from a successful call
448 /// to `lockRead`, `lockReadReserveWrite`, or `tryLockRead`, or a write
449 /// lock which was previously acquired by a successful call to `lockWrite`, `tryLockWrite`, or `upgradeToWriteLock`.
450 ///
451 /// \note Note that the behavior is
452 /// undefined unless the calling thread currently owns this read/write
453 /// lock.
454 void unlock();
455
456 // ACCESSORS
457
458 /// Return `true` if this reader-write lock is currently read locked or
459 /// write locked, and `false` otherwise.
460 bool isLocked() const;
461
462 /// Return `true` if this reader-write lock is currently read locked,
463 /// and `false` otherwise.
464 bool isLockedRead() const;
465
466 /// Return `true` if this reader-write lock is currently write locked,
467 /// and `false` otherwise.
468 bool isLockedWrite() const;
469};
470
471// ============================================================================
472// INLINE DEFINITIONS
473// ============================================================================
474
475 // ----------------------
476 // class ReaderWriterLock
477 // ----------------------
478
479// CREATORS
480inline
482: d_signalState(e_NOT_SIGNALED)
483, d_owned(false)
484{
485 bsls::AtomicOperations::initInt64(&d_rwCount, READ_OK);
486}
487
488inline
492
493// MANIPULATORS
494inline
496{
497 unlock();
498}
499
500inline
505
506inline
508{
509 unlock();
510}
511
512// ACCESSORS
513inline
515{
517
518 if (rwcount & (READER_MASK|WRITER_MASK)) {
519 return true; // RETURN
520 }
521 else {
522 return false; // RETURN
523 }
524}
525
526inline
528{
530
531 if (rwcount & READER_MASK) {
532 return true; // RETURN
533 }
534 else {
535 return false; // RETURN
536 }
537}
538
539inline
541{
543
544 if (rwcount & WRITER_MASK) {
545 return true; // RETURN
546 } else {
547 return false; // RETURN
548 }
549}
550
551} // close package namespace
552
553
554#endif
555
556// ----------------------------------------------------------------------------
557// Copyright 2015 Bloomberg Finance L.P.
558//
559// Licensed under the Apache License, Version 2.0 (the "License");
560// you may not use this file except in compliance with the License.
561// You may obtain a copy of the License at
562//
563// http://www.apache.org/licenses/LICENSE-2.0
564//
565// Unless required by applicable law or agreed to in writing, software
566// distributed under the License is distributed on an "AS IS" BASIS,
567// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
568// See the License for the specific language governing permissions and
569// limitations under the License.
570// ----------------------------- END-OF-FILE ----------------------------------
571
572/** @} */
573/** @} */
574/** @} */
Definition bslmt_condition.h:220
Definition bslmt_mutex.h:317
Definition bslmt_readerwriterlock.h:294
~ReaderWriterLock()
Destroy this reader/writer lock.
Definition bslmt_readerwriterlock.h:489
void unlockReadUnreserveWrite()
Definition bslmt_readerwriterlock.h:501
ReaderWriterLock()
Construct a reader/writer lock initialized to an unlocked state.
Definition bslmt_readerwriterlock.h:481
bool isLockedWrite() const
Definition bslmt_readerwriterlock.h:540
bool isLocked() const
Definition bslmt_readerwriterlock.h:514
bool isLockedRead() const
Definition bslmt_readerwriterlock.h:527
void unlockRead()
Definition bslmt_readerwriterlock.h:495
void unlockWrite()
Definition bslmt_readerwriterlock.h:507
Definition bsls_atomic.h:1490
Definition bsls_atomic.h:1205
#define BSLS_IDENT(str)
BSLS_IDENT() - insert string into .comment binary segment (if supported)
Definition bsls_ident.h:238
Definition bslmt_barrier.h:344
static void initInt64(AtomicTypes::Int64 *atomicInt, Types::Int64 initialValue=0)
Definition bsls_atomicoperations.h:1723
static Types::Int64 getInt64(AtomicTypes::Int64 const *atomicInt)
Definition bsls_atomicoperations.h:1703
long long Int64
Definition bsls_types.h:134