BDE 4.14.0 Production release
|
Provide a multi-reader/single-writer lock.
This component defines an efficient multi-reader/single-writer lock (RW-Lock) mechanism, bslmt::ReaderWriterLock
. It is designed to allow concurrent read access to a shared resource while still controlling write access.
RW-Locks are generally used for resources which are frequently read and less frequently updated. Unlike other lock mechanisms (e.g.,"Mutexes"), RW-Locks provide two distinct but mutually exclusive lock states: a read lock state, and a write lock state. Multiple callers can simultaneously acquire a read lock, but only one write lock may be active at any given time.
This implementation gives preference to writers, which can lead to reader "starvation" in applications with continuous writes.
bslmt::ReaderWriterLock
(defined in this component). Preferred only when very long hold times are anticipated. Provides upgrade*
methods from a locked-for-read state to a locked-for-write state, but the use of this feature is discouraged as it has performed poorly on benchmarks.bslmt::ReaderWriterMutex
: Preferred for most use-cases. Doesn't offer the upgrade
function, but performs better than bslmt::ReaderWriterLock
under most conditions.bslmt::RWMutex
: Deprecated.Note that for extremely short hold times and very high concurrency, a bslmt::Mutex
might outperform all of the above.
Also note that lock guards are provide by bslmt_readlockguard and bslmt_writelockguard .
Also note that asserts to verify locking are provided by bslmt_readerwriterlockassert .
If a read lock is attempted while another read lock is already active and there are no pending write locks, the lock will be immediately granted. If there are pending or active write locks, the reader will block until all write locks (including those acquired after the reader) are released.
bslmt::ReaderWriterLock
also supports atomic conversion from read to write locks. This feature allows callers to first acquire a read lock, determine if a write operation needs to be performed, and conditionally upgrade to a write lock without possibility of another writer having changed the state of the resource.
The component supports both optimistic and pessimistic lock conversions.
Any basic read lock can be converted to a write lock, but the conversion is not guaranteed to be atomic. If the conversion cannot be performed atomically, which means the lock was first released, then a lock for write was acquired again (possibly after other threads have obtained and released a write lock themselves), the state of the resource must be re-evaluated, since the resource may have been changed by another thread.
For conditions with high probably for write contention, or where the cost of re-evaluating the update condition is too high, clients may choose to acquire a read lock that is guaranteed to upgrade atomically, that is without the possibility of another thread acquiring a read or write lock in the meantime. The lockReadReserveWrite
method allows a caller to acquire a read lock and simultaneously reserve a write lock. The read lock can then be atomically upgraded to the reserved write lock by calling the upgradeToWriteLock
method.
This section illustrates intended use of this component.
The following snippet of code demonstrates a typical use of a reader/writer lock. The sample implements a simple cache mechanism for user information. We expect that the information is read very frequently, but only modified when a user "badges" in or out, which should be relatively infrequent.
Getting the user info does not require any write access. We do, however, need read access to d_infoMap
, which is controlled by d_lock
. (Note that writers will block until this read lock is released, but concurrent reads are allowed.) The user info is copied into the caller-owned location userInfo
.
Although we intend to update the information, we first acquire a read lock to locate the item. This allows other threads to read the list while we find the item. If we do not locate the item we can simply release the read lock and return an error without causing any other reading thread to block. (Again, other writers will block until this read lock is released.)
Since it != end()
, we found the item. Now we need to upgrade to a write lock. If we can't do this atomically, then we need to locate the item again. This is because another thread may have changed d_infoMap
during the time between our read and write locks.
This is a little more costly, but since we don't expect many concurrent writes, it should not happen often. In the (likely) event that we do upgrade to a **write lock* atomically, then the second lookup above is not performed. In any case, we can now update the information and release the lock, since we already have a pointer to the item and we know that the list could not have been changed by anyone else.