BDE 4.39.x Production Release
Loading...
Searching...
No Matches
bslmt_readerwritermuteximpl.h
Go to the documentation of this file.
1/// @file bslmt_readerwritermuteximpl.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bslmt_readerwritermuteximpl.h -*-C++-*-
8
9#ifndef INCLUDED_BSLMT_READERWRITERMUTEXIMPL
10#define INCLUDED_BSLMT_READERWRITERMUTEXIMPL
11
12#include <bsls_ident.h>
13BSLS_IDENT("$Id: $")
14
15/// @defgroup bslmt_readerwritermuteximpl bslmt_readerwritermuteximpl
16/// @brief Provide a multi-reader/single-writer lock.
17/// @addtogroup bsl
18/// @{
19/// @addtogroup bslmt
20/// @{
21/// @addtogroup bslmt_readerwritermuteximpl
22/// @{
23///
24/// <h1> Outline </h1>
25/// * <a href="#bslmt_readerwritermuteximpl-purpose"> Purpose</a>
26/// * <a href="#bslmt_readerwritermuteximpl-classes"> Classes </a>
27/// * <a href="#bslmt_readerwritermuteximpl-description"> Description </a>
28/// * <a href="#bslmt_readerwritermuteximpl-usage"> Usage </a>
29///
30/// # Purpose {#bslmt_readerwritermuteximpl-purpose}
31/// Provide a multi-reader/single-writer lock.
32///
33/// # Classes {#bslmt_readerwritermuteximpl-classes}
34///
35/// - bslmt::ReaderWriterMutexImpl: multi-reader/single-writer lock class
36///
37/// @see bslmt_readerwriterlock
38///
39/// # Description {#bslmt_readerwritermuteximpl-description}
40/// This component defines an efficient multi-reader/single-writer
41/// lock mechanism, `bslmt::ReaderWriterMutexImpl`. It is designed to allow
42/// concurrent *read* access to a shared resource while still controlling
43/// *write* access.
44///
45/// Reader-writer locks are generally used for resources that are frequently
46/// read and less frequently updated. Unlike other lock mechanisms (e.g.,
47/// "mutexes"), reader-writer locks provide two distinct but mutually exclusive
48/// lock states: a *read* *lock* state, and a *write* *lock* state.
49///
50/// To the extent the implementation's underlying mutex prevents a thread from
51/// starving, readers can not be starved by writers and writers can not be
52/// starved by readers. If the underlying mutex, to some extent, favors
53/// re-acquisition of the mutex to allowing a new thread to obtain the mutex
54/// (e.g., the mutex obtained on Linux), this reader-writer lock is writer
55/// biased since writers can re-acquire the lock in the presence of readers but
56/// readers will not be able to re-acquire the lock in the presence of writers.
57///
58/// ## Usage {#bslmt_readerwritermuteximpl-usage}
59///
60///
61/// There is no usage example for this component since it is not meant for
62/// direct client use.
63/// @}
64/** @} */
65/** @} */
66
67/** @addtogroup bsl
68 * @{
69 */
70/** @addtogroup bslmt
71 * @{
72 */
73/** @addtogroup bslmt_readerwritermuteximpl
74 * @{
75 */
76
77#include <bslscm_version.h>
78
79#include <bsls_assert.h>
81#include <bsls_types.h>
82
83
84namespace bslmt {
85
86 // ===========================
87 // class ReaderWriterMutexImpl
88 // ===========================
89
90/// This class provides a multi-reader/single-writer lock mechanism.
91///
92/// See @ref bslmt_readerwritermuteximpl
93template <class ATOMIC_OP, class MUTEX, class SEMAPHORE>
95
96 // CLASS DATA
97 static const bsls::Types::Int64 k_READER_MASK = 0x00000000ffffffffLL;
98 static const bsls::Types::Int64 k_READER_INC = 0x0000000000000001LL;
99 static const bsls::Types::Int64 k_PENDING_WRITER_MASK
100 = 0x0fffffff00000000LL;
101 static const bsls::Types::Int64 k_PENDING_WRITER_INC
102 = 0x0000000100000000LL;
103 static const bsls::Types::Int64 k_WRITER = 0x1000000000000000LL;
104
105 // DATA
106 bsls::AtomicOperations::AtomicTypes::Int64 d_state; // atomic value
107 // used to track
108 // the state of
109 // this mutex
110
111 MUTEX d_mutex; // primary access
112 // control
113
114 SEMAPHORE d_semaphore; // used to capture
115 // writers
116 // released from
117 // 'd_mutex' but
118 // must wait for
119 // readers to
120 // finish
121
122 private:
123 // NOT IMPLEMENTED
126
127 public:
128 // CREATORS
129
130 /// Construct a reader/writer lock initialized to an unlocked state.
132
133 /// Destroy this object
135
136 // MANIPULATORS
137
138 /// Lock this reader-writer mutex for reading. If there are no active
139 /// or pending write locks, lock this mutex for reading and return
140 /// immediately. Otherwise, block until the read lock on this mutex is
141 /// acquired. Use `unlockRead` or `unlock` to release the lock on this mutex.
142 ///
143 /// \pre The behavior is undefined if this method is called from a
144 /// thread that already has a lock on this mutex.
145 void lockRead();
146
147 /// Lock this reader-writer mutex for writing. If there are no active
148 /// or pending locks on this mutex, lock this mutex for writing and
149 /// return immediately. Otherwise, block until the write lock on this
150 /// mutex is acquired. Use `unlockWrite` or `unlock` to release the lock on this mutex.
151 ///
152 /// \pre The behavior is undefined if this method is
153 /// called from a thread that already has a lock on this mutex.
154 void lockWrite();
155
156 /// Attempt to lock this reader-writer mutex for reading. Immediately
157 /// return 0 on success, and a non-zero value if there are active or
158 /// pending writers. If successful, `unlockRead` or `unlock` must be
159 /// used to release the lock on this mutex.
160 ///
161 /// \pre The behavior is undefined if this method is called from a thread that already has a lock on
162 /// this mutex.
164
165 /// Attempt to lock this reader-writer mutex for writing. Immediately
166 /// return 0 on success, and a non-zero value if there are active or
167 /// pending locks on this mutex. If successful, `unlockWrite` or
168 /// `unlock` must be used to release the lock on this mutex.
169 ///
170 /// \pre The behavior is undefined if this method is called from a thread that
171 /// already has a lock on this mutex.
173
174 /// Release the lock that the calling thread holds on this reader-writer mutex.
175 ///
176 /// \pre The behavior is undefined unless the calling thread
177 /// currently has a lock on this mutex.
178 void unlock();
179
180 /// Release the read lock that the calling thread holds on this reader-writer mutex.
181 ///
182 /// \pre The behavior is undefined unless the calling
183 /// thread currently has a read lock on this mutex.
185
186 /// Release the write lock that the calling thread holds on this reader-writer mutex.
187 ///
188 /// \pre The behavior is undefined unless the calling
189 /// thread currently has a write lock on this mutex.
191
192 // ACCESSORS
193
194 /// Return `true` if this reader-write mutex is currently read locked or
195 /// write locked, and `false` otherwise.
196 bool isLocked() const;
197
198 /// Return `true` if this reader-write mutex is currently read locked,
199 /// and `false` otherwise.
200 bool isLockedRead() const;
201
202 /// Return `true` if this reader-write mutex is currently write locked,
203 /// and `false` otherwise.
204 bool isLockedWrite() const;
205};
206
207// ============================================================================
208// INLINE DEFINITIONS
209// ============================================================================
210
211 // ---------------------------
212 // class ReaderWriterMutexImpl
213 // ---------------------------
214
215// CREATORS
216template <class ATOMIC_OP, class MUTEX, class SEMAPHORE>
217inline
222
223// MANIPULATORS
224template <class ATOMIC_OP, class MUTEX, class SEMAPHORE>
225inline
227{
228 bsls::Types::Int64 state = ATOMIC_OP::getInt64(&d_state);
229 bsls::Types::Int64 expState;
230
231 do {
232 // If there are no actual or pending writers, the lock can be obtained
233 // by simply incrementing the reader count. This results, typically,
234 // in a substantial performance benefit when there are very few writers
235 // in the system and no noticible degredation in other scenarios.
236
237 if (state & (k_WRITER | k_PENDING_WRITER_MASK)) {
238 d_mutex.lock();
239 ATOMIC_OP::addInt64AcqRel(&d_state, k_READER_INC);
240 d_mutex.unlock();
241 return; // RETURN
242 }
243
244 expState = state;
245 state = ATOMIC_OP::testAndSwapInt64AcqRel(&d_state,
246 state,
247 state + k_READER_INC);
248 } while (state != expState);
249}
250
251template <class ATOMIC_OP, class MUTEX, class SEMAPHORE>
252inline
254{
255 // The presence of a pending writer must be noted before attempting the
256 // 'mutex.lock' in case this thread blocks on the mutex lock operation.
257
258 ATOMIC_OP::addInt64AcqRel(&d_state, k_PENDING_WRITER_INC);
259 d_mutex.lock();
260 if (ATOMIC_OP::addInt64NvAcqRel(&d_state,
261 k_WRITER - k_PENDING_WRITER_INC)
262 & k_READER_MASK) {
263 // There must be no readers present to obtain the write lock. By
264 // obtaining the mutex, there can be no new readers obtaining a read
265 // lock (ensuring this lock is not reader biased). If there are
266 // currently readers present, the last reader to release its read lock
267 // will 'post' to 'd_semaphore'. Note that, since the locking
268 // primitive is a semaphore, the timing of the 'wait' and the 'post' is
269 // irrelevant.
270
271 d_semaphore.wait();
272 }
273}
274
275template <class ATOMIC_OP, class MUTEX, class SEMAPHORE>
276inline
278{
279 bsls::Types::Int64 state = ATOMIC_OP::getInt64(&d_state);
280
281 // If there are no actual or pending writers, the lock can be obtained by
282 // simply incrementing the reader count. Since this method must return
283 // "immediately" if the lock is not obtained, only one attempt will be
284 // performed.
285
286 if (0 == (state & (k_WRITER | k_PENDING_WRITER_MASK))) {
287 if (state == ATOMIC_OP::testAndSwapInt64AcqRel(&d_state,
288 state,
289 state + k_READER_INC)) {
290 return 0; // RETURN
291 }
292 }
293
294 // To accomodate the possibility of mutex re-acquisition being important
295 // for the performance characteristics of this lock, the mutex acquisition
296 // must be attempted.
297
298 if (0 == d_mutex.tryLock()) {
299 ATOMIC_OP::addInt64AcqRel(&d_state, k_READER_INC);
300 d_mutex.unlock();
301 return 0; // RETURN
302 }
303 return 1;
304}
305
306template <class ATOMIC_OP, class MUTEX, class SEMAPHORE>
307inline
309{
310 // To obtain a write lock, 'd_mutex' must be obtained *and* there must be
311 // no readers.
312
313 if (0 == d_mutex.tryLock()) {
314 bsls::Types::Int64 state = ATOMIC_OP::getInt64Acquire(&d_state);
315
316 if (0 == (state & k_READER_MASK)) {
317 // Since the mutex is obtained and there are no readers (and none
318 // can enter while the mutex is held), the lock has been obtained.
319
320 ATOMIC_OP::addInt64AcqRel(&d_state, k_WRITER);
321
322 return 0; // RETURN
323 }
324 d_mutex.unlock();
325 }
326 return 1;
327}
328
329template <class ATOMIC_OP, class MUTEX, class SEMAPHORE>
330inline
332{
333 // A caller of 'unlock', by contract, is either the owner of a read lock or
334 // the owner of the write lock. If the caller is the owner of the write
335 // lock, there are no readers and there can not be readers until after the
336 // 'unlockWrite' completes. If the caller owns a read lock, 'd_state' must
337 // reflect at least one reader until the 'unlockRead' completes. In either
338 // case, the chosen branch of the following 'if' is correct.
339
340 if (ATOMIC_OP::getInt64Acquire(&d_state) & k_READER_MASK) {
341 unlockRead();
342 }
343 else {
344 unlockWrite();
345 }
346}
347
348template <class ATOMIC_OP, class MUTEX, class SEMAPHORE>
349inline
351{
352 BSLS_ASSERT_SAFE(0 < (ATOMIC_OP::getInt64Acquire(&d_state)
353 & k_READER_MASK));
354
355 bsls::Types::Int64 state = ATOMIC_OP::addInt64Nv(&d_state, -k_READER_INC);
356
357 // If this is the last reader and there is a pending writer who obtained
358 // 'd_mutex' (and hence will be calling 'wait' on 'd_semaphore'), 'post' to
359 // 'd_semaphore' to allow the pending writer to complete obtaining the
360 // write lock.
361
362 if (0 == (state & k_READER_MASK) && (state & k_WRITER)) {
363 d_semaphore.post();
364 }
365}
366
367template <class ATOMIC_OP, class MUTEX, class SEMAPHORE>
368inline
370{
371 BSLS_ASSERT_SAFE(k_WRITER == (ATOMIC_OP::getInt64Acquire(&d_state)
372 & k_WRITER));
373
374 ATOMIC_OP::addInt64AcqRel(&d_state, -k_WRITER);
375 d_mutex.unlock();
376}
377
378// ACCESSORS
379template <class ATOMIC_OP, class MUTEX, class SEMAPHORE>
380inline
382{
383 bsls::Types::Int64 state = ATOMIC_OP::getInt64Acquire(&d_state);
384 return (state & k_READER_MASK) || (k_WRITER == (state & k_WRITER));
385}
386
387template <class ATOMIC_OP, class MUTEX, class SEMAPHORE>
388inline
390{
391 bsls::Types::Int64 state = ATOMIC_OP::getInt64Acquire(&d_state);
392 return state & k_READER_MASK;
393}
394
395template <class ATOMIC_OP, class MUTEX, class SEMAPHORE>
396inline
398{
399 bsls::Types::Int64 state = ATOMIC_OP::getInt64Acquire(&d_state);
400 return k_WRITER == (state & k_WRITER);
401}
402
403} // close package namespace
404
405
406#endif
407
408// ----------------------------------------------------------------------------
409// Copyright 2016 Bloomberg Finance L.P.
410//
411// Licensed under the Apache License, Version 2.0 (the "License");
412// you may not use this file except in compliance with the License.
413// You may obtain a copy of the License at
414//
415// http://www.apache.org/licenses/LICENSE-2.0
416//
417// Unless required by applicable law or agreed to in writing, software
418// distributed under the License is distributed on an "AS IS" BASIS,
419// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
420// See the License for the specific language governing permissions and
421// limitations under the License.
422// ----------------------------- END-OF-FILE ----------------------------------
423
424/** @} */
425/** @} */
426/** @} */
Definition bslmt_readerwritermuteximpl.h:94
bool isLockedRead() const
Definition bslmt_readerwritermuteximpl.h:389
void lockRead()
Definition bslmt_readerwritermuteximpl.h:226
bool isLockedWrite() const
Definition bslmt_readerwritermuteximpl.h:397
void unlock()
Definition bslmt_readerwritermuteximpl.h:331
void unlockRead()
Definition bslmt_readerwritermuteximpl.h:350
void unlockWrite()
Definition bslmt_readerwritermuteximpl.h:369
ReaderWriterMutexImpl()
Construct a reader/writer lock initialized to an unlocked state.
Definition bslmt_readerwritermuteximpl.h:218
int tryLockRead()
Definition bslmt_readerwritermuteximpl.h:277
~ReaderWriterMutexImpl()
Destroy this object.
void lockWrite()
Definition bslmt_readerwritermuteximpl.h:253
bool isLocked() const
Definition bslmt_readerwritermuteximpl.h:381
int tryLockWrite()
Definition bslmt_readerwritermuteximpl.h:308
#define BSLS_ASSERT_SAFE(X)
Definition bsls_assert.h:1917
#define BSLS_IDENT(str)
BSLS_IDENT() - insert string into .comment binary segment (if supported)
Definition bsls_ident.h:238
Definition bslmt_barrier.h:344
long long Int64
Definition bsls_types.h:134