summaryrefslogtreecommitdiff
path: root/util/concurrency/mutex.h
diff options
context:
space:
mode:
Diffstat (limited to 'util/concurrency/mutex.h')
-rw-r--r--util/concurrency/mutex.h129
1 files changed, 91 insertions, 38 deletions
diff --git a/util/concurrency/mutex.h b/util/concurrency/mutex.h
index 797ab77..c463498 100644
--- a/util/concurrency/mutex.h
+++ b/util/concurrency/mutex.h
@@ -20,13 +20,29 @@
#include <map>
#include <set>
-namespace mongo {
+#include "../heapcheck.h"
+
+namespace mongo {
- extern bool __destroyingStatics;
class mutex;
- // only used on _DEBUG builds:
- class MutexDebugger {
+ 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<mid,int> Preceeding;
map< mid, int > maxNest;
@@ -34,36 +50,41 @@ namespace mongo {
map< mid, set<mid> > 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
+ // so
// a.lock(); b.lock(); is fine
// b.lock(); alone is fine too
// only checked on _DEBUG builds.
string a,b;
-
- void aBreakPoint(){}
+
+ /** outputs some diagnostic info on mutexes (on _DEBUG builds) */
void programEnding();
+
MutexDebugger();
+
void entering(mid m) {
- if( magic != 0x12345678 ) return;
+ 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 ) {
+ if( a == m ) {
aBreakPoint();
if( preceeding[b.c_str()] ) {
- cout << "mutex problem " << b << " was locked before " << a << endl;
+ cout << "****** MutexDebugger error! warning " << b << " was locked before " << a << endl;
assert(false);
}
}
preceeding[m]++;
- if( preceeding[m] > 1 ) {
+ if( preceeding[m] > 1 ) {
// recursive re-locking.
if( preceeding[m] > maxNest[m] )
maxNest[m] = preceeding[m];
@@ -75,19 +96,19 @@ namespace mongo {
{
boost::mutex::scoped_lock lk(x);
followers[m];
- for( Preceeding::iterator i = preceeding.begin(); i != preceeding.end(); i++ ) {
+ 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 ){
+ 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";
+ "\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++ ) {
+ for( Preceeding::iterator i = preceeding.begin(); i != preceeding.end(); i++ ) {
if( i->first != m && i->first != bad && i->second > 0 )
q << " " << i->first << '\n';
}
@@ -105,8 +126,8 @@ namespace mongo {
assert( 0 );
}
}
- void leaving(mid m) {
- if( magic != 0x12345678 ) return;
+ void leaving(mid m) {
+ if( this == 0 ) return; // still in startup pre-main()
Preceeding& preceeding = *us.get();
preceeding[m]--;
if( preceeding[m] < 0 ) {
@@ -116,38 +137,67 @@ namespace mongo {
}
};
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
+ // 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:
- ~StaticObserver() { __destroyingStatics = true; }
+ static bool _destroyingStatics;
+ ~StaticObserver() { _destroyingStatics = true; }
};
- // On pthread systems, it is an error to destroy a mutex while held. Static global
- // mutexes may be held upon shutdown in our implementation, and this way we avoid
- // destroying them.
+ /** On pthread systems, it is an error to destroy a mutex while held. 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 *_name;
+ const char * const _name;
#endif
#if defined(_DEBUG)
- mutex(const char *name)
- : _name(name)
+ mutex(const char *name)
+ : _name(name)
#else
- mutex(const char *)
+ mutex(const char *)
#endif
- {
- _m = new boost::mutex();
+ {
+ _m = new boost::timed_mutex();
+ IGNORE_OBJECT( _m ); // Turn-off heap checking on _m
}
~mutex() {
- if( !__destroyingStatics ) {
+ 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
+ {
+ }
+
+ ~try_lock() {
+ }
+
+ private:
+ boost::timed_mutex::scoped_timed_lock _l;
+
+ public:
+ const bool ok;
+ };
+
+
class scoped_lock : boost::noncopyable {
#if defined(_DEBUG)
mongo::mutex *mut;
@@ -159,20 +209,23 @@ namespace mongo {
mutexDebugger.entering(mut->_name);
#endif
}
- ~scoped_lock() {
+ ~scoped_lock() {
#if defined(_DEBUG)
mutexDebugger.leaving(mut->_name);
#endif
}
- boost::mutex::scoped_lock &boost() { return _l; }
+ boost::timed_mutex::scoped_lock &boost() { return _l; }
private:
- boost::mutex::scoped_lock _l;
+ boost::timed_mutex::scoped_lock _l;
};
+
+
private:
- boost::mutex &boost() { return *_m; }
- boost::mutex *_m;
+
+ 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;