Chris@16: // (C) Copyright 2012 Vicente J. Botet Escriba Chris@16: // Distributed under the Boost Software License, Version 1.0. (See Chris@16: // accompanying file LICENSE_1_0.txt or copy at Chris@16: // http://www.boost.org/LICENSE_1_0.txt) Chris@16: Chris@16: Chris@16: #ifndef BOOST_THREAD_TESTABLE_LOCKABLE_HPP Chris@16: #define BOOST_THREAD_TESTABLE_LOCKABLE_HPP Chris@16: Chris@16: #include Chris@16: Chris@16: #include Chris@16: Chris@16: #include Chris@16: #include Chris@16: Chris@16: #include Chris@16: Chris@16: namespace boost Chris@16: { Chris@16: /** Chris@16: * Based on Associate Mutexes with Data to Prevent Races, By Herb Sutter, May 13, 2010 Chris@16: * http://www.drdobbs.com/windows/associate-mutexes-with-data-to-prevent-r/224701827?pgno=3 Chris@16: * Chris@16: * Make our mutex testable if it isn't already. Chris@16: * Chris@16: * Many mutex services (including boost::mutex) don't provide a way to ask, Chris@16: * "Do I already hold a lock on this mutex?" Chris@16: * Sometimes it is needed to know if a method like is_locked to be available. Chris@16: * This wrapper associates an arbitrary lockable type with a thread id that stores the ID of the thread that Chris@16: * currently holds the lockable. The thread id initially holds an invalid value that means no threads own the mutex. Chris@16: * When we acquire a lock, we set the thread id; and when we release a lock, we reset it back to its default no id state. Chris@16: * Chris@16: */ Chris@16: template Chris@16: class testable_mutex Chris@16: { Chris@16: Lockable mtx_; Chris@16: atomic id_; Chris@16: public: Chris@16: /// the type of the wrapped lockable Chris@16: typedef Lockable lockable_type; Chris@16: Chris@16: /// Non copyable Chris@16: BOOST_THREAD_NO_COPYABLE(testable_mutex) Chris@16: Chris@16: testable_mutex() : id_(thread::id()) {} Chris@16: Chris@16: void lock() Chris@16: { Chris@16: BOOST_ASSERT(! is_locked_by_this_thread()); Chris@16: mtx_.lock(); Chris@16: id_ = this_thread::get_id(); Chris@16: } Chris@16: Chris@16: void unlock() Chris@16: { Chris@16: BOOST_ASSERT(is_locked_by_this_thread()); Chris@16: id_ = thread::id(); Chris@16: mtx_.unlock(); Chris@16: } Chris@16: Chris@16: bool try_lock() Chris@16: { Chris@16: BOOST_ASSERT(! is_locked_by_this_thread()); Chris@16: if (mtx_.try_lock()) Chris@16: { Chris@16: id_ = this_thread::get_id(); Chris@16: return true; Chris@16: } Chris@16: else Chris@16: { Chris@16: return false; Chris@16: } Chris@16: } Chris@16: #ifdef BOOST_THREAD_USES_CHRONO Chris@16: template Chris@16: bool try_lock_for(const chrono::duration& rel_time) Chris@16: { Chris@16: BOOST_ASSERT(! is_locked_by_this_thread()); Chris@16: if (mtx_.try_lock_for(rel_time)) Chris@16: { Chris@16: id_ = this_thread::get_id(); Chris@16: return true; Chris@16: } Chris@16: else Chris@16: { Chris@16: return false; Chris@16: } Chris@16: } Chris@16: template Chris@16: bool try_lock_until(const chrono::time_point& abs_time) Chris@16: { Chris@16: BOOST_ASSERT(! is_locked_by_this_thread()); Chris@16: if (mtx_.try_lock_until(abs_time)) Chris@16: { Chris@16: id_ = this_thread::get_id(); Chris@16: return true; Chris@16: } Chris@16: else Chris@16: { Chris@16: return false; Chris@16: } Chris@16: } Chris@16: #endif Chris@16: Chris@16: bool is_locked_by_this_thread() const Chris@16: { Chris@16: return this_thread::get_id() == id_; Chris@16: } Chris@16: bool is_locked() const Chris@16: { Chris@16: return ! (thread::id() == id_); Chris@16: } Chris@16: Chris@16: thread::id get_id() const Chris@16: { Chris@16: return id_; Chris@16: } Chris@16: Chris@16: // todo add the shared and upgrade mutex functions Chris@16: }; Chris@16: Chris@16: template Chris@16: struct is_testable_lockable : false_type Chris@16: {}; Chris@16: Chris@16: template Chris@16: struct is_testable_lockable > : true_type Chris@16: {}; Chris@16: Chris@16: // /** Chris@16: // * Overloaded function used to check if the mutex is locked when it is testable and do nothing otherwise. Chris@16: // * Chris@16: // * This function is used usually to assert the pre-condition when the function can only be called when the mutex Chris@16: // * must be locked by the current thread. Chris@16: // */ Chris@16: // template Chris@16: // bool is_locked_by_this_thread(testable_mutex const& mtx) Chris@16: // { Chris@16: // return mtx.is_locked(); Chris@16: // } Chris@16: // template Chris@16: // bool is_locked_by_this_thread(Lockable const&) Chris@16: // { Chris@16: // return true; Chris@16: // } Chris@16: } Chris@16: Chris@16: #include Chris@16: Chris@16: #endif // header