MongoDB  2.7.0
mutexdebugger.h
1 // @file mutexdebugger.h
2 
31 #pragma once
32 
34 
35 #include <iostream>
36 #include <map>
37 #include <set>
38 #include <sstream>
39 #include <string>
40 
41 #include "boost/thread/mutex.hpp"
42 
44 
45 #include "mongo/util/assert_util.h"
46 
47 namespace mongo {
48 
53  class MutexDebugger {
54  typedef const char * mid; // mid = mutex ID
55  typedef std::map<mid,int> Preceeding;
56  std::map< mid, int > maxNest;
57  boost::thread_specific_ptr< Preceeding > us;
58  std::map< mid, std::set<mid> > followers;
59  boost::mutex &x;
60  unsigned magic;
61  void aBreakPoint() { } // for debugging
62  public:
63  // set these to create an assert that
64  // b must never be locked before a
65  // so
66  // a.lock(); b.lock(); is fine
67  // b.lock(); alone is fine too
68  // only checked on _DEBUG builds.
69  std::string a,b;
70 
72  void programEnding();
73 
74  MutexDebugger();
75 
76  std::string currentlyLocked() const {
77  Preceeding *_preceeding = us.get();
78  if( _preceeding == 0 )
79  return "";
80  Preceeding &preceeding = *_preceeding;
81  std::stringstream q;
82  for( Preceeding::const_iterator i = preceeding.begin(); i != preceeding.end(); i++ ) {
83  if( i->second > 0 )
84  q << " " << i->first << ' ' << i->second << '\n';
85  }
86  return q.str();
87  }
88 
89  void entering(mid m) {
90  if( this == 0 || m == 0 ) return;
91  verify( magic == 0x12345678 );
92 
93  Preceeding *_preceeding = us.get();
94  if( _preceeding == 0 )
95  us.reset( _preceeding = new Preceeding() );
96  Preceeding &preceeding = *_preceeding;
97 
98  if( a == m ) {
99  aBreakPoint();
100  if( preceeding[b.c_str()] ) {
101  std::cout << "****** MutexDebugger error! warning " << b << " was locked before " << a << std::endl;
102  verify(false);
103  }
104  }
105 
106  preceeding[m]++;
107  if( preceeding[m] > 1 ) {
108  // recursive re-locking.
109  if( preceeding[m] > maxNest[m] )
110  maxNest[m] = preceeding[m];
111  return;
112  }
113 
114  bool failed = false;
115  std::string err;
116  {
117  boost::mutex::scoped_lock lk(x);
118  followers[m];
119  for( Preceeding::iterator i = preceeding.begin(); i != preceeding.end(); i++ ) {
120  if( m != i->first && i->second > 0 ) {
121  followers[i->first].insert(m);
122  if( followers[m].count(i->first) != 0 ) {
123  failed = true;
124  std::stringstream ss;
125  mid bad = i->first;
126  ss << "mutex problem" <<
127  "\n when locking " << m <<
128  "\n " << bad << " was already locked and should not be."
129  "\n set a and b above to debug.\n";
130  std::stringstream q;
131  for( Preceeding::iterator i = preceeding.begin(); i != preceeding.end(); i++ ) {
132  if( i->first != m && i->first != bad && i->second > 0 )
133  q << " " << i->first << '\n';
134  }
135  std::string also = q.str();
136  if( !also.empty() )
137  ss << "also locked before " << m << " in this thread (no particular order):\n" << also;
138  err = ss.str();
139  break;
140  }
141  }
142  }
143  }
144  if( failed ) {
145  std::cout << err << std::endl;
146  verify( 0 );
147  }
148  }
149  void leaving(mid m) {
150  if( this == 0 || m == 0 ) return; // still in startup pre-main()
151  Preceeding& preceeding = *us.get();
152  preceeding[m]--;
153  if( preceeding[m] < 0 ) {
154  std::cout << "ERROR: lock count for " << m << " is " << preceeding[m] << std::endl;
155  verify( preceeding[m] >= 0 );
156  }
157  }
158  };
159  extern MutexDebugger &mutexDebugger;
160 
161 }
void programEnding()
outputs some diagnostic info on mutexes (on _DEBUG builds)
remove mongo implementation macros after using
macros for mongo internals
only used on _DEBUG builds.
Definition: mutexdebugger.h:53