|
MongoDB
2.0.3
|
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 #include "../heapcheck.h" 00023 00024 namespace mongo { 00025 00026 void printStackTrace( ostream &o ); 00027 00028 class mutex; 00029 00030 inline boost::xtime incxtimemillis( long long s ) { 00031 boost::xtime xt; 00032 boost::xtime_get(&xt, boost::TIME_UTC); 00033 xt.sec += (int)( s / 1000 ); 00034 xt.nsec += (int)(( s % 1000 ) * 1000000); 00035 if ( xt.nsec >= 1000000000 ) { 00036 xt.nsec -= 1000000000; 00037 xt.sec++; 00038 } 00039 return xt; 00040 } 00041 00046 class MutexDebugger { 00047 typedef const char * mid; // mid = mutex ID 00048 typedef map<mid,int> Preceeding; 00049 map< mid, int > maxNest; 00050 boost::thread_specific_ptr< Preceeding > us; 00051 map< mid, set<mid> > followers; 00052 boost::mutex &x; 00053 unsigned magic; 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 mutex(const char *name) : _name(name) 00160 #else 00161 mutex(const char *) 00162 #endif 00163 { 00164 _m = new boost::timed_mutex(); 00165 IGNORE_OBJECT( _m ); // Turn-off heap checking on _m 00166 } 00167 ~mutex() { 00168 if( !StaticObserver::_destroyingStatics ) { 00169 UNIGNORE_OBJECT( _m ); 00170 delete _m; 00171 } 00172 } 00173 00174 class try_lock : boost::noncopyable { 00175 public: 00176 try_lock( mongo::mutex &m , int millis = 0 ) 00177 : _l( m.boost() , incxtimemillis( millis ) ) , 00178 #if BOOST_VERSION >= 103500 00179 ok( _l.owns_lock() ) 00180 #else 00181 ok( _l.locked() ) 00182 #endif 00183 { } 00184 private: 00185 boost::timed_mutex::scoped_timed_lock _l; 00186 public: 00187 const bool ok; 00188 }; 00189 00190 class scoped_lock : boost::noncopyable { 00191 public: 00192 #if defined(_DEBUG) 00193 struct PostStaticCheck { 00194 PostStaticCheck() { 00195 if ( StaticObserver::_destroyingStatics ) { 00196 cout << "trying to lock a mongo::mutex during static shutdown" << endl; 00197 printStackTrace( cout ); 00198 } 00199 } 00200 }; 00201 00202 PostStaticCheck _check; 00203 mongo::mutex * const _mut; 00204 #endif 00205 scoped_lock( mongo::mutex &m ) : 00206 #if defined(_DEBUG) 00207 _mut(&m), 00208 #endif 00209 _l( m.boost() ) { 00210 #if defined(_DEBUG) 00211 mutexDebugger.entering(_mut->_name); 00212 #endif 00213 } 00214 ~scoped_lock() { 00215 #if defined(_DEBUG) 00216 mutexDebugger.leaving(_mut->_name); 00217 #endif 00218 } 00219 boost::timed_mutex::scoped_lock &boost() { return _l; } 00220 private: 00221 boost::timed_mutex::scoped_lock _l; 00222 }; 00223 private: 00224 boost::timed_mutex &boost() { return *_m; } 00225 boost::timed_mutex *_m; 00226 }; 00227 00228 typedef mutex::scoped_lock scoped_lock; 00229 typedef boost::recursive_mutex::scoped_lock recursive_scoped_lock; 00230 00236 #if defined(_WIN32) 00237 class SimpleMutex : boost::noncopyable { 00238 CRITICAL_SECTION _cs; 00239 public: 00240 SimpleMutex(const char *name) { InitializeCriticalSection(&_cs); } 00241 ~SimpleMutex() { DeleteCriticalSection(&_cs); } 00242 00243 void lock() { EnterCriticalSection(&_cs); } 00244 void unlock() { LeaveCriticalSection(&_cs); } 00245 00246 class scoped_lock : boost::noncopyable { 00247 SimpleMutex& _m; 00248 public: 00249 scoped_lock( SimpleMutex &m ) : _m(m) { _m.lock(); } 00250 ~scoped_lock() { _m.unlock(); } 00251 }; 00252 }; 00253 #else 00254 class SimpleMutex : boost::noncopyable { 00255 public: 00256 SimpleMutex(const char* name) { assert( pthread_mutex_init(&_lock,0) == 0 ); } 00257 ~SimpleMutex(){ 00258 if ( ! StaticObserver::_destroyingStatics ) { 00259 assert( pthread_mutex_destroy(&_lock) == 0 ); 00260 } 00261 } 00262 00263 void lock() { assert( pthread_mutex_lock(&_lock) == 0 ); } 00264 void unlock() { assert( pthread_mutex_unlock(&_lock) == 0 ); } 00265 00266 class scoped_lock : boost::noncopyable { 00267 SimpleMutex& _m; 00268 public: 00269 scoped_lock( SimpleMutex &m ) : _m(m) { _m.lock(); } 00270 ~scoped_lock() { _m.unlock(); } 00271 }; 00272 00273 private: 00274 pthread_mutex_t _lock; 00275 }; 00276 00277 #endif 00278 00279 }
1.8.0