// @file mutex.h /* Copyright 2009 10gen Inc. * * 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. */ #pragma once #include #include #include "../heapcheck.h" namespace mongo { void printStackTrace( ostream &o ); class mutex; inline boost::xtime incxtimemillis( long long s ) { boost::xtime xt; boost::xtime_get(&xt, boost::TIME_UTC); xt.sec += (int)( s / 1000 ); xt.nsec += (int)(( s % 1000 ) * 1000000); if ( xt.nsec >= 1000000000 ) { xt.nsec -= 1000000000; xt.sec++; } return xt; } /** only used on _DEBUG builds. MutexDebugger checks that we always acquire locks for multiple mutexes in a consistant (acyclic) order. If we were inconsistent we could deadlock. */ class MutexDebugger { typedef const char * mid; // mid = mutex ID typedef map Preceeding; map< mid, int > maxNest; boost::thread_specific_ptr< Preceeding > us; map< mid, set > followers; boost::mutex &x; unsigned magic; void aBreakPoint() { } // for debugging public: // set these to create an assert that // b must never be locked before a // so // a.lock(); b.lock(); is fine // b.lock(); alone is fine too // only checked on _DEBUG builds. string a,b; /** outputs some diagnostic info on mutexes (on _DEBUG builds) */ void programEnding(); MutexDebugger(); void entering(mid m) { if( this == 0 ) return; assert( magic == 0x12345678 ); Preceeding *_preceeding = us.get(); if( _preceeding == 0 ) us.reset( _preceeding = new Preceeding() ); Preceeding &preceeding = *_preceeding; if( a == m ) { aBreakPoint(); if( preceeding[b.c_str()] ) { cout << "****** MutexDebugger error! warning " << b << " was locked before " << a << endl; assert(false); } } preceeding[m]++; if( preceeding[m] > 1 ) { // recursive re-locking. if( preceeding[m] > maxNest[m] ) maxNest[m] = preceeding[m]; return; } bool failed = false; string err; { boost::mutex::scoped_lock lk(x); followers[m]; for( Preceeding::iterator i = preceeding.begin(); i != preceeding.end(); i++ ) { if( m != i->first && i->second > 0 ) { followers[i->first].insert(m); if( followers[m].count(i->first) != 0 ) { failed = true; stringstream ss; mid bad = i->first; ss << "mutex problem" << "\n when locking " << m << "\n " << bad << " was already locked and should not be." "\n set a and b above to debug.\n"; stringstream q; for( Preceeding::iterator i = preceeding.begin(); i != preceeding.end(); i++ ) { if( i->first != m && i->first != bad && i->second > 0 ) q << " " << i->first << '\n'; } string also = q.str(); if( !also.empty() ) ss << "also locked before " << m << " in this thread (no particular order):\n" << also; err = ss.str(); break; } } } } if( failed ) { cout << err << endl; assert( 0 ); } } void leaving(mid m) { if( this == 0 ) return; // still in startup pre-main() Preceeding& preceeding = *us.get(); preceeding[m]--; if( preceeding[m] < 0 ) { cout << "ERROR: lock count for " << m << " is " << preceeding[m] << endl; assert( preceeding[m] >= 0 ); } } }; extern MutexDebugger &mutexDebugger; // If you create a local static instance of this class, that instance will be destroyed // before all global static objects are destroyed, so _destroyingStatics will be set // to true before the global static variables are destroyed. class StaticObserver : boost::noncopyable { public: static bool _destroyingStatics; ~StaticObserver() { _destroyingStatics = true; } }; /** On pthread systems, it is an error to destroy a mutex while held (boost mutex * may use pthread). Static global mutexes may be held upon shutdown in our * implementation, and this way we avoid destroying them. * NOT recursive. */ class mutex : boost::noncopyable { public: #if defined(_DEBUG) const char * const _name; mutex(const char *name) : _name(name) #else mutex(const char *) #endif { _m = new boost::timed_mutex(); IGNORE_OBJECT( _m ); // Turn-off heap checking on _m } ~mutex() { if( !StaticObserver::_destroyingStatics ) { UNIGNORE_OBJECT( _m ); delete _m; } } class try_lock : boost::noncopyable { public: try_lock( mongo::mutex &m , int millis = 0 ) : _l( m.boost() , incxtimemillis( millis ) ) , #if BOOST_VERSION >= 103500 ok( _l.owns_lock() ) #else ok( _l.locked() ) #endif { } private: boost::timed_mutex::scoped_timed_lock _l; public: const bool ok; }; class scoped_lock : boost::noncopyable { public: #if defined(_DEBUG) struct PostStaticCheck { PostStaticCheck() { if ( StaticObserver::_destroyingStatics ) { cout << "trying to lock a mongo::mutex during static shutdown" << endl; printStackTrace( cout ); } } }; PostStaticCheck _check; mongo::mutex * const _mut; #endif scoped_lock( mongo::mutex &m ) : #if defined(_DEBUG) _mut(&m), #endif _l( m.boost() ) { #if defined(_DEBUG) mutexDebugger.entering(_mut->_name); #endif } ~scoped_lock() { #if defined(_DEBUG) mutexDebugger.leaving(_mut->_name); #endif } boost::timed_mutex::scoped_lock &boost() { return _l; } private: boost::timed_mutex::scoped_lock _l; }; private: boost::timed_mutex &boost() { return *_m; } boost::timed_mutex *_m; }; typedef mutex::scoped_lock scoped_lock; typedef boost::recursive_mutex::scoped_lock recursive_scoped_lock; /** The concept with SimpleMutex is that it is a basic lock/unlock with no special functionality (such as try and try timeout). Thus it can be implemented using OS-specific facilities in all environments (if desired). On Windows, the implementation below is faster than boost mutex. */ #if defined(_WIN32) class SimpleMutex : boost::noncopyable { CRITICAL_SECTION _cs; public: SimpleMutex(const char *name) { InitializeCriticalSection(&_cs); } ~SimpleMutex() { DeleteCriticalSection(&_cs); } void lock() { EnterCriticalSection(&_cs); } void unlock() { LeaveCriticalSection(&_cs); } class scoped_lock : boost::noncopyable { SimpleMutex& _m; public: scoped_lock( SimpleMutex &m ) : _m(m) { _m.lock(); } ~scoped_lock() { _m.unlock(); } }; }; #else class SimpleMutex : boost::noncopyable { public: SimpleMutex(const char* name) { assert( pthread_mutex_init(&_lock,0) == 0 ); } ~SimpleMutex(){ if ( ! StaticObserver::_destroyingStatics ) { assert( pthread_mutex_destroy(&_lock) == 0 ); } } void lock() { assert( pthread_mutex_lock(&_lock) == 0 ); } void unlock() { assert( pthread_mutex_unlock(&_lock) == 0 ); } class scoped_lock : boost::noncopyable { SimpleMutex& _m; public: scoped_lock( SimpleMutex &m ) : _m(m) { _m.lock(); } ~scoped_lock() { _m.unlock(); } }; private: pthread_mutex_t _lock; }; #endif }