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

Macros

#define BSLMT_MUTEXASSERT_IS_LOCKED(mutex_p)   ((void) 0)
 
#define BSLMT_MUTEXASSERT_IS_LOCKED_SAFE(mutex_p)   ((void) 0)
 
#define BSLMT_MUTEXASSERT_IS_LOCKED_OPT(mutex_p)   ((void) 0)
 

Detailed Description

Outline

Purpose

Provide an assert macro for verifying that a mutex is locked.

Classes

Macros

See also
bslmt_mutex

Description

This component provides macros for asserting that a mutex is locked. It does not distinguish between locks held by the current thread or other threads. If the macro is active in the current build mode, when the macro is called, if the supplied mutex is unlocked, the assert handler installed for BSLS_ASSERT will be called. The assert handler installed by default will report an error and abort the task.

The three macros defined by the component are analogous to the macros defined by BSLS_ASSERT:

In build modes where any one of these macros is not active, calling it will have no effect.

If any of these asserts are in effect and fail (because the mutex in question was unlocked), the behavior parallels the behavior of the assertion macros defined in bsls_assert.hbsls::Assert::invokeHandler is called, with a source code expression, the name of the source file, and the line number in the source file where the macro was called. If the default handler is installed, this will result in an error message and an abort.

Usage

This section illustrates intended use of this component.

Example 1: Checking Consistency Within a Private Method

Sometimes multithreaded code is written such that the author of a function requires that a caller has already acquired a mutex. The BSLMT_MUTEXASSERT_IS_LOCKED* family of assertions allows the programmers to verify, using defensive programming techniques, that the mutex in question is indeed locked.

Suppose we have a fully thread-safe queue that contains int values, and is guarded by an internal mutex. We can use BSLMT_MUTEXASSERT_IS_LOCKED_SAFE to ensure (in appropriate build modes) that proper internal locking of the mutex is taking place.

First, we define the container:

/// This `class` provides a fully *thread-safe* unidirectional queue of
/// `int` values. See {`bsls_glossary`|Fully Thread-Safe}. All public
/// manipulators operate as single, atomic actions.
class MyThreadSafeQueue {
// DATA
bsl::deque<int> d_deque; // underlying non-*thread-safe*
// standard container
mutable bslmt::Mutex d_mutex; // mutex to provide thread safety
// PRIVATE MANIPULATOR
/// Assign the value at the front of the queue to the specified
/// `*result`, and remove the value at the front of the queue;
/// return 0 if the queue was not initially empty, and a non-zero
/// value (with no effect) otherwise. The behavior is undefined
/// unless `d_mutex` is locked.
int popImp(int *result);
public:
// ...
// MANIPULATORS
/// Assign the value at the front of the queue to the specified
/// `*result`, and remove the value at the front of the queue;
/// return 0 if the queue was not initially empty, and a non-zero
/// value (with no effect) otherwise.
int pop(int *result);
/// Assign the values of all the elements from this queue, in order,
/// to the specified `*result`, and remove them from this queue.
/// Any previous contents of `*result` are discarded. Note that, as
/// with the other public manipulators, this entire operation occurs
/// as a single, atomic action.
void popAll(bsl::vector<int> *result);
/// ...
void push(int value);
/// ...
template <class INPUT_ITER>
void pushRange(const INPUT_ITER& first, const INPUT_ITER& last);
};
Definition bslstl_deque.h:772
Definition bslstl_vector.h:1025
Definition bslmt_mutex.h:315

Notice that our public manipulators have two forms: push/pop a single element, and push/pop a collection of elements. Popping even a single element is non-trivial, so we factor this operation into a non-*thread-safe* private manipulator that performs the pop, and is used in both public pop methods. This private manipulator requires that the mutex be locked, but cannot lock the mutex itself, since the correctness of popAll demands that all of the pops be collectively performed using a single mutex lock/unlock.

Then, we define the private manipulator:

// PRIVATE MANIPULATOR
int MyThreadSafeQueue::popImp(int *result)
{
if (d_deque.empty()) {
return -1; // RETURN
}
else {
*result = d_deque.front();
d_deque.pop_front();
return 0; // RETURN
}
}
#define BSLMT_MUTEXASSERT_IS_LOCKED_SAFE(mutex_p)
Definition bslmt_mutexassert.h:310

