// bslmt_mutexassert.h -*-C++-*- // ---------------------------------------------------------------------------- // NOTICE // // This component is not up to date with current BDE coding standards, and // should not be used as an example for new development. // ---------------------------------------------------------------------------- #ifndef INCLUDED_BSLMT_MUTEXASSERT #define INCLUDED_BSLMT_MUTEXASSERT #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide an assert macro for verifying that a mutex is locked. // //@CLASSES: // //@MACROS: // BSLMT_MUTEXASSERT_IS_LOCKED: verify a mutex is locked in non-opt modes // BSLMT_MUTEXASSERT_IS_LOCKED_SAFE: verify a mutex is locked in safe mode // BSLMT_MUTEXASSERT_IS_LOCKED_OPT: verify a mutex is locked in all modes // //@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': // //: o 'BSLMT_MUTEXASSERT_IS_LOCKED': active when 'BSLS_ASSERT' is active //: //: o 'BSLMT_MUTEXASSERT_IS_LOCKED_SAFE': active when 'BSLS_ASSERT_SAFE' is //: active //: //: o 'BSLMT_MUTEXASSERT_IS_LOCKED_OPT': active when 'BSLS_ASSERT_OPT' is //: active // // 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.h' -- 'bsls::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: //.. // class MyThreadSafeQueue { // // 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. // // // DATA // bsl::deque<int> d_deque; // underlying non-*thread-safe* // // standard container // // mutable bslmt::Mutex d_mutex; // mutex to provide thread safety // // // PRIVATE MANIPULATOR // int popImp(int *result); // // 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. // // public: // // ... // // // MANIPULATORS // int pop(int *result); // // 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. // // void popAll(bsl::vector<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 push(int value); // // ... // // template <class INPUT_ITER> // void pushRange(const INPUT_ITER& first, const INPUT_ITER& last); // // ... // }; //.. // 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) // { // BSLMT_MUTEXASSERT_IS_LOCKED_SAFE(&d_mutex); // // if (d_deque.empty()) { // return -1; // RETURN // } // else { // *result = d_deque.front(); // d_deque.pop_front(); // return 0; // RETURN // } // } //.. // 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(); // } //.. // 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: //.. // bsl::vector<int> v; // queue.popAll(&v); // // stream << "Remaining raw numbers: "; // for (bsl::size_t ti = 0; ti < v.size(); ++ti) { // stream << (ti ? ", " : "") << v[ti]; // } // stream << bsl::endl; // } //.. // 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. #include <bslscm_version.h> #include <bsls_assert.h> #if defined(BSLS_ASSERT_IS_ACTIVE) #define BSLMT_MUTEXASSERT_IS_LOCKED(mutex_p) do { \ BloombergLP::bslmt::MutexAssert_Imp::assertIsLockedImpl( \ (mutex_p), \ "BSLMT_MUTEXASSERT_IS_LOCKED(" #mutex_p ")", \ __FILE__, \ __LINE__); } while (false) #else #define BSLMT_MUTEXASSERT_IS_LOCKED(mutex_p) ((void) 0) #endif #if defined(BSLS_ASSERT_SAFE_IS_ACTIVE) #define BSLMT_MUTEXASSERT_IS_LOCKED_SAFE(mutex_p) do { \ BloombergLP::bslmt::MutexAssert_Imp::assertIsLockedImpl( \ (mutex_p), \ "BSLMT_MUTEXASSERT_IS_LOCKED_SAFE(" #mutex_p ")", \ __FILE__, \ __LINE__); } while (false) #else #define BSLMT_MUTEXASSERT_IS_LOCKED_SAFE(mutex_p) ((void) 0) #endif #if defined(BSLS_ASSERT_OPT_IS_ACTIVE) #define BSLMT_MUTEXASSERT_IS_LOCKED_OPT(mutex_p) do { \ BloombergLP::bslmt::MutexAssert_Imp::assertIsLockedImpl( \ (mutex_p), \ "BSLMT_MUTEXASSERT_IS_LOCKED_OPT(" #mutex_p ")", \ __FILE__, \ __LINE__); } while (false) #else #define BSLMT_MUTEXASSERT_IS_LOCKED_OPT(mutex_p) ((void) 0) #endif namespace BloombergLP { namespace bslmt { class Mutex; // ===================== // class MutexAssert_Imp // ===================== struct MutexAssert_Imp { // This 'struct' provides a (component private) namespace for // implementation functions of the assert macros defined in this component. // This class should *not* be used directly in client code. // CLASS METHODS static void assertIsLockedImpl(Mutex *mutex, const char *text, const char *file, int line); // If the specified 'mutex' is not locked, call // 'bsls::Assert::invokeHandler' with the specified 'text', 'file', and // 'line', where 'text' is text describing the assertion being // performed, 'file' is the name of the source file that called the // macro, and 'line' is the line number in the file where the macro was // called. This function is intended to implement // 'BSLMT_MUTEXASSERT_IS_LOCKED', 'BSLMT_MUTEXASSERT_IS_LOCKED_SAFE', // and 'BSLMT_MUTEXASSERT_IS_LOCKED_OPT' and should not otherwise be // called directly. }; } // close package namespace } // close enterprise namespace #endif // ---------------------------------------------------------------------------- // Copyright 2015 Bloomberg Finance L.P. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ----------------------------- END-OF-FILE ----------------------------------