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

Detailed Description

Outline

Purpose

Provide a platform-independent mutex.

Classes

See also
bslmt_recursivemutex, bslmt_mutex

Description

This component provides a mutually exclusive lock primitive ("mutex") by wrapping a suitable platform-specific mechanism. The bslmt::Mutex class provides the following operations: lock, tryLock, and unlock.

The behavior is undefined if unlock is invoked on a bslmt::Mutex object from a thread that did not successfully acquire the lock, or if lock is called twice in a thread without calling unlock in between (i.e., bslmt::Mutex is non-recursive). In particular, lock may or may not deadlock if the current thread holds the lock.

Usage

This section illustrates intended use of this component.

Example 1: Basic Usage

The following snippets of code illustrate the use of bslmt::Mutex to write a thread-safe class, my_SafeAccount, given a thread-unsafe class, my_Account. The simple my_Account class is defined as follows:

/// This `class` represents a bank account with a single balance. It
/// is not thread-safe.
class my_Account {
// DATA
double d_money; // amount of money in the account
public:
// CREATORS
/// Create an account with zero balance.
my_Account();
/// Create an account having the value of the specified `original`
/// account.
my_Account(const my_Account& original);
/// Destroy this account.
~my_Account();
// MANIPULATORS
/// Assign to this account the value of the specified `rhs` account,
/// and return a reference to this modifiable account.
my_Account& operator=(const my_Account& rhs);
/// Deposit the specified `amount` of money into this account.
void deposit(double amount);
/// Withdraw the specified `amount` of money from this account.
void withdraw(double amount);
// ACCESSORS
/// Return the amount of money that is available for withdrawal
/// from this account.
double balance() const;
};
// CREATORS
my_Account::my_Account()
: d_money(0.0)
{
}
my_Account::my_Account(const my_Account& original)
: d_money(original.d_money)
{
}
my_Account::~my_Account()
{
}
// MANIPULATORS
my_Account& my_Account::operator=(const my_Account& rhs)
{
d_money = rhs.d_money;
return *this;
}
void my_Account::deposit(double amount)
{
d_money += amount;
}
void my_Account::withdraw(double amount)
{
d_money -= amount;
}
// ACCESSORS
double my_Account::balance() const
{
return d_money;
}

Next, we use a bslmt::Mutex object to render atomic the function calls of a new thread-safe class that uses the thread-unsafe class in its implementation. Note the typical use of mutable for the lock:

/// This `class` provides a thread-safe handle to an account (held, not
/// owned) passed at construction.
class my_SafeAccountHandle {
// DATA
my_Account *d_account_p; // held, not owned
mutable bslmt::Mutex d_lock; // guard access to `d_account_p`
// NOT IMPLEMENTED
my_SafeAccountHandle(const my_SafeAccountHandle&);
my_SafeAccountHandle& operator=(const my_SafeAccountHandle&);
public:
// CREATORS
/// Create a thread-safe handle to the specified `account`.
my_SafeAccountHandle(my_Account *account);
/// Destroy this handle. Note that the held account is unaffected
/// by this operation.
~my_SafeAccountHandle();
// MANIPULATORS
/// Atomically deposit the specified `amount` of money into the
/// account held by this handle. Note that this operation is
/// thread-safe; no `lock` is needed.
void deposit(double amount);
/// Provide exclusive access to the underlying account held by this
/// object.
void lock();
/// Release exclusivity of the access to the underlying account held
/// by this object.
void unlock();
/// Atomically withdraw the specified `amount` of money from the
/// account held by this handle. Note that this operation is
/// thread-safe; no `lock` is needed.
void withdraw(double amount);
// ACCESSORS
/// Return the address of the modifiable account held by this
/// handle.
my_Account *account() const;
/// Atomically return the amount of money that is available for
/// withdrawal from the account held by this handle.
double balance() const;
};
Definition bslmt_mutex.h:315

The implementation show-casing the use of bslmt::Mutex follows:

// CREATORS
my_SafeAccountHandle::my_SafeAccountHandle(my_Account *account)
: d_account_p(account)
{
}
my_SafeAccountHandle::~my_SafeAccountHandle()
{
}
// MANIPULATORS
void my_SafeAccountHandle::deposit(double amount)
{

Where appropriate, clients should use a lock-guard to ensure that an acquired mutex is always properly released, even if an exception is thrown. See bslmt_lockguard for more information:

d_lock.lock(); // consider using 'bslmt::LockGuard'
d_account_p->deposit(amount);
d_lock.unlock();
}
void my_SafeAccountHandle::lock()
{
d_lock.lock();
}
void my_SafeAccountHandle::unlock()
{
d_lock.unlock();
}
void my_SafeAccountHandle::withdraw(double amount)
{
d_lock.lock(); // consider using 'bslmt::LockGuard'
d_account_p->withdraw(amount);
d_lock.unlock();
}
// ACCESSORS
my_Account *my_SafeAccountHandle::account() const
{
return d_account_p;
}
double my_SafeAccountHandle::balance() const
{
d_lock.lock(); // consider using 'bslmt::LockGuard'
const double res = d_account_p->balance();
d_lock.unlock();
return res;
}

The handle's atomic methods are used just as the corresponding methods in my_Account:

my_Account account;
account.deposit(100.50);
double paycheck = 50.25;
my_SafeAccountHandle handle(&account);
assert(100.50 == handle.balance());
handle.deposit(paycheck); assert(150.75 == handle.balance());

We can also use the handle's lock and unlock methods to implement non-primitive atomic transactions on the account:

double check[5] = { 25.0, 100.0, 99.95, 75.0, 50.0 };
handle.lock(); // consider using 'bslmt::LockGuard'
double originalBalance = handle.account()->balance();
for (int i = 0; i < 5; ++i) {
handle.account()->deposit(check[i]);
}
assert(originalBalance + 349.95 == handle.account()->balance());
handle.unlock();