MongoDB  2.0.3
mutex.h
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 }