00001 // @file mutex.h 00002 00003 /* Copyright 2009 10gen Inc. 00004 * 00005 * Licensed under the Apache License, Version 2.0 (the "License"); 00006 * you may not use this file except in compliance with the License. 00007 * You may obtain a copy of the License at 00008 * 00009 * http://www.apache.org/licenses/LICENSE-2.0 00010 * 00011 * Unless required by applicable law or agreed to in writing, software 00012 * distributed under the License is distributed on an "AS IS" BASIS, 00013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00014 * See the License for the specific language governing permissions and 00015 * limitations under the License. 00016 */ 00017 00018 #pragma once 00019 00020 #include <map> 00021 #include <set> 00022 00023 #include "../heapcheck.h" 00024 00025 namespace mongo { 00026 00027 class mutex; 00028 00029 inline boost::xtime incxtimemillis( long long s ) { 00030 boost::xtime xt; 00031 boost::xtime_get(&xt, boost::TIME_UTC); 00032 xt.sec += (int)( s / 1000 ); 00033 xt.nsec += (int)(( s % 1000 ) * 1000000); 00034 if ( xt.nsec >= 1000000000 ) { 00035 xt.nsec -= 1000000000; 00036 xt.sec++; 00037 } 00038 return xt; 00039 } 00040 00045 class MutexDebugger { 00046 typedef const char * mid; // mid = mutex ID 00047 typedef map<mid,int> Preceeding; 00048 map< mid, int > maxNest; 00049 boost::thread_specific_ptr< Preceeding > us; 00050 map< mid, set<mid> > followers; 00051 boost::mutex &x; 00052 unsigned magic; 00053 00054 void aBreakPoint() { } // for debugging 00055 public: 00056 // set these to create an assert that 00057 // b must never be locked before a 00058 // so 00059 // a.lock(); b.lock(); is fine 00060 // b.lock(); alone is fine too 00061 // only checked on _DEBUG builds. 00062 string a,b; 00063 00065 void programEnding(); 00066 00067 MutexDebugger(); 00068 00069 void entering(mid m) { 00070 if( this == 0 ) return; 00071 assert( magic == 0x12345678 ); 00072 00073 Preceeding *_preceeding = us.get(); 00074 if( _preceeding == 0 ) 00075 us.reset( _preceeding = new Preceeding() ); 00076 Preceeding &preceeding = *_preceeding; 00077 00078 if( a == m ) { 00079 aBreakPoint(); 00080 if( preceeding[b.c_str()] ) { 00081 cout << "****** MutexDebugger error! warning " << b << " was locked before " << a << endl; 00082 assert(false); 00083 } 00084 } 00085 00086 preceeding[m]++; 00087 if( preceeding[m] > 1 ) { 00088 // recursive re-locking. 00089 if( preceeding[m] > maxNest[m] ) 00090 maxNest[m] = preceeding[m]; 00091 return; 00092 } 00093 00094 bool failed = false; 00095 string err; 00096 { 00097 boost::mutex::scoped_lock lk(x); 00098 followers[m]; 00099 for( Preceeding::iterator i = preceeding.begin(); i != preceeding.end(); i++ ) { 00100 if( m != i->first && i->second > 0 ) { 00101 followers[i->first].insert(m); 00102 if( followers[m].count(i->first) != 0 ) { 00103 failed = true; 00104 stringstream ss; 00105 mid bad = i->first; 00106 ss << "mutex problem" << 00107 "\n when locking " << m << 00108 "\n " << bad << " was already locked and should not be." 00109 "\n set a and b above to debug.\n"; 00110 stringstream q; 00111 for( Preceeding::iterator i = preceeding.begin(); i != preceeding.end(); i++ ) { 00112 if( i->first != m && i->first != bad && i->second > 0 ) 00113 q << " " << i->first << '\n'; 00114 } 00115 string also = q.str(); 00116 if( !also.empty() ) 00117 ss << "also locked before " << m << " in this thread (no particular order):\n" << also; 00118 err = ss.str(); 00119 break; 00120 } 00121 } 00122 } 00123 } 00124 if( failed ) { 00125 cout << err << endl; 00126 assert( 0 ); 00127 } 00128 } 00129 void leaving(mid m) { 00130 if( this == 0 ) return; // still in startup pre-main() 00131 Preceeding& preceeding = *us.get(); 00132 preceeding[m]--; 00133 if( preceeding[m] < 0 ) { 00134 cout << "ERROR: lock count for " << m << " is " << preceeding[m] << endl; 00135 assert( preceeding[m] >= 0 ); 00136 } 00137 } 00138 }; 00139 extern MutexDebugger &mutexDebugger; 00140 00141 // If you create a local static instance of this class, that instance will be destroyed 00142 // before all global static objects are destroyed, so _destroyingStatics will be set 00143 // to true before the global static variables are destroyed. 00144 class StaticObserver : boost::noncopyable { 00145 public: 00146 static bool _destroyingStatics; 00147 ~StaticObserver() { _destroyingStatics = true; } 00148 }; 00149 00155 class mutex : boost::noncopyable { 00156 public: 00157 #if defined(_DEBUG) 00158 const char * const _name; 00159 #endif 00160 00161 #if defined(_DEBUG) 00162 mutex(const char *name) 00163 : _name(name) 00164 #else 00165 mutex(const char *) 00166 #endif 00167 { 00168 _m = new boost::timed_mutex(); 00169 IGNORE_OBJECT( _m ); // Turn-off heap checking on _m 00170 } 00171 ~mutex() { 00172 if( !StaticObserver::_destroyingStatics ) { 00173 UNIGNORE_OBJECT( _m ); 00174 delete _m; 00175 } 00176 } 00177 00178 class try_lock : boost::noncopyable { 00179 public: 00180 try_lock( mongo::mutex &m , int millis = 0 ) 00181 : _l( m.boost() , incxtimemillis( millis ) ) , 00182 #if BOOST_VERSION >= 103500 00183 ok( _l.owns_lock() ) 00184 #else 00185 ok( _l.locked() ) 00186 #endif 00187 { 00188 } 00189 00190 ~try_lock() { 00191 } 00192 00193 private: 00194 boost::timed_mutex::scoped_timed_lock _l; 00195 00196 public: 00197 const bool ok; 00198 }; 00199 00200 00201 class scoped_lock : boost::noncopyable { 00202 #if defined(_DEBUG) 00203 mongo::mutex *mut; 00204 #endif 00205 public: 00206 scoped_lock( mongo::mutex &m ) : _l( m.boost() ) { 00207 #if defined(_DEBUG) 00208 mut = &m; 00209 mutexDebugger.entering(mut->_name); 00210 #endif 00211 } 00212 ~scoped_lock() { 00213 #if defined(_DEBUG) 00214 mutexDebugger.leaving(mut->_name); 00215 #endif 00216 } 00217 boost::timed_mutex::scoped_lock &boost() { return _l; } 00218 private: 00219 boost::timed_mutex::scoped_lock _l; 00220 }; 00221 00222 00223 private: 00224 00225 boost::timed_mutex &boost() { return *_m; } 00226 boost::timed_mutex *_m; 00227 }; 00228 00229 typedef mutex::scoped_lock scoped_lock; 00230 typedef boost::recursive_mutex::scoped_lock recursive_scoped_lock; 00231 00232 }