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:
class my_Account {
double d_money;
public:
my_Account();
my_Account(const my_Account& original);
~my_Account();
my_Account& operator=(const my_Account& rhs);
void deposit(double amount);
void withdraw(double amount);
double balance() const;
};
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()
{
}
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;
}
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:
class my_SafeAccountHandle {
my_Account *d_account_p;
my_SafeAccountHandle(const my_SafeAccountHandle&);
my_SafeAccountHandle& operator=(const my_SafeAccountHandle&);
public:
my_SafeAccountHandle(my_Account *account);
~my_SafeAccountHandle();
void deposit(double amount);
void lock();
void unlock();
void withdraw(double amount);
my_Account *account() const;
double balance() const;
};
Definition bslmt_mutex.h:315
The implementation show-casing the use of bslmt::Mutex
follows:
my_SafeAccountHandle::my_SafeAccountHandle(my_Account *account)
: d_account_p(account)
{
}
my_SafeAccountHandle::~my_SafeAccountHandle()
{
}
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();
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();
d_account_p->withdraw(amount);
d_lock.unlock();
}
my_Account *my_SafeAccountHandle::account() const
{
return d_account_p;
}
double my_SafeAccountHandle::balance() const
{
d_lock.lock();
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();
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();