Notice that, on the very first line, the private manipulator verifies, as a precondition check, that the mutex has been acquired, using one of the BSLMT_MUTEXASSERT_IS_LOCKED* macros. We use the ...IS_LOCKED_SAFE... version of the macro so that the check, which on some platforms is as expensive as locking the mutex, is performed in only the safe build mode.

Next, we define the public manipulators; each of which must acquire a lock on the mutex (note that there is a bug in popAll):

// MANIPULATORS
int MyThreadSafeQueue::pop(int *result)
{
BSLS_ASSERT(result);
d_mutex.lock();
int rc = popImp(result);
d_mutex.unlock();
return rc;
}
void MyThreadSafeQueue::popAll(bsl::vector<int> *result)
{
BSLS_ASSERT(result);
const int size = static_cast<int>(d_deque.size());
result->resize(size);
int *begin = result->begin();
for (int index = 0; index < size; ++index) {
int rc = popImp(&begin[index]);
BSLS_ASSERT(0 == rc);
}
}
void MyThreadSafeQueue::push(int value)
{
d_mutex.lock();
d_deque.push_back(value);
d_mutex.unlock();
}
template <class INPUT_ITER>
void MyThreadSafeQueue::pushRange(const INPUT_ITER& first,
const INPUT_ITER& last)
{
d_mutex.lock();
d_deque.insert(d_deque.begin(), first, last);
d_mutex.unlock();
}
iterator begin() BSLS_KEYWORD_NOEXCEPT
Definition bslstl_vector.h:2511
void resize(size_type newSize)
Definition bslstl_vector.h:3616
#define BSLS_ASSERT(X)
Definition bsls_assert.h:1804

Notice that, in popAll, we forgot to lock/unlock the mutex!

Then, in our function example2Function, we make use of our class to create and exercise a MyThreadSafeQueue object:

void testThreadSafeQueue(bsl::ostream& stream)
{
MyThreadSafeQueue queue;

Next, we populate the queue using pushRange:

const int rawData[] = { 17, 3, 21, -19, 4, 87, 29, 3, 101, 31, 36 };
enum { k_RAW_DATA_LENGTH = sizeof rawData / sizeof *rawData };
queue.pushRange(rawData + 0, rawData + k_RAW_DATA_LENGTH);

Then, we pop a few items off the front of the queue and verify their values:

int value = -1;
assert(0 == queue.pop(&value)); assert(17 == value);
assert(0 == queue.pop(&value)); assert( 3 == value);
assert(0 == queue.pop(&value)); assert(21 == value);

Next, we attempt to empty the queue with popAll, which, if built in safe mode, would fail because it neglects to lock the mutex:

queue.popAll(&v);
stream << "Remaining raw numbers: ";
for (bsl::size_t ti = 0; ti < v.size(); ++ti) {
stream << (ti ? ", " : "") << v[ti];
}
stream << bsl::endl;
}
size_type size() const BSLS_KEYWORD_NOEXCEPT
Return the number of elements in this vector.
Definition bslstl_vector.h:2664

Then, we build in non-safe mode and run:

Remaining raw numbers: -19, 4, 87, 29, 3, 101, 31, 36

Notice that, since the test case is being run in a single thread and our check is disabled, the bug where the mutex was not acquired does not manifest itself in a visible error, and we observe the seemingly correct output.

Now, we build in safe mode (which enables our check), run the program (which calls example2Function), and observe that, when we call popAll, the BSLMT_MUTEXASSERT_IS_LOCKED_SAFE(&d_mutex) macro issues an error message and aborts:

Assertion failed: BSLMT_MUTEXASSERT_IS_LOCKED_SAFE(&d_mutex),
file bslmt_mutexassertislocked.t.cpp, line 137 Aborted (core dumped)

Finally, note that the message printed above and the subsequent aborting of the program were the result of a call to bsls::Assert::invokeHandler, which in this case was configured (by default) to call bsls::Assert::failAbort. Other handlers may be installed that produce different results, but in all cases should prevent the program from proceeding normally.

Macro Definition Documentation

◆ BSLMT_MUTEXASSERT_IS_LOCKED

#define BSLMT_MUTEXASSERT_IS_LOCKED (   mutex_p)    ((void) 0)

◆ BSLMT_MUTEXASSERT_IS_LOCKED_OPT

#define BSLMT_MUTEXASSERT_IS_LOCKED_OPT (   mutex_p)    ((void) 0)

◆ BSLMT_MUTEXASSERT_IS_LOCKED_SAFE

#define BSLMT_MUTEXASSERT_IS_LOCKED_SAFE (   mutex_p)    ((void) 0)