BDE 4.39.x Production Release
Loading...
Searching...
No Matches
bslmt_readlockguard.h
Go to the documentation of this file.
1/// @file bslmt_readlockguard.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bslmt_readlockguard.h -*-C++-*-
8#ifndef INCLUDED_BSLMT_READLOCKGUARD
9#define INCLUDED_BSLMT_READLOCKGUARD
10
11#include <bsls_ident.h>
12BSLS_IDENT("$Id: $")
13
14/// @defgroup bslmt_readlockguard bslmt_readlockguard
15/// @brief Provide generic scoped guards for read synchronization objects.
16/// @addtogroup bsl
17/// @{
18/// @addtogroup bslmt
19/// @{
20/// @addtogroup bslmt_readlockguard
21/// @{
22///
23/// <h1> Outline </h1>
24/// * <a href="#bslmt_readlockguard-purpose"> Purpose</a>
25/// * <a href="#bslmt_readlockguard-classes"> Classes </a>
26/// * <a href="#bslmt_readlockguard-description"> Description </a>
27/// * <a href="#bslmt_readlockguard-behavior-of-the-release-method"> Behavior of the release Method </a>
28/// * <a href="#bslmt_readlockguard-usage"> Usage </a>
29/// * <a href="#bslmt_readlockguard-example-1-basic-usage"> Example 1: Basic Usage </a>
30///
31/// # Purpose {#bslmt_readlockguard-purpose}
32/// Provide generic scoped guards for read synchronization objects.
33///
34/// # Classes {#bslmt_readlockguard-classes}
35///
36/// - bslmt::ReadLockGuard: automatic locking-unlocking for read access
37/// - bslmt::ReadLockGuardUnlock: automatic unlocking-locking for read access
38/// - bslmt::ReadLockGuardTryLock: automatic non-blocking locking-unlocking
39/// - bslmt::LockReadGuard: DEPRECATED
40///
41/// @see bslmt_lockguard, bslmt_writelockguard
42///
43/// # Description {#bslmt_readlockguard-description}
44/// This component provides generic guards, `bslmt::ReadLockGuard`,
45/// `bslmt::ReadLockGuardUnlock`, and `bslmt::ReadLockGuardTryLock`, to
46/// automatically lock and unlock an external synchronization object for
47/// reading. The synchronization object can be any type (e.g.,
48/// `bslmt::ReaderWriterLock`) that provides the following methods:
49/// @code
50/// void lockRead();
51/// void unlock();
52/// @endcode
53/// Both `bslmt::ReadLockGuard` and `bslmt::ReadLockGuardUnlock` implement the
54/// "construction is acquisition, destruction is release" idiom. During
55/// construction, `bslmt::ReadLockGuard` automatically calls `lockRead` on the
56/// user-supplied object, and `unlock` when it is destroyed (unless released).
57/// `bslmt::ReadLockGuardUnlock` does the opposite -- it invokes the `unlock`
58/// method when constructed and the `lockRead` method when destroyed.
59///
60/// A third type of guard, `bslmt::ReadLockGuardTryLock`, attempts to acquire a
61/// lock, and if acquisition succeeds, releases it upon destruction. Since the
62/// acquisition is done at construction time, it is not possible to return a
63/// value to indicate success. Rather, the `bslmt::ReadLockGuardTryLock`
64/// contains a pointer to the synchronization object if `tryLock` succeeds, and
65/// is null otherwise. The synchronization object can be any type (e.g.,
66/// `bslmt::Mutex` or `bslmt::RecursiveMutex`) that provides the following
67/// methods:
68/// @code
69/// int tryLockRead();
70/// void unlock();
71/// @endcode
72/// Note that objects of none of these guard types assumes ownership of the
73/// synchronization object provided at construction. Also note that objects of
74/// all of the guard types may be constructed with a null `lock` whereby the
75/// constructed guard objects guard no lock. The destructor of each of the
76/// guard types has no effect if no lock is under management.
77///
78/// ## Behavior of the release Method {#bslmt_readlockguard-behavior-of-the-release-method}
79///
80///
81/// Like all BDE guard classes, each of the three `bslmt::ReadLockGuard*`
82/// classes provides a `release` method that terminates the guard's management
83/// of any lock object that the guard holds. The `release` method has *no*
84/// *effect* on the state of the lock object.
85///
86/// In particular, `bslmt::ReadLockGuard::release` does not unlock the lock
87/// object under management. If a user wants to release the lock object *and*
88/// unlock the lock object (because the lock is no longer required before the
89/// guard goes out of scope), the following idiom can be used:
90/// @code
91/// // 'guard' is an existing guard of type 'bslmt::ReadLockGuard<my_RLock>',
92/// // created in a scope that we do not control.
93///
94/// {
95/// // ... Do work that requires the lock.
96///
97/// // We know that the lock is no longer needed.
98///
99/// my_RLock *rlock = guard.release();
100///
101/// // 'rlock' is no longer managed, but is *still* *locked*.
102///
103/// rlock->unlock();
104///
105/// // ... Do work that does not require the lock.
106/// }
107/// @endcode
108///
109/// ## Usage {#bslmt_readlockguard-usage}
110///
111///
112/// This section illustrates intended use of this component.
113///
114/// ### Example 1: Basic Usage {#bslmt_readlockguard-example-1-basic-usage}
115///
116///
117/// Use this component to ensure that in the event of an exception or exit from
118/// any point in a given scope, the synchronization object will be properly
119/// unlocked. The following function, `errorProneFunc`, is overly complex, not
120/// exception safe, and contains a bug.
121/// @code
122/// static void errorProneFunc(const my_Object *obj, my_RWLock *rwlock)
123/// {
124/// rwlock->lockRead();
125/// if (someCondition) {
126/// obj->someMethod();
127/// rwlock->unlock();
128/// return; // RETURN
129/// } else if (someOtherCondition) {
130/// obj->someOtherMethod();
131/// // MISTAKE! forgot to unlock rwlock
132/// return; // RETURN
133/// }
134/// obj->defaultMethod();
135/// rwlock->unlock();
136/// return;
137/// }
138/// @endcode
139/// The function can be rewritten with a cleaner and safer implementation using
140/// a guard object. The `safeFunc` function is simpler than `errorProneFunc`,
141/// is exception safe, and avoids the multiple calls to unlock that can be a
142/// source of errors.
143/// @code
144/// static void safeFunc(const my_Object *obj, my_RWLock *rwlock)
145/// {
146/// bslmt::ReadLockGuard<my_RWLock> guard(rwlock);
147/// if (someCondition) {
148/// obj->someMethod();
149/// return; // RETURN
150/// } else if (someOtherCondition) {
151/// obj->someOtherMethod();
152/// // OK, rwlock is automatically unlocked
153/// return; // RETURN
154/// }
155/// obj->defaultMethod();
156/// return;
157/// }
158/// @endcode
159/// When blocking while acquiring the lock is not desirable, one may instead use
160/// a `bslmt::ReadLockGuardTryLock` in the typical following fashion:
161/// @code
162/// /// Perform task and return positive value if locking succeeds. Return
163/// /// 0 if locking fails.
164/// static int safeButNonBlockingFunc(const my_Object *obj, my_RWLock *rwlock)
165/// {
166/// const int RETRIES = 1; // use higher values for higher success rate
167/// bslmt::ReadLockGuardTryLock<my_RWLock> guard(rwlock, RETRIES);
168/// if (guard.ptr()) { // rwlock is locked
169/// if (someCondition) {
170/// obj->someMethod();
171/// return 2; // RETURN
172/// } else if (someOtherCondition) {
173/// obj->someOtherMethod();
174/// return 3; // RETURN
175/// }
176/// obj->defaultMethod();
177/// return 1; // RETURN
178/// }
179/// return 0;
180/// }
181/// @endcode
182/// If the underlying lock object provides an upgrade to a lock for write (as
183/// does `bslmt::ReaderWriterLock` with the `upgradeToWriteLock` function, for
184/// example), this can be safely used in conjunction with
185/// `bslmt::ReadLockGuard`, as long as the same `unlock` method is used to
186/// release both kinds of locks. The following method illustrates this usage:
187/// @code
188/// static void safeUpdateFunc(my_Object *obj, my_RWLock *rwlock)
189/// {
190/// const my_Object *constObj = obj;
191/// bslmt::ReadLockGuard<my_RWLock> guard(rwlock);
192/// if (someUpgradeCondition) {
193/// rwlock->upgradeToWriteLock();
194/// obj->someUpgradeMethod();
195/// return; // RETURN
196/// } else if (someOtherCondition) {
197/// constObj->someOtherMethod();
198/// // OK, rwlock is automatically unlocked
199/// return; // RETURN
200/// }
201/// constObj->defaultMethod();
202/// return;
203/// }
204/// @endcode
205/// In the above code, the call to `upgradeToWriteLock` is not necessarily
206/// atomic, as the upgrade may release the lock for read and be interrupted
207/// before getting a lock for write. It is possible to guarantee atomicity (as
208/// does `bslmt::ReaderWriterLock` if the `lockReadReserveWrite` function is
209/// used instead of `lockRead`, for example), but the standard constructor
210/// should not be used. Instead, the `lockReadReserveWrite` lock function
211/// should be used explicitly, and the guard constructed with an object which is
212/// already locked. The following method illustrates this usage:
213/// @code
214/// static void safeAtomicUpdateFunc(my_Object *obj, my_RWLock *rwlock)
215/// {
216/// const my_Object *constObj = obj;
217/// rwlock->lockReadReserveWrite();
218/// const int PRELOCKED = 1;
219/// bslmt::ReadLockGuard<my_RWLock> guard(rwlock, PRELOCKED);
220/// if (someUpgradeCondition) {
221/// rwlock->upgradeToWriteLock();
222/// obj->someUpgradeMethod();
223/// return; // RETURN
224/// } else if (someOtherCondition) {
225/// constObj->someOtherMethod();
226/// return; // RETURN
227/// }
228/// constObj->defaultMethod();
229/// return;
230/// }
231/// @endcode
232/// Note that in the code above, the function `rwlock->lockRead()` is never
233/// called, but is nevertheless required for the code to compile.
234///
235/// Instantiations of `bslmt::ReadLockGuardUnlock` can be interleaved with
236/// instantiations of `bslmt::ReadLockGuard` to create both critical sections
237/// and regions where the lock is released.
238/// @code
239/// void f(my_RWLock *rwlock)
240/// {
241/// bslmt::ReadLockGuard<my_RWLock> guard(rwlock);
242///
243/// // critical section here
244///
245/// {
246/// bslmt::ReadLockGuardUnlock<my_RWLock> guard(rwlock);
247///
248/// // rwlock is unlocked here
249///
250/// } // rwlock is locked again here
251///
252/// // critical section here
253///
254/// } // rwlock is unlocked here
255/// @endcode
256/// Care must be taken so as not to interleave guard objects in such a way as to
257/// cause an illegal sequence of calls on a lock (two sequential lock calls or
258/// two sequential unlock calls on a non-recursive read/write lock).
259/// @}
260/** @} */
261/** @} */
262
263/** @addtogroup bsl
264 * @{
265 */
266/** @addtogroup bslmt
267 * @{
268 */
269/** @addtogroup bslmt_readlockguard
270 * @{
271 */
272
273#include <bslscm_version.h>
274
275
276namespace bslmt {
277
278 // ===================
279 // class ReadLockGuard
280 // ===================
281
282/// This class template implements a guard for acquisition and release of
283/// read synchronization resources (i.e., reader locks).
284///
285/// See @ref bslmt_readlockguard
286template <class T>
288
289 // DATA
290 T *d_lock_p; // lock guarded by this object (held, not owned)
291
292 private:
293 // NOT IMPLEMENTED
295 ReadLockGuard<T>& operator=(const ReadLockGuard<T>&);
296
297 public:
298 // CREATORS
299
300 /// Create a scoped guard that conditionally manages the specified
301 /// `lock` (if non-null) and invokes `lock->lockRead()`. Supplying a null `lock` has no effect.
302 ///
303 /// \pre The behavior is undefined unless `lock`
304 /// (if non-null) is not already locked by this thread.
305 ///
306 /// \note Note that `lock` must remain valid throughout the lifetime of this guard, or
307 /// until `release` is called.
308 explicit ReadLockGuard(T *lock);
309
310 /// Create a scoped guard that conditionally manages the specified
311 /// `lock` (if non-null) and invokes `lock->lockRead()` if the specified
312 /// `alreadyLockedFlag` is `false`. Supplying a null `lock` has no effect.
313 ///
314 /// \pre The behavior is undefined unless the state of `lock` (if
315 /// non-null) is consistent with `alreadyLockedFlag`.
316 ///
317 /// \note Note that `alreadyLockedFlag` is used to indicate whether `lock` is in an
318 /// already-locked state when passed, so if `alreadyLockedFlag` is
319 /// `true` the `lock` method will *not* be called on the supplied
320 /// `lock`. Also note that `lock` must remain valid throughout the
321 /// lifetime of this guard, or until `release` is called.
322 ReadLockGuard(T *lock, bool alreadyLockedFlag);
323
324 /// Destroy this scoped guard and invoke the `unlock` method on the
325 /// lock object under management by this guard, if any. If no lock is
326 /// currently being managed, this method has no effect.
328
329 // MANIPULATORS
330
331 /// Return the address of the modifiable lock object under management by
332 /// this guard, and release the lock from further management by this
333 /// guard. If no lock is currently being managed, return 0 with no other effect.
334 ///
335 /// \note Note that this operation does *not* unlock the lock
336 /// object (if any) that was under management.
337 T *release();
338
339 // ACCESSORS
340
341 /// Return the address of the modifiable lock object under management by
342 /// this guard, or 0 if no lock is currently being managed.
343 T *ptr() const;
344};
345
346 // ===================
347 // class LockReadGuard
348 // ===================
349
350/// @deprecated Use @ref ReadLockGuard instead.
351///
352/// See @ref bslmt_readlockguard
353template <class T>
354class LockReadGuard : public ReadLockGuard<T> {
355
356 private:
357 // NOT IMPLEMENTED
359 LockReadGuard<T>& operator=(const LockReadGuard<T>&);
360
361 public:
362 // CREATORS
363
364 /// @deprecated Use @ref ReadLockGuard instead.
365 explicit LockReadGuard(T *lock);
366
367 /// @deprecated Use @ref ReadLockGuard instead.
368 LockReadGuard(T *lock, bool alreadyLockedFlag);
369};
370
371 // =========================
372 // class ReadLockGuardUnlock
373 // =========================
374
375/// This class template implements a guard for release and reacquisition
376/// of read synchronization resources (i.e., reader locks).
377///
378/// See @ref bslmt_readlockguard
379template <class T>
381
382 // DATA
383 T *d_lock_p; // lock guarded by this object (held, not owned)
384
385 private:
386 // NOT IMPLEMENTED
389
390 public:
391 // CREATORS
392
393 /// Create a scoped guard that conditionally manages the specified
394 /// `lock` (if non-null) and invokes `lock->unlock()`. Supplying a null `lock` has no effect.
395 ///
396 /// \pre The behavior is undefined unless `lock` (if non-null) is locked by this thread.
397 ///
398 /// \note Note that `lock` must remain
399 /// valid throughout the lifetime of this guard, or until `release` is
400 /// called.
401 explicit ReadLockGuardUnlock(T *lock);
402
403 /// Create a scoped guard that conditionally manages the specified
404 /// `lock` (if non-null) and invokes `lock->unlock()` if the specified
405 /// `alreadyUnlockedFlag` is `false`. Supplying a null `lock` has no effect.
406 ///
407 /// \pre The behavior is undefined unless the state of `lock` (if
408 /// non-null) is consistent with `alreadyUnlockedFlag`.
409 ///
410 /// \note Note that `alreadyUnlockedFlag` is used to indicate whether `lock` is in an
411 /// already-unlocked state when passed, so if `alreadyUnlockedFlag` is
412 /// `true` the `unlock` method will *not* be called on the supplied
413 /// `lock`. Also note that `lock` must remain valid throughout the
414 /// lifetime of this guard, or until `release` is called.
415 ReadLockGuardUnlock(T *lock, bool alreadyUnlockedFlag);
416
417 /// Destroy this scoped guard and invoke the `lockRead` method on the
418 /// lock object under management by this guard, if any. If no lock is
419 /// currently being managed, this method has no effect.
421
422 // MANIPULATORS
423
424 /// Return the address of the modifiable lock object under management by
425 /// this guard, and release the lock from further management by this
426 /// guard. If no lock is currently being managed, return 0 with no other effect.
427 ///
428 /// \note Note that this operation does *not* lock the lock
429 /// object (if any) that was under management.
430 T *release();
431
432 // ACCESSORS
433
434 /// Return the address of the modifiable lock object under management by
435 /// this guard, or 0 if no lock is currently being managed.
436 T *ptr() const;
437};
438
439 // ==========================
440 // class ReadLockGuardTryLock
441 // ==========================
442
443/// This class template implements a guard for tentative acquisition and
444/// release of read synchronization resources (i.e., reader locks).
445///
446/// See @ref bslmt_readlockguard
447template <class T>
449
450 // DATA
451 T *d_lock_p; // lock guarded by this object (held, not owned)
452
453 private:
454 // NOT IMPLEMENTED
457
458 public:
459 // CREATORS
460
461 /// Create a scoped guard that conditionally manages the specified
462 /// `lock` (if non-null) and invokes `lock->tryLockRead()` until the
463 /// lock is acquired for reading, or until the optionally specified
464 /// `attempts` have been made to acquire the lock. If `attempts` is not
465 /// specified only one attempt is made to acquire the lock. Supplying a null `lock` has no effect.
466 ///
467 /// \pre The behavior is undefined unless `lock`
468 /// (if non-null) is not already locked by this thread and `0 < attempts`.
469 ///
470 /// \note Note that `lock` must remain valid throughout the
471 /// lifetime of this guard, or until `release` is called.
472 explicit ReadLockGuardTryLock(T *lock, int attempts = 1);
473
474 /// Destroy this scoped guard and invoke the `unlock` method on the
475 /// lock object under management by this guard, if any. If no lock is
476 /// currently being managed, this method has no effect.
478
479 // MANIPULATORS
480
481 /// Return the address of the modifiable lock object under management by
482 /// this guard, and release the lock from further management by this
483 /// guard. If no lock is currently being managed, return 0 with no other effect.
484 ///
485 /// \note Note that this operation does *not* unlock the lock
486 /// object (if any) that was under management.
487 T *release();
488
489 // ACCESSORS
490
491 /// Return the address of the modifiable lock object under management by
492 /// this guard, or 0 if no lock is currently being managed.
493 T *ptr() const;
494};
495
496// ============================================================================
497// INLINE DEFINITIONS
498// ============================================================================
499
500 // -------------------
501 // class ReadLockGuard
502 // -------------------
503
504// CREATORS
505template <class T>
506inline
508: d_lock_p(lock)
509{
510 if (d_lock_p) {
511 d_lock_p->lockRead();
512 }
513}
514
515template <class T>
516inline
517ReadLockGuard<T>::ReadLockGuard(T *lock, bool alreadyLockedFlag)
518: d_lock_p(lock)
519{
520 if (d_lock_p && !alreadyLockedFlag) {
521 d_lock_p->lockRead();
522 }
523}
524
525template <class T>
526inline
528{
529 if (d_lock_p) {
530 d_lock_p->unlock();
531 }
532}
533
534// MANIPULATORS
535template <class T>
536inline
538{
539 T *lock = d_lock_p;
540 d_lock_p = 0;
541 return lock;
542}
543
544// ACCESSORS
545template <class T>
546inline
548{
549 return d_lock_p;
550}
551
552 // -------------------
553 // class LockReadGuard
554 // -------------------
555
556// CREATORS
557template <class T>
558inline
560: ReadLockGuard<T>(lock)
561{
562}
563
564template <class T>
565inline
566LockReadGuard<T>::LockReadGuard(T *lock, bool alreadyLockedFlag)
567: ReadLockGuard<T>(lock, alreadyLockedFlag)
568{
569}
570
571 // -------------------------
572 // class ReadLockGuardUnlock
573 // -------------------------
574
575// CREATORS
576template <class T>
577inline
579: d_lock_p(lock)
580{
581 if (d_lock_p) {
582 d_lock_p->unlock();
583 }
584}
585
586template <class T>
587inline
589 bool alreadyUnlockedFlag)
590: d_lock_p(lock)
591{
592 if (d_lock_p && !alreadyUnlockedFlag) {
593 d_lock_p->unlock();
594 }
595}
596
597template <class T>
598inline
600{
601 if (d_lock_p) {
602 d_lock_p->lockRead();
603 }
604}
605
606// MANIPULATORS
607template <class T>
608inline
610{
611 T *lock = d_lock_p;
612 d_lock_p = 0;
613 return lock;
614}
615
616// ACCESSORS
617template <class T>
618inline
620{
621 return d_lock_p;
622}
623
624 // --------------------------
625 // class ReadLockGuardTryLock
626 // --------------------------
627
628// CREATORS
629template <class T>
631: d_lock_p(0)
632{
633 if (lock) {
634 while (attempts--) {
635 if (!lock->tryLockRead()) {
636 d_lock_p = lock;
637 break;
638 }
639 }
640 }
641}
642
643template <class T>
644inline
646{
647 if (d_lock_p) {
648 d_lock_p->unlock();
649 }
650}
651
652// MANIPULATORS
653template <class T>
654inline
656{
657 T *lock = d_lock_p;
658 d_lock_p = 0;
659 return lock;
660}
661
662// ACCESSORS
663template <class T>
664inline
666{
667 return d_lock_p;
668}
669
670} // close package namespace
671
672
673#endif
674
675// ----------------------------------------------------------------------------
676// Copyright 2015 Bloomberg Finance L.P.
677//
678// Licensed under the Apache License, Version 2.0 (the "License");
679// you may not use this file except in compliance with the License.
680// You may obtain a copy of the License at
681//
682// http://www.apache.org/licenses/LICENSE-2.0
683//
684// Unless required by applicable law or agreed to in writing, software
685// distributed under the License is distributed on an "AS IS" BASIS,
686// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
687// See the License for the specific language governing permissions and
688// limitations under the License.
689// ----------------------------- END-OF-FILE ----------------------------------
690
691/** @} */
692/** @} */
693/** @} */
Definition bslmt_readlockguard.h:354
Definition bslmt_readlockguard.h:448
T * release()
Definition bslmt_readlockguard.h:655
~ReadLockGuardTryLock()
Definition bslmt_readlockguard.h:645
T * ptr() const
Definition bslmt_readlockguard.h:665
Definition bslmt_readlockguard.h:380
T * ptr() const
Definition bslmt_readlockguard.h:619
T * release()
Definition bslmt_readlockguard.h:609
~ReadLockGuardUnlock()
Definition bslmt_readlockguard.h:599
Definition bslmt_readlockguard.h:287
~ReadLockGuard()
Definition bslmt_readlockguard.h:527
T * release()
Definition bslmt_readlockguard.h:537
T * ptr() const
Definition bslmt_readlockguard.h:547
#define BSLS_IDENT(str)
BSLS_IDENT() - insert string into .comment binary segment (if supported)
Definition bsls_ident.h:238
Definition bslmt_barrier.h:344