BDE 4.14.0 Production release
Loading...
Searching...
No Matches
bslmt_writelockguard

Detailed Description

Outline

Purpose

Provide generic scoped guards for write synchronization objects.

Classes

See also
bslmt_lockguard, bslmt_readlockguard

Description

This component provides generic guards, bslmt::WriteLockGuard, bslmt::WriteLockGuardUnlock, and bslmt::WriteLockGuardTryLock, to automatically lock and unlock an external synchronization object for writing. The synchronization object can be any type (e.g., bslmt::ReaderWriterLock) that provides the following methods:

void lockWrite();
void unlock();

Both bslmt::WriteLockGuard and bslmt::WriteLockGuardUnlock implement the "construction is acquisition, destruction is release" idiom. During construction, bslmt::WriteLockGuard automatically calls lockWrite on the user-supplied object, and unlock when it is destroyed (unless released). bslmt::WriteLockGuardUnlock does the opposite – it invokes the unlock method when constructed and the lockWrite method when destroyed.

A third type of guard, bslmt::WriteLockGuardTryLock, attempts to acquire a lock, and if acquisition succeeds, releases it upon destruction. Since the acquisition is done at construction time, it is not possible to return a value to indicate success. Rather, the bslmt::WriteLockGuardTryLock contains a pointer to the synchronization object if tryLock succeeds, and is null otherwise. The synchronization object can be any type (e.g., bslmt::Mutex or bslmt::RecursiveMutex) that provides the following methods:

int tryLockWrite();
void unlock();

Note that objects of neither guard type assumes ownership of the synchronization object passed at construction. Also note that objects of all of the guard types may be constructed with a null lock whereby the constructed guard objects guard no lock. The destructor of each of the guard types has no effect if no lock is under management.

Behavior of the release Method

Like all BDE guard classes, each of the three bslmt::WriteLockGuard* classes provides a release method that terminates the guard's management of any lock object that the guard holds. The release method has no effect on the state of the lock object.

In particular, bslmt::WriteLockGuard::release does not unlock the lock object under management. If a user wants to release the lock object and unlock the lock object (because the lock is no longer required before the guard goes out of scope), the following idiom can be used:

// 'guard' is an existing guard of type 'bslmt::WriteLockGuard<my_RLock>',
// created in a scope that we do not control.
{
// ... Do work that requires the lock.
// We know that the lock is no longer needed.
my_RLock *rlock = guard.release();
// 'rlock' is no longer managed, but is *still* *locked*.
rlock->unlock();
// ... Do work that does not require the lock.
}

Usage

This section illustrates intended use of this component.

Example 1: Basic Usage

Use this component to ensure that in the event of an exception or exit from any point in a given scope, the synchronization object will be properly unlocked. The following function, errorProneFunc, is overly complex, not exception safe, and contains a bug.

static void errorProneFunc(my_Object *obj, my_RWLock *rwlock)
{
rwlock->lockWrite();
if (someUpgradeCondition) {
obj->someUpgradeMethod();
rwlock->unlock();
return; // RETURN
} else if (someOtherUpgradeCondition) {
obj->someOtherUpgradeMethod();
// MISTAKE! forgot to unlock rwlock
return; // RETURN
}
obj->defaultUpgradeMethod();
rwlock->unlock();
return;
}

The function can be rewritten with a cleaner and safer implementation using a guard object. The safeFunc function is simpler than errorProneFunc, is exception safe, and avoids the multiple calls to unlock that can be a source of errors.

static void safeFunc(my_Object *obj, my_RWLock *rwlock)
{
if (someUpgradeCondition) {
obj->someUpgradeMethod();
return; // RETURN
} else if (someOtherUpgradeCondition) {
obj->someOtherUpgradeMethod();
// OK, rwlock is automatically unlocked
return; // RETURN
}
obj->defaultUpgradeMethod();
return;
}
Definition bslmt_writelockguard.h:221

When blocking while acquiring the lock is not desirable, one may instead use a bslmt::WriteLockGuardTryLock in the typical following fashion:

/// Perform upgrade and return positive value if locking succeeds.
/// Return 0 if locking fails.
static int safeButNonBlockingFunc(my_Object *obj, my_RWLock *rwlock)
{
const int RETRIES = 1; // use higher values for higher success rate
if (guard.ptr()) { // rwlock is locked
if (someUpgradeCondition) {
obj->someUpgradeMethod();
return 2; // RETURN
} else if (someOtherUpgradeCondition) {
obj->someOtherUpgradeMethod();
return 3; // RETURN
}
obj->defaultUpgradeMethod();
return 1; // RETURN
}
return 0;
}
Definition bslmt_writelockguard.h:373

If the underlying lock object provides an upgrade from a lock for read to a lock for write (as does bslmt::ReaderWriterLock with the upgradeToWriteLock function, for example), and the lock is already guarded by a bslmt::ReadLockGuard, then it is not necessary to transfer the guard to a bslmt::WriteLockGuard. In fact, a combination of bslmt::ReadLockGuard and bslmt::WriteLockGuard guarding a common lock object should probably never be needed.

Care must be taken so as not to interleave guard objects in such a way as to cause an illegal sequence of calls on a lock (two sequential lock calls or two sequential unlock calls on a non-recursive read/write lock).