BDE 4.14.0 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 // NOT IMPLEMENTED
125
126 public:
127 // CREATORS
128
129 /// Construct a reader/writer lock initialized to an unlocked state.
131
132 /// Destroy this object
134
135 // MANIPULATORS
136
137 /// Lock this reader-writer mutex for reading. If there are no active
138 /// or pending write locks, lock this mutex for reading and return
139 /// immediately. Otherwise, block until the read lock on this mutex is
140 /// acquired. Use `unlockRead` or `unlock` to release the lock on this
141 /// mutex. The behavior is undefined if this method is called from a
142 /// thread that already has a lock on this mutex.
143 void lockRead();
144
145 /// Lock this reader-writer mutex for writing. If there are no active
146 /// or pending locks on this mutex, lock this mutex for writing and
147 /// return immediately. Otherwise, block until the write lock on this
148 /// mutex is acquired. Use `unlockWrite` or `unlock` to release the
149 /// lock on this mutex. The behavior is undefined if this method is
150 /// called from a thread that already has a lock on this mutex.
151 void lockWrite();
152
153 /// Attempt to lock this reader-writer mutex for reading. Immediately
154 /// return 0 on success, and a non-zero value if there are active or
155 /// pending writers. If successful, `unlockRead` or `unlock` must be
156 /// used to release the lock on this mutex. The behavior is undefined
157 /// if this method is called from a thread that already has a lock on
158 /// this mutex.
160
161 /// Attempt to lock this reader-writer mutex for writing. Immediately
162 /// return 0 on success, and a non-zero value if there are active or
163 /// pending locks on this mutex. If successful, `unlockWrite` or
164 /// `unlock` must be used to release the lock on this mutex. The
165 /// behavior is undefined if this method is called from a thread that
166 /// already has a lock on this mutex.
168
169 /// Release the lock that the calling thread holds on this reader-writer
170 /// mutex. The behavior is undefined unless the calling thread
171 /// currently has a lock on this mutex.
172 void unlock();
173
174 /// Release the read lock that the calling thread holds on this
175 /// reader-writer mutex. The behavior is undefined unless the calling
176 /// thread currently has a read lock on this mutex.
178
179 /// Release the write lock that the calling thread holds on this
180 /// reader-writer mutex. The behavior is undefined unless the calling
181 /// thread currently has a write lock on this mutex.
183
184 // ACCESSORS
185
186 /// Return `true` if this reader-write mutex is currently read locked or
187 /// write locked, and `false` otherwise.
188 bool isLocked() const;
189
190 /// Return `true` if this reader-write mutex is currently read locked,
191 /// and `false` otherwise.
192 bool isLockedRead() const;
193
194 /// Return `true` if this reader-write mutex is currently write locked,
195 /// and `false` otherwise.
196 bool isLockedWrite() const;
197};
198
199// ============================================================================
200// INLINE DEFINITIONS
201// ============================================================================
202
203 // ---------------------------
204 // class ReaderWriterMutexImpl
205 // ---------------------------
206
207// CREATORS
208template <class ATOMIC_OP, class MUTEX, class SEMAPHORE>
209inline
214
215// MANIPULATORS
216template <class ATOMIC_OP, class MUTEX, class SEMAPHORE>
217inline
219{
220 bsls::Types::Int64 state = ATOMIC_OP::getInt64(&d_state);
221 bsls::Types::Int64 expState;
222
223 do {
224 // If there are no actual or pending writers, the lock can be obtained
225 // by simply incrementing the reader count. This results, typically,
226 // in a substantial performance benefit when there are very few writers
227 // in the system and no noticible degredation in other scenarios.
228
229 if (state & (k_WRITER | k_PENDING_WRITER_MASK)) {
230 d_mutex.lock();
231 ATOMIC_OP::addInt64AcqRel(&d_state, k_READER_INC);
232 d_mutex.unlock();
233 return; // RETURN
234 }
235
236 expState = state;
237 state = ATOMIC_OP::testAndSwapInt64AcqRel(&d_state,
238 state,
239 state + k_READER_INC);
240 } while (state != expState);
241}
242
243template <class ATOMIC_OP, class MUTEX, class SEMAPHORE>
244inline
246{
247 // The presence of a pending writer must be noted before attempting the
248 // 'mutex.lock' in case this thread blocks on the mutex lock operation.
249
250 ATOMIC_OP::addInt64AcqRel(&d_state, k_PENDING_WRITER_INC);
251 d_mutex.lock();
252 if (ATOMIC_OP::addInt64NvAcqRel(&d_state,
253 k_WRITER - k_PENDING_WRITER_INC)
254 & k_READER_MASK) {
255 // There must be no readers present to obtain the write lock. By
256 // obtaining the mutex, there can be no new readers obtaining a read
257 // lock (ensuring this lock is not reader biased). If there are
258 // currently readers present, the last reader to release its read lock
259 // will 'post' to 'd_semaphore'. Note that, since the locking
260 // primitive is a semaphore, the timing of the 'wait' and the 'post' is
261 // irrelevant.
262
263 d_semaphore.wait();
264 }
265}
266
267template <class ATOMIC_OP, class MUTEX, class SEMAPHORE>
268inline
270{
271 bsls::Types::Int64 state = ATOMIC_OP::getInt64(&d_state);
272
273 // If there are no actual or pending writers, the lock can be obtained by
274 // simply incrementing the reader count. Since this method must return
275 // "immediately" if the lock is not obtained, only one attempt will be
276 // performed.
277
278 if (0 == (state & (k_WRITER | k_PENDING_WRITER_MASK))) {
279 if (state == ATOMIC_OP::testAndSwapInt64AcqRel(&d_state,
280 state,
281 state + k_READER_INC)) {
282 return 0; // RETURN
283 }
284 }
285
286 // To accomodate the possibility of mutex re-acquisition being important
287 // for the performance characteristics of this lock, the mutex acquisition
288 // must be attempted.
289
290 if (0 == d_mutex.tryLock()) {
291 ATOMIC_OP::addInt64AcqRel(&d_state, k_READER_INC);
292 d_mutex.unlock();
293 return 0; // RETURN
294 }
295 return 1;
296}
297
298template <class ATOMIC_OP, class MUTEX, class SEMAPHORE>
299inline
301{
302 // To obtain a write lock, 'd_mutex' must be obtained *and* there must be
303 // no readers.
304
305 if (0 == d_mutex.tryLock()) {
306 bsls::Types::Int64 state = ATOMIC_OP::getInt64Acquire(&d_state);
307
308 if (0 == (state & k_READER_MASK)) {
309 // Since the mutex is obtained and there are no readers (and none
310 // can enter while the mutex is held), the lock has been obtained.
311
312 ATOMIC_OP::addInt64AcqRel(&d_state, k_WRITER);
313
314 return 0; // RETURN
315 }
316 d_mutex.unlock();
317 }
318 return 1;
319}
320
321template <class ATOMIC_OP, class MUTEX, class SEMAPHORE>
322inline
324{
325 // A caller of 'unlock', by contract, is either the owner of a read lock or
326 // the owner of the write lock. If the caller is the owner of the write
327 // lock, there are no readers and there can not be readers until after the
328 // 'unlockWrite' completes. If the caller owns a read lock, 'd_state' must
329 // reflect at least one reader until the 'unlockRead' completes. In either
330 // case, the chosen branch of the following 'if' is correct.
331
332 if (ATOMIC_OP::getInt64Acquire(&d_state) & k_READER_MASK) {
333 unlockRead();
334 }
335 else {
336 unlockWrite();
337 }
338}
339
340template <class ATOMIC_OP, class MUTEX, class SEMAPHORE>
341inline
343{
344 BSLS_ASSERT_SAFE(0 < (ATOMIC_OP::getInt64Acquire(&d_state)
345 & k_READER_MASK));
346
347 bsls::Types::Int64 state = ATOMIC_OP::addInt64Nv(&d_state, -k_READER_INC);
348
349 // If this is the last reader and there is a pending writer who obtained
350 // 'd_mutex' (and hence will be calling 'wait' on 'd_semaphore'), 'post' to
351 // 'd_semaphore' to allow the pending writer to complete obtaining the
352 // write lock.
353
354 if (0 == (state & k_READER_MASK) && (state & k_WRITER)) {
355 d_semaphore.post();
356 }
357}
358
359template <class ATOMIC_OP, class MUTEX, class SEMAPHORE>
360inline
362{
363 BSLS_ASSERT_SAFE(k_WRITER == (ATOMIC_OP::getInt64Acquire(&d_state)
364 & k_WRITER));
365
366 ATOMIC_OP::addInt64AcqRel(&d_state, -k_WRITER);
367 d_mutex.unlock();
368}
369
370// ACCESSORS
371template <class ATOMIC_OP, class MUTEX, class SEMAPHORE>
372inline
374{
375 bsls::Types::Int64 state = ATOMIC_OP::getInt64Acquire(&d_state);
376 return (state & k_READER_MASK) || (k_WRITER == (state & k_WRITER));
377}
378
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;
385}
386
387template <class ATOMIC_OP, class MUTEX, class SEMAPHORE>
388inline
390{
391 bsls::Types::Int64 state = ATOMIC_OP::getInt64Acquire(&d_state);
392 return k_WRITER == (state & k_WRITER);
393}
394
395} // close package namespace
396
397
398#endif
399
400// ----------------------------------------------------------------------------
401// Copyright 2016 Bloomberg Finance L.P.
402//
403// Licensed under the Apache License, Version 2.0 (the "License");
404// you may not use this file except in compliance with the License.
405// You may obtain a copy of the License at
406//
407// http://www.apache.org/licenses/LICENSE-2.0
408//
409// Unless required by applicable law or agreed to in writing, software
410// distributed under the License is distributed on an "AS IS" BASIS,
411// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
412// See the License for the specific language governing permissions and
413// limitations under the License.
414// ----------------------------- END-OF-FILE ----------------------------------
415
416/** @} */
417/** @} */
418/** @} */
Definition bslmt_readerwritermuteximpl.h:94
bool isLockedRead() const
Definition bslmt_readerwritermuteximpl.h:381
void lockRead()
Definition bslmt_readerwritermuteximpl.h:218
bool isLockedWrite() const
Definition bslmt_readerwritermuteximpl.h:389
void unlock()
Definition bslmt_readerwritermuteximpl.h:323
void unlockRead()
Definition bslmt_readerwritermuteximpl.h:342
void unlockWrite()
Definition bslmt_readerwritermuteximpl.h:361
ReaderWriterMutexImpl()
Construct a reader/writer lock initialized to an unlocked state.
Definition bslmt_readerwritermuteximpl.h:210
int tryLockRead()
Definition bslmt_readerwritermuteximpl.h:269
~ReaderWriterMutexImpl()
Destroy this object.
void lockWrite()
Definition bslmt_readerwritermuteximpl.h:245
bool isLocked() const
Definition bslmt_readerwritermuteximpl.h:373
int tryLockWrite()
Definition bslmt_readerwritermuteximpl.h:300
#define BSLS_ASSERT_SAFE(X)
Definition bsls_assert.h:1762
#define BSLS_IDENT(str)
Definition bsls_ident.h:195
Definition bslmt_barrier.h:344
long long Int64
Definition bsls_types.h:132