summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorKaloian Manassiev <kaloian.manassiev@mongodb.com>2014-08-13 14:53:12 -0400
committerKaloian Manassiev <kaloian.manassiev@mongodb.com>2014-08-25 16:58:58 -0400
commit052175eb9e0b793de19575fea37a7fdd95126a50 (patch)
treee9b3d5ad0b30e8825448c6e8dffe19dc5297af05 /src/mongo
parenteacc825ccae11f81a3d06513ceced054b9562ef6 (diff)
downloadmongo-052175eb9e0b793de19575fea37a7fdd95126a50.tar.gz
SERVER-14668 Move DB-level locks to be on the LockManager
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/db/commands/write_commands/batch_executor.cpp2
-rw-r--r--src/mongo/db/concurrency/SConscript3
-rw-r--r--src/mongo/db/concurrency/d_concurrency.cpp505
-rw-r--r--src/mongo/db/concurrency/d_concurrency.h51
-rw-r--r--src/mongo/db/concurrency/d_concurrency_test.cpp82
-rw-r--r--src/mongo/db/concurrency/lock_state.cpp152
-rw-r--r--src/mongo/db/concurrency/lock_state.h132
-rw-r--r--src/mongo/db/concurrency/lock_state_test.cpp54
-rw-r--r--src/mongo/db/instance.cpp2
-rw-r--r--src/mongo/dbtests/framework.cpp6
-rw-r--r--src/mongo/dbtests/threadedtests.cpp7
11 files changed, 393 insertions, 603 deletions
diff --git a/src/mongo/db/commands/write_commands/batch_executor.cpp b/src/mongo/db/commands/write_commands/batch_executor.cpp
index 452c8155b52..c1ba188d76e 100644
--- a/src/mongo/db/commands/write_commands/batch_executor.cpp
+++ b/src/mongo/db/commands/write_commands/batch_executor.cpp
@@ -1120,7 +1120,7 @@ namespace mongo {
}
///////////////////////////////////////////
- Lock::DBWrite writeLock(txn->lockState(), nsString.ns(), useExperimentalDocLocking);
+ Lock::DBWrite writeLock(txn->lockState(), nsString.ns());
///////////////////////////////////////////
if (!checkShardVersion(txn, &shardingState, *updateItem.getRequest(), result))
diff --git a/src/mongo/db/concurrency/SConscript b/src/mongo/db/concurrency/SConscript
index 5f8e83d3ed7..7f81b512ae4 100644
--- a/src/mongo/db/concurrency/SConscript
+++ b/src/mongo/db/concurrency/SConscript
@@ -22,7 +22,8 @@ env.Library(
env.CppUnitTest(
target='lock_mgr_test',
- source=['lock_mgr_new_test.cpp',
+ source=['d_concurrency_test.cpp',
+ 'lock_mgr_new_test.cpp',
'lock_state_test.cpp'
],
LIBDEPS=[
diff --git a/src/mongo/db/concurrency/d_concurrency.cpp b/src/mongo/db/concurrency/d_concurrency.cpp
index f331af72ec5..82f71ae5c09 100644
--- a/src/mongo/db/concurrency/d_concurrency.cpp
+++ b/src/mongo/db/concurrency/d_concurrency.cpp
@@ -32,6 +32,7 @@
#include "mongo/db/concurrency/d_concurrency.h"
+#include "mongo/db/client.h"
#include "mongo/db/commands/server_status.h"
#include "mongo/db/curop.h"
#include "mongo/db/d_globals.h"
@@ -40,7 +41,6 @@
#include "mongo/db/server_parameters.h"
#include "mongo/db/operation_context.h"
#include "mongo/util/assert_util.h"
-#include "mongo/util/concurrency/mapsf.h"
#include "mongo/util/concurrency/qlock.h"
#include "mongo/util/log.h"
#include "mongo/util/mongoutils/str.h"
@@ -60,30 +60,6 @@ namespace mongo {
virtual ~DBTryLockTimeoutException() throw() { }
};
- /* dbname->lock
- Currently these are never deleted - will linger if db was closed. (that should be fine.)
- We don't put the lock inside the Database object as those can come and go with open and
- closes and that would just add complexity.
- Note there is no path concept for where the database is; if somehow you had two db's open
- in different directories with the same name, it will be ok but they are sharing a lock
- then.
- */
- typedef mapsf< StringMap<WrapperForRWLock*> > DBLocksMap;
- static DBLocksMap dblocks;
-
- /* we don't want to touch dblocks too much as a mutex is involved. thus party for that,
- this is here...
- */
- WrapperForRWLock *nestableLocks[] = {
- 0,
- new WrapperForRWLock("local"),
- new WrapperForRWLock("admin")
- };
-
- LockStat* Lock::nestableLockStat( Nestable db ) {
- return &nestableLocks[db]->getStats();
- }
-
class WrapperForQLock {
public:
QLock q;
@@ -144,9 +120,28 @@ namespace mongo {
};
static WrapperForQLock& qlk = *new WrapperForQLock();
- LockStat* Lock::globalLockStat() {
- return &qlk.stats;
- }
+
+ /**
+ * SERVER-14978: This class is temporary and is used to aggregate the per-operation lock
+ * acquisition times. It will go away once we have figured out the lock stats reporting story.
+ */
+ class TrackLockAcquireTime {
+ MONGO_DISALLOW_COPYING(TrackLockAcquireTime);
+ public:
+ TrackLockAcquireTime(char type) : _type(type) {
+
+ }
+
+ ~TrackLockAcquireTime() {
+ if (haveClient()) {
+ cc().curop()->lockStat().recordAcquireTimeMicros(_type, _timer.micros());
+ }
+ }
+
+ private:
+ char _type;
+ Timer _timer;
+ };
RWLockRecursive &Lock::ParallelBatchWriterMode::_batchLock = *(new RWLockRecursive("special"));
@@ -172,75 +167,58 @@ namespace mongo {
Lock::ScopedLock::ScopedLock(LockState* lockState, char type)
- : _lockState(lockState), _pbws_lk(lockState), _type(type), _stat(0) {
+ : _lockState(lockState), _pbws_lk(lockState), _type(type) {
_lockState->enterScopedLock(this);
}
Lock::ScopedLock::~ScopedLock() {
- int prevCount = _lockState->recursiveCount();
- Lock::ScopedLock* what = _lockState->leaveScopedLock();
- fassert( 16171 , prevCount != 1 || what == this );
- }
-
- long long Lock::ScopedLock::acquireFinished( LockStat* stat ) {
- long long acquisitionTime = _timer.micros();
- _timer.reset();
- _stat = stat;
-
- // increment the operation level statistics
- cc().curop()->lockStat().recordAcquireTimeMicros( _type , acquisitionTime );
-
- return acquisitionTime;
+ _lockState->leaveScopedLock(this);
}
void Lock::ScopedLock::tempRelease() {
- long long micros = _timer.micros();
_tempRelease();
_pbws_lk.tempRelease();
- _recordTime( micros ); // might as well do after we unlock
}
- void Lock::ScopedLock::_recordTime( long long micros ) {
- if ( _stat )
- _stat->recordLockTimeMicros( _type , micros );
- cc().curop()->lockStat().recordLockTimeMicros( _type , micros );
- }
-
- void Lock::ScopedLock::recordTime() {
- _recordTime(_timer.micros());
+ void Lock::ScopedLock::relock() {
+ _pbws_lk.relock();
+ _relock();
}
void Lock::ScopedLock::resetTime() {
_timer.reset();
}
-
- void Lock::ScopedLock::relock() {
- _pbws_lk.relock();
- resetTime();
- _relock();
+
+ void Lock::ScopedLock::recordTime() {
+ if (haveClient()) {
+ cc().curop()->lockStat().recordLockTimeMicros(_type, _timer.micros());
+ }
}
Lock::TempRelease::TempRelease(LockState* lockState)
: cant(lockState->isRecursive()), _lockState(lockState) {
- if( cant )
+ if (cant) {
return;
+ }
fassert(16116, _lockState->recursiveCount() == 1);
fassert(16117, _lockState->threadState() != 0);
- scopedLk = _lockState->leaveScopedLock();
+ scopedLk = _lockState->getCurrentScopedLock();
fassert(16118, scopedLk);
invariant(_lockState == scopedLk->_lockState);
scopedLk->tempRelease();
+ _lockState->leaveScopedLock(scopedLk);
}
- Lock::TempRelease::~TempRelease()
- {
- if( cant )
+
+ Lock::TempRelease::~TempRelease() {
+ if (cant) {
return;
+ }
fassert(16119, scopedLk);
fassert(16120, _lockState->threadState() == 0);
@@ -252,16 +230,18 @@ namespace mongo {
void Lock::GlobalWrite::_tempRelease() {
fassert(16121, !noop);
char ts = _lockState->threadState();
- fassert(16122, ts != 'R'); // indicates downgraded; not allowed with temprelease
fassert(16123, ts == 'W');
qlk.unlock_W(_lockState);
+ recordTime();
}
void Lock::GlobalWrite::_relock() {
fassert(16125, !noop);
char ts = _lockState->threadState();
fassert(16126, ts == 0);
- Acquiring a(this, *_lockState);
+
+ TrackLockAcquireTime a('W');
qlk.lock_W(_lockState);
+ resetTime();
}
void Lock::GlobalRead::_tempRelease() {
@@ -269,26 +249,30 @@ namespace mongo {
char ts = _lockState->threadState();
fassert(16128, ts == 'R');
qlk.unlock_R(_lockState);
+ recordTime();
}
+
void Lock::GlobalRead::_relock() {
fassert(16129, !noop);
char ts = _lockState->threadState();
fassert(16130, ts == 0);
- Acquiring a(this, *_lockState);
+
+ TrackLockAcquireTime a('R');
qlk.lock_R(_lockState);
+ resetTime();
}
void Lock::DBWrite::_tempRelease() {
unlockDB();
}
void Lock::DBWrite::_relock() {
- lockDB(_what);
+ lockDB();
}
void Lock::DBRead::_tempRelease() {
unlockDB();
}
void Lock::DBRead::_relock() {
- lockDB(_what);
+ lockDB();
}
Lock::GlobalWrite::GlobalWrite(LockState* lockState, int timeoutms)
@@ -302,7 +286,7 @@ namespace mongo {
}
dassert( ts == 0 );
- Acquiring a(this, *_lockState);
+ TrackLockAcquireTime a('W');
if ( timeoutms != -1 ) {
bool success = qlk.lock_W_try(_lockState, timeoutms);
@@ -311,18 +295,22 @@ namespace mongo {
else {
qlk.lock_W(_lockState);
}
+
+ resetTime();
}
Lock::GlobalWrite::~GlobalWrite() {
if( noop ) {
return;
}
- recordTime(); // for lock stats
+
if (_lockState->threadState() == 'R') { // we downgraded
qlk.unlock_R(_lockState);
}
else {
qlk.unlock_W(_lockState);
}
+
+ recordTime();
}
void Lock::GlobalWrite::downgrade() {
verify( !noop );
@@ -351,7 +339,7 @@ namespace mongo {
return;
}
- Acquiring a(this, *_lockState);
+ TrackLockAcquireTime a('R');
if ( timeoutms != -1 ) {
bool success = qlk.lock_R_try(_lockState, timeoutms);
@@ -361,302 +349,188 @@ namespace mongo {
// we are unlocked in the qlock/top sense. lock_R will assert if we are in an in compatible state
qlk.lock_R(_lockState);
}
+
+ resetTime();
}
+
Lock::GlobalRead::~GlobalRead() {
if( !noop ) {
- recordTime(); // for lock stats
qlk.unlock_R(_lockState);
+ recordTime();
}
}
- void Lock::DBWrite::lockNestable(Nestable db) {
- _nested = true;
-
- if (_lockState->nestableCount()) {
- if( db != _lockState->whichNestable() ) {
- error() << "can't lock local and admin db at the same time " << (int) db << ' ' << (int) _lockState->whichNestable() << endl;
- fassert(16131,false);
- }
- verify( _lockState->nestableCount() > 0 );
- }
- else {
- fassert(16132,_weLocked==0);
- _lockState->lockedNestable(db, 1);
- _weLocked = nestableLocks[db];
- _weLocked->lock();
- }
- }
- void Lock::DBRead::lockNestable(Nestable db) {
- _nested = true;
-
- if (_lockState->nestableCount()) {
- // we are nested in our locking of local. previous lock could be read OR write lock on local.
- }
- else {
- _lockState->lockedNestable(db, -1);
- fassert(16133,_weLocked==0);
- _weLocked = nestableLocks[db];
- _weLocked->lock_shared();
- }
+ static bool isLocalOrAdmin(const string& dbName) {
+ return (dbName == "local" || dbName == "admin");
}
- void Lock::DBWrite::lockOtherRead(const StringData& db) {
- fassert(18517, !db.empty());
- // we do checks first, as on assert destructor won't be called so don't want to be half finished with our work.
- if( _lockState->otherCount() ) {
- // nested. prev could be read or write. if/when we do temprelease with DBRead/DBWrite we will need to increment/decrement here
- // (so we can not release or assert if nested). temprelease we should avoid if we can though, it's a bit of an anti-pattern.
- invariant(db == _lockState->otherName());
- return;
- }
+ Lock::DBWrite::DBWrite(LockState* lockState, const StringData& dbOrNs)
+ : ScopedLock(lockState, 'w'),
+ _lockAcquired(false),
+ _ns(dbOrNs.toString()) {
- // first lock for this db. check consistent order with local db lock so we never deadlock. local always comes last
- invariant(_lockState->nestableCount() == 0);
+ fassert(16253, !_ns.empty());
+ lockDB();
+ }
- if (db != _lockState->otherName()) {
- DBLocksMap::ref r(dblocks);
- WrapperForRWLock*& lock = r[db];
- if (lock == NULL) {
- lock = new WrapperForRWLock(db);
- }
+ Lock::DBWrite::~DBWrite() {
+ unlockDB();
+ }
- _lockState->lockedOther(db, -1, lock);
- }
- else {
- DEV OCCASIONALLY{ dassert(dblocks.get(db) == _lockState->otherLock()); }
- _lockState->lockedOther(-1);
+ void Lock::DBWrite::lockTop() {
+ switch (_lockState->threadState()) {
+ case 0:
+ _lockState->lockedStart('w');
+ qlk.q.lock_w();
+ break;
+ case 'w':
+ break;
+ default:
+ invariant(!"Invalid thread state");
}
-
- fassert(18515, _weLocked == 0);
- _lockState->otherLock()->lock_shared();
- _weLocked = _lockState->otherLock();
}
- void Lock::DBWrite::lockOtherWrite(const StringData& db) {
- fassert(16252, !db.empty());
-
- // we do checks first, as on assert destructor won't be called so don't want to be half finished with our work.
- if (_lockState->otherCount()) {
- // nested. if/when we do temprelease with DBWrite we will need to increment here
- // (so we can not release or assert if nested).
- invariant(db == _lockState->otherName());
+ void Lock::DBWrite::lockDB() {
+ if (_lockState->isW()) {
return;
}
- // first lock for this db. check consistent order with local db lock so we never deadlock. local always comes last
- invariant(_lockState->nestableCount() == 0);
+ TrackLockAcquireTime a('w');
- if (db != _lockState->otherName()) {
- DBLocksMap::ref r(dblocks);
- WrapperForRWLock*& lock = r[db];
- if (lock == NULL) {
- lock = new WrapperForRWLock(db);
- }
+ const StringData db = nsToDatabaseSubstring(_ns);
+ const newlm::ResourceId resIdDb(newlm::RESOURCE_DATABASE, db);
- _lockState->lockedOther(db, 1, lock);
+ // As weird as it looks, currently the top (global 'w' lock) is acquired after the DB lock
+ // has been acquired in order to avoid deadlock with UpgradeGlobalLockToExclusive, which is
+ // called while holding some form of write lock on the database.
+ //
+ // However, for the local/admin database, since its lock is acquired usually after another
+ // DB lock is already held, the nested database's lock (local or admin) needs to be
+ // acquired after the global lock. Consider the following sequence:
+ //
+ // T1: Acquires DBWrite on 'other' DB, then 'w'
+ // T2: Wants to flush so it queues behind T1 for 'R' lock
+ // T3: Acquires DBRead on 'local' DB, then blocks on 'r' (T2 has precedence, due to 'R')
+ // T1: Does whatever writes it does and tries to acquire DBWrite on 'local' in order to
+ // insert in the OpLog, and blocks because of T3's 'r' lock on local.
+ //
+ // This is a deadlock T1 -> T3 -> T2 -> T1.
+ //
+ // Moving the acquisition of local or admin's lock to be after the global lock solves this
+ // problem, which would otherwise break replication.
+ //
+ // TODO: This whole lock ordering mess will go away once we make flush be it's own lock
+ // instead of doing upgrades on the global lock.
+ if (isLocalOrAdmin(db.toString())) {
+ lockTop();
}
- else {
- DEV OCCASIONALLY{ dassert(dblocks.get(db) == _lockState->otherLock()); }
- _lockState->lockedOther(1);
- }
-
- fassert(16134,_weLocked==0);
-
- _lockState->otherLock()->lock();
- _weLocked = _lockState->otherLock();
- }
-
- static Lock::Nestable n(const StringData& db) {
- if( db == "local" )
- return Lock::local;
- if( db == "admin" )
- return Lock::admin;
- return Lock::notnestable;
- }
-
- void Lock::DBWrite::lockDB(const string& ns) {
- fassert( 16253, !ns.empty() );
- Acquiring a(this, *_lockState);
- _locked_W=false;
- _locked_w=false;
- _weLocked=0;
+ _lockState->lock(resIdDb, newlm::MODE_X);
- invariant(!_lockState->hasAnyReadLock());
+ if (!isLocalOrAdmin(db.toString())) {
+ lockTop();
+ }
- if (_lockState->isW())
- return;
+ _lockAcquired = true;
- StringData db = nsToDatabaseSubstring( ns );
- Nestable nested = n(db);
- if( nested == admin ) {
- // we can't nestedly lock both admin and local as implemented. so lock_W.
- qlk.lock_W(_lockState);
- _locked_W = true;
- return;
- }
+ resetTime();
+ }
- if (!nested) {
- if (_isIntentWrite) {
- lockOtherRead(db);
- }
- else {
- lockOtherWrite(db);
+ void Lock::DBWrite::unlockDB() {
+ if (_lockAcquired) {
+ const StringData db = nsToDatabaseSubstring(_ns);
+ const newlm::ResourceId resIdDb(newlm::RESOURCE_DATABASE, db);
+
+ _lockState->unlock(resIdDb);
+
+ // The last one frees the Global lock
+ if (_lockState->recursiveCount() == 1) {
+ invariant(_lockState->threadState() == 'w');
+ qlk.q.unlock_w();
+ _lockState->unlocked();
+ recordTime();
}
- }
- lockTop();
- if( nested )
- lockNestable(nested);
+ _lockAcquired = false;
+ }
}
- void Lock::DBRead::lockDB(const string& ns) {
- fassert( 16254, !ns.empty() );
-
- Acquiring a(this, *_lockState);
- _locked_r=false;
- _weLocked=0;
-
- if (_lockState->isRW())
- return;
-
- StringData db = nsToDatabaseSubstring(ns);
- Nestable nested = n(db);
- if( !nested )
- lockOther(db);
- lockTop();
- if( nested )
- lockNestable(nested);
- }
- Lock::DBWrite::DBWrite(LockState* lockState, const StringData& ns, bool intentWrite)
- : ScopedLock(lockState, 'w'),
- _isIntentWrite(intentWrite),
- _what(ns.toString()),
- _nested(false) {
- lockDB(_what);
- }
+ Lock::DBRead::DBRead(LockState* lockState, const StringData& dbOrNs)
+ : ScopedLock(lockState, 'r'),
+ _lockAcquired(false),
+ _ns(dbOrNs.toString()) {
- Lock::DBRead::DBRead(LockState* lockState, const StringData& ns)
- : ScopedLock(lockState, 'r' ), _what(ns.toString()), _nested(false) {
- lockDB( _what );
+ fassert(16254, !_ns.empty());
+ lockDB();
}
- Lock::DBWrite::~DBWrite() {
- unlockDB();
- }
Lock::DBRead::~DBRead() {
unlockDB();
}
- void Lock::DBWrite::unlockDB() {
- if( _weLocked ) {
- recordTime(); // for lock stats
-
- if ( _nested )
- _lockState->unlockedNestable();
- else
- _lockState->unlockedOther();
-
- _weLocked->unlock();
- }
-
- if( _locked_w ) {
- wassert(_lockState->threadState() == 'w');
- _lockState->unlocked();
- qlk.q.unlock_w();
- }
-
- if( _locked_W ) {
- qlk.unlock_W(_lockState);
- }
-
- _weLocked = 0;
- _locked_W = _locked_w = false;
- }
- void Lock::DBRead::unlockDB() {
- if( _weLocked ) {
- recordTime(); // for lock stats
-
- if( _nested )
- _lockState->unlockedNestable();
- else
- _lockState->unlockedOther();
-
- _weLocked->unlock_shared();
- }
-
- if( _locked_r ) {
- wassert(_lockState->threadState() == 'r');
- _lockState->unlocked();
- qlk.q.unlock_r();
- }
- _weLocked = 0;
- _locked_r = false;
- }
-
- void Lock::DBWrite::lockTop() {
+ void Lock::DBRead::lockTop() {
switch (_lockState->threadState()) {
- case 'w':
+ case 0:
+ _lockState->lockedStart('r');
+ qlk.q.lock_r();
break;
- default:
- verify(false);
- case 0 :
- verify(_lockState->threadState() == 0);
- _lockState->lockedStart('w');
- qlk.q.lock_w();
- _locked_w = true;
- }
- }
- void Lock::DBRead::lockTop() {
- switch (_lockState->threadState()) {
case 'r':
case 'w':
break;
default:
- verify(false);
- case 0 :
- verify(_lockState->threadState() == 0);
- _lockState->lockedStart('r');
- qlk.q.lock_r();
- _locked_r = true;
+ invariant(false);
}
}
- void Lock::DBRead::lockOther(const StringData& db) {
- fassert( 16255, !db.empty() );
-
- // we do checks first, as on assert destructor won't be called so don't want to be half finished with our work.
- if( _lockState->otherCount() ) {
- // nested. prev could be read or write. if/when we do temprelease with DBRead/DBWrite we will need to increment/decrement here
- // (so we can not release or assert if nested). temprelease we should avoid if we can though, it's a bit of an anti-pattern.
- invariant(db == _lockState->otherName());
+ void Lock::DBRead::lockDB() {
+ if (_lockState->isRW()) {
return;
}
- // first lock for this db. check consistent order with local db lock so we never deadlock. local always comes last
- invariant(_lockState->nestableCount() == 0);
+ TrackLockAcquireTime a('r');
- if (db != _lockState->otherName()) {
- DBLocksMap::ref r(dblocks);
- WrapperForRWLock*& lock = r[db];
- if (lock == NULL) {
- lock = new WrapperForRWLock(db);
- }
+ const StringData db = nsToDatabaseSubstring(_ns);
+ const newlm::ResourceId resIdDb(newlm::RESOURCE_DATABASE, db);
- _lockState->lockedOther(db, -1, lock);
+ // Keep the order of acquisition of the 'r' lock the same as it is in DBWrite in order to
+ // avoid deadlocks. See the comment inside DBWrite::lockDB for more information on the
+ // reason for the weird ordering of locks.
+ if (isLocalOrAdmin(db.toString())) {
+ lockTop();
}
- else {
- DEV OCCASIONALLY{ dassert(dblocks.get(db) == _lockState->otherLock()); }
- _lockState->lockedOther(-1);
+
+ _lockState->lock(resIdDb, newlm::MODE_S);
+
+ if (!isLocalOrAdmin(db.toString())) {
+ lockTop();
}
- fassert(16135,_weLocked==0);
- _lockState->otherLock()->lock_shared();
- _weLocked = _lockState->otherLock();
+ _lockAcquired = true;
+ resetTime();
}
+ void Lock::DBRead::unlockDB() {
+ if (_lockAcquired) {
+ const StringData db = nsToDatabaseSubstring(_ns);
+ const newlm::ResourceId resIdDb(newlm::RESOURCE_DATABASE, db);
+
+ _lockState->unlock(resIdDb);
+
+ // The last one frees the Global lock
+ if (_lockState->recursiveCount() == 1) {
+ invariant(_lockState->threadState() == 'r');
+ qlk.q.unlock_r();
+ _lockState->unlocked();
+ recordTime();
+ }
+
+ _lockAcquired = false;
+ }
+ }
+
+
Lock::UpgradeGlobalLockToExclusive::UpgradeGlobalLockToExclusive(LockState* lockState)
: _lockState(lockState) {
fassert( 16187, _lockState->threadState() == 'w' );
@@ -767,7 +641,7 @@ namespace mongo {
BSONObjBuilder t;
t.append( "totalTime" , (long long)(1000 * ( curTimeMillis64() - _started ) ) );
- t.append( "lockTime" , Lock::globalLockStat()->getTimeLocked( 'W' ) );
+ t.append( "lockTime" , qlk.stats.getTimeLocked( 'W' ) );
// This returns the blocked lock states
{
@@ -811,14 +685,9 @@ namespace mongo {
BSONObj generateSection( const BSONElement& configElement ) const {
BSONObjBuilder b;
b.append(".", qlk.stats.report());
- b.append("admin", nestableLocks[Lock::admin]->getStats().report());
- b.append("local", nestableLocks[Lock::local]->getStats().report());
- {
- DBLocksMap::ref r(dblocks);
- for( DBLocksMap::const_iterator i = r.r.begin(); i != r.r.end(); ++i ) {
- b.append(i->first, i->second->getStats().report());
- }
- }
+
+ // TODO: Add per-db lock information here
+
return b.obj();
}
diff --git a/src/mongo/db/concurrency/d_concurrency.h b/src/mongo/db/concurrency/d_concurrency.h
index 03940bb13a8..bf77649601d 100644
--- a/src/mongo/db/concurrency/d_concurrency.h
+++ b/src/mongo/db/concurrency/d_concurrency.h
@@ -42,16 +42,10 @@
namespace mongo {
- class WrapperForRWLock;
class LockState;
class Lock : boost::noncopyable {
public:
- enum Nestable { notnestable=0, local, admin };
-
- static LockStat* globalLockStat();
- static LockStat* nestableLockStat( Nestable db );
-
class ScopedLock;
// note: avoid TempRelease when possible. not a good thing.
@@ -84,19 +78,18 @@ namespace mongo {
public:
virtual ~ScopedLock();
- /** @return micros since we started acquiring */
- long long acquireFinished( LockStat* stat );
+ // Start recording a new period, starting now()
+ void resetTime();
// Accrue elapsed lock time since last we called reset
void recordTime();
- // Start recording a new period, starting now()
- void resetTime();
protected:
- explicit ScopedLock(LockState* lockState, char type );
+ explicit ScopedLock(LockState* lockState, char type );
private:
friend struct TempRelease;
+
void tempRelease(); // TempRelease class calls these
void relock();
@@ -123,10 +116,8 @@ namespace mongo {
ParallelBatchWriterSupport _pbws_lk;
- void _recordTime( long long micros );
Timer _timer;
char _type; // 'r','w','R','W'
- LockStat* _stat; // the stat for the relevant lock to increment when we're done
};
// note that for these classes recursive locking is ok if the recursive locking "makes sense"
@@ -160,19 +151,8 @@ namespace mongo {
// lock this database. do not shared_lock globally first, that is handledin herein.
class DBWrite : public ScopedLock {
- /**
- * flow
- * 1) lockDB
- * a) lockTop
- * b) lockNestable or lockOther
- * 2) unlockDB
- */
-
void lockTop();
- void lockNestable(Nestable db);
- void lockOtherWrite(const StringData& db);
- void lockOtherRead(const StringData& db);
- void lockDB(const std::string& ns);
+ void lockDB();
void unlockDB();
protected:
@@ -180,24 +160,18 @@ namespace mongo {
void _relock();
public:
- DBWrite(LockState* lockState, const StringData& dbOrNs, bool intentWrite = false);
+ DBWrite(LockState* lockState, const StringData& dbOrNs);
virtual ~DBWrite();
private:
- bool _locked_w;
- bool _locked_W;
- bool _isIntentWrite;
- WrapperForRWLock *_weLocked;
- const std::string _what;
- bool _nested;
+ bool _lockAcquired;
+ const std::string _ns;
};
// lock this database for reading. do not shared_lock globally first, that is handledin herein.
class DBRead : public ScopedLock {
void lockTop();
- void lockNestable(Nestable db);
- void lockOther(const StringData& db);
- void lockDB(const std::string& ns);
+ void lockDB();
void unlockDB();
protected:
@@ -209,11 +183,8 @@ namespace mongo {
virtual ~DBRead();
private:
- bool _locked_r;
- WrapperForRWLock *_weLocked;
- std::string _what;
- bool _nested;
-
+ bool _lockAcquired;
+ const std::string _ns;
};
/**
diff --git a/src/mongo/db/concurrency/d_concurrency_test.cpp b/src/mongo/db/concurrency/d_concurrency_test.cpp
new file mode 100644
index 00000000000..19a2bbead66
--- /dev/null
+++ b/src/mongo/db/concurrency/d_concurrency_test.cpp
@@ -0,0 +1,82 @@
+/**
+ * Copyright (C) 2014 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include <boost/scoped_ptr.hpp>
+#include <boost/thread/thread.hpp>
+
+#include "mongo/db/concurrency/d_concurrency.h"
+#include "mongo/db/concurrency/lock_state.h"
+#include "mongo/unittest/unittest.h"
+
+
+// Most of the tests here will be removed once we move everything over to using LockManager
+//
+
+namespace mongo {
+
+ // These two tests ensure that we have preserved the behaviour of TempRelease
+ TEST(DConcurrency, TempReleaseOneDB) {
+ LockState ls;
+
+ Lock::DBRead r1(&ls, "db1");
+
+ {
+ Lock::TempRelease tempRelease(&ls);
+ }
+
+ ls.assertAtLeastReadLocked("db1");
+ }
+
+ TEST(DConcurrency, TempReleaseRecursive) {
+ LockState ls;
+
+ Lock::DBRead r1(&ls, "db1");
+ Lock::DBRead r2(&ls, "db2");
+
+ {
+ Lock::TempRelease tempRelease(&ls);
+
+ ls.assertAtLeastReadLocked("db1");
+ ls.assertAtLeastReadLocked("db2");
+ }
+
+ ls.assertAtLeastReadLocked("db1");
+ ls.assertAtLeastReadLocked("db2");
+ }
+
+ TEST(DConcurrency, MultipleDBLocks) {
+ LockState ls;
+
+ Lock::DBWrite r1(&ls, "db1");
+ Lock::DBRead r2(&ls, "db1");
+
+ ls.assertWriteLocked("db1");
+ }
+} // namespace mongo
diff --git a/src/mongo/db/concurrency/lock_state.cpp b/src/mongo/db/concurrency/lock_state.cpp
index a61d5133122..49307f95a7e 100644
--- a/src/mongo/db/concurrency/lock_state.cpp
+++ b/src/mongo/db/concurrency/lock_state.cpp
@@ -46,10 +46,6 @@ namespace mongo {
_batchWriter(false),
_recursive(0),
_threadState(0),
- _whichNestable( Lock::notnestable ),
- _nestableCount(0),
- _otherCount(0),
- _otherLock(NULL),
_scopedLk(NULL),
_lockPending(false),
_lockPendingParallelWriter(false)
@@ -68,24 +64,6 @@ namespace mongo {
return _threadState == 'r' || _threadState == 'R';
}
- bool LockState::isLocked( const StringData& ns ) const {
- char db[MaxDatabaseNameLen];
- nsToDatabase(ns, db);
-
- DEV verify( _otherName.find( '.' ) == string::npos ); // XXX this shouldn't be here, but somewhere
- if ( _otherCount && db == _otherName )
- return true;
-
- if ( _nestableCount ) {
- if ( mongoutils::str::equals( db , "local" ) )
- return _whichNestable == Lock::local;
- if ( mongoutils::str::equals( db , "admin" ) )
- return _whichNestable == Lock::admin;
- }
-
- return false;
- }
-
bool LockState::isLocked() const {
return threadState() != 0;
}
@@ -99,7 +77,10 @@ namespace mongo {
return true;
}
- return isLocked(ns);
+ const StringData db = nsToDatabaseSubstring(ns);
+ const newlm::ResourceId resIdNs(newlm::RESOURCE_DATABASE, db);
+
+ return isLockHeldForMode(resIdNs, newlm::MODE_X);
}
bool LockState::isAtLeastReadLocked(const StringData& ns) const {
@@ -107,7 +88,11 @@ namespace mongo {
return true; // global
if (threadState() == 0)
return false;
- return isLocked(ns);
+
+ const StringData db = nsToDatabaseSubstring(ns);
+ const newlm::ResourceId resIdNs(newlm::RESOURCE_DATABASE, db);
+
+ return isLockHeldForMode(resIdNs, newlm::MODE_S);
}
bool LockState::isLockedForCommitting() const {
@@ -138,6 +123,7 @@ namespace mongo {
void LockState::lockedStart( char newState ) {
_threadState = newState;
}
+
void LockState::unlocked() {
_threadState = 0;
}
@@ -174,22 +160,9 @@ namespace mongo {
buf[1] = 0;
b.append("^", buf);
}
- if( _nestableCount ) {
- string s = "?";
- if( _whichNestable == Lock::local )
- s = "^local";
- else if( _whichNestable == Lock::admin )
- s = "^admin";
- b.append(s, kind(_nestableCount));
- }
- if( _otherCount ) {
- WrapperForRWLock *k = _otherLock;
- if( k ) {
- string s = "^";
- s += k->name();
- b.append(s, kind(_otherCount));
- }
- }
+
+ // SERVER-14978: Report state from the Locker
+
BSONObj o = b.obj();
if (!o.isEmpty()) {
res->append("locks", o);
@@ -205,104 +178,33 @@ namespace mongo {
ss << "unlocked";
}
else {
- ss << s;
- if( _recursive ) {
- ss << " recursive:" << _recursive;
- }
- ss << " otherCount:" << _otherCount;
- if( _otherCount ) {
- ss << " otherdb:" << _otherName;
- }
- if( _nestableCount ) {
- ss << " nestableCount:" << _nestableCount << " which:";
- if( _whichNestable == Lock::local )
- ss << "local";
- else if( _whichNestable == Lock::admin )
- ss << "admin";
- else
- ss << (int)_whichNestable;
- }
+ // SERVER-14978: Dump lock stats information
}
log() << ss.str() << endl;
}
- void LockState::enterScopedLock( Lock::ScopedLock* lock ) {
+ void LockState::enterScopedLock(Lock::ScopedLock* lock) {
_recursive++;
- if ( _recursive == 1 ) {
- fassert(16115, _scopedLk == 0);
+ if (_recursive == 1) {
+ invariant(_scopedLk == NULL);
_scopedLk = lock;
}
}
- Lock::ScopedLock* LockState::leaveScopedLock() {
- _recursive--;
- dassert( _recursive < 10000 );
- Lock::ScopedLock* temp = _scopedLk;
-
- if ( _recursive > 0 ) {
- return NULL;
- }
-
- _scopedLk = NULL;
- return temp;
- }
-
- void LockState::lockedNestable( Lock::Nestable what , int type) {
- verify( type );
- _whichNestable = what;
- _nestableCount += type;
- }
-
- void LockState::unlockedNestable() {
- _whichNestable = Lock::notnestable;
- _nestableCount = 0;
- }
-
- void LockState::lockedOther( int type ) {
- fassert( 16231 , _otherCount == 0 );
- _otherCount = type;
- }
-
- void LockState::lockedOther( const StringData& other , int type , WrapperForRWLock* lock ) {
- fassert( 16170 , _otherCount == 0 );
- _otherName = other.toString();
- _otherCount = type;
- _otherLock = lock;
- }
-
- void LockState::unlockedOther() {
- // we leave _otherName and _otherLock set as
- // _otherLock exists to cache a pointer
- _otherCount = 0;
+ Lock::ScopedLock* LockState::getCurrentScopedLock() const {
+ invariant(_recursive == 1);
+ return _scopedLk;
}
- LockStat* LockState::getRelevantLockStat() {
- if ( _whichNestable )
- return Lock::nestableLockStat( _whichNestable );
-
- if ( _otherCount && _otherLock )
- return &_otherLock->getStats();
-
- if ( isRW() )
- return Lock::globalLockStat();
-
- return 0;
- }
-
-
- Acquiring::Acquiring( Lock::ScopedLock* lock, LockState& ls )
- : _lock( lock ), _ls( ls ){
- _ls._lockPending = true;
- }
-
- Acquiring::~Acquiring() {
- _ls._lockPending = false;
- LockStat* stat = _ls.getRelevantLockStat();
- if ( stat && _lock ) {
- // increment the global stats for this counter
- stat->recordAcquireTimeMicros( _ls.threadState(), _lock->acquireFinished( stat ) );
+ void LockState::leaveScopedLock(Lock::ScopedLock* lock) {
+ if (_recursive == 1) {
+ // Sanity check we are releasing the same lock
+ invariant(_scopedLk == lock);
+ _scopedLk = NULL;
}
+ _recursive--;
}
+
AcquiringParallelWriter::AcquiringParallelWriter( LockState& ls )
: _ls( ls ) {
diff --git a/src/mongo/db/concurrency/lock_state.h b/src/mongo/db/concurrency/lock_state.h
index dcc69216b78..d349fbea96f 100644
--- a/src/mongo/db/concurrency/lock_state.h
+++ b/src/mongo/db/concurrency/lock_state.h
@@ -1,32 +1,32 @@
// lock_state.h
/**
-* Copyright (C) 2008 10gen Inc.
-*
-* This program is free software: you can redistribute it and/or modify
-* it under the terms of the GNU Affero General Public License, version 3,
-* as published by the Free Software Foundation.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU Affero General Public License for more details.
-*
-* You should have received a copy of the GNU Affero General Public License
-* along with this program. If not, see <http://www.gnu.org/licenses/>.
-*
-* As a special exception, the copyright holders give permission to link the
-* code of portions of this program with the OpenSSL library under certain
-* conditions as described in each individual source file and distribute
-* linked combinations including the program with the OpenSSL library. You
-* must comply with the GNU Affero General Public License in all respects for
-* all of the code used other than as permitted herein. If you modify file(s)
-* with this exception, you may extend this exception to your version of the
-* file(s), but you are not obligated to do so. If you do not wish to do so,
-* delete this exception statement from your version. If you delete this
-* exception statement from all source files in the program, then also delete
-* it in the license file.
-*/
+ * Copyright (C) 2008 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
#pragma once
@@ -35,10 +35,6 @@
namespace mongo {
-
- class Acquiring;
-
-
namespace newlm {
/**
@@ -185,7 +181,14 @@ namespace newlm {
} // namespace newlm
- // per thread
+ /**
+ * One of these exists per OperationContext and serves as interface for acquiring locks and
+ * obtaining lock statistics for this particular operation.
+ *
+ * TODO: It is only temporary that this class inherits from Locker. Both will eventually be
+ * merged and most of the code in LockState will go away (i.e., once we move the GlobalLock to
+ * be its own lock resource under the lock manager).
+ */
class LockState : public newlm::Locker {
public:
LockState();
@@ -198,7 +201,9 @@ namespace newlm {
unsigned recursiveCount() const { return _recursive; }
/**
- * @return 0 rwRW
+ * Indicates the mode of acquisition of the GlobalLock by this particular thread. The
+ * return values are '0' (no global lock is held), 'r', 'w', 'R', 'W'. See the commends of
+ * QLock for more information on what these modes mean.
*/
char threadState() const { return _threadState; }
@@ -206,7 +211,6 @@ namespace newlm {
bool isW() const; // W
bool hasAnyReadLock() const; // explicitly rR
- bool isLocked(const StringData& ns) const; // rwRW
bool isLocked() const;
bool isWriteLocked() const;
bool isWriteLocked(const StringData& ns) const;
@@ -231,25 +235,14 @@ namespace newlm {
* this is mostly for W_to_R or R_to_W
*/
void changeLockState( char newstate );
-
- Lock::Nestable whichNestable() const { return _whichNestable; }
- int nestableCount() const { return _nestableCount; }
-
- int otherCount() const { return _otherCount; }
- const std::string& otherName() const { return _otherName; }
- WrapperForRWLock* otherLock() const { return _otherLock; }
- void enterScopedLock( Lock::ScopedLock* lock );
- Lock::ScopedLock* leaveScopedLock();
-
- void lockedNestable( Lock::Nestable what , int type );
- void unlockedNestable();
- void lockedOther( const StringData& db , int type , WrapperForRWLock* lock );
- void lockedOther( int type ); // "same lock as last time" case
- void unlockedOther();
+ // Those are only used for TempRelease. Eventually they should be removed.
+ void enterScopedLock(Lock::ScopedLock* lock);
+ Lock::ScopedLock* getCurrentScopedLock() const;
+ void leaveScopedLock(Lock::ScopedLock* lock);
+
bool _batchWriter;
- LockStat* getRelevantLockStat();
void recordLockTime() { _scopedLk->recordTime(); }
void resetLockTime() { _scopedLk->resetTime(); }
@@ -259,14 +252,6 @@ namespace newlm {
// global lock related
char _threadState; // 0, 'r', 'w', 'R', 'W'
- // db level locking related
- Lock::Nestable _whichNestable;
- int _nestableCount; // recursive lock count on local or admin db XXX - change name
-
- int _otherCount; // >0 means write lock, <0 read lock - XXX change name
- std::string _otherName; // which database are we locking and working with (besides local/admin)
- WrapperForRWLock* _otherLock; // so we don't have to check the map too often (the map has a mutex)
-
// for temprelease
// for the nonrecursive case. otherwise there would be many
// the first lock goes here, which is ok since we can't yield recursive locks
@@ -275,42 +260,9 @@ namespace newlm {
bool _lockPending;
bool _lockPendingParallelWriter;
- friend class Acquiring;
friend class AcquiringParallelWriter;
};
- class WrapperForRWLock : boost::noncopyable {
- SimpleRWLock rw;
- SimpleMutex m;
- bool sharedLatching;
- LockStat stats;
- public:
- std::string name() const { return rw.name; }
- LockStat& getStats() { return stats; }
-
- WrapperForRWLock(const StringData& name)
- : rw(name), m(name) {
- // For the local datbase, all operations are short,
- // either writing one entry, or doing a tail.
- // In tests, use a SimpleMutex is much faster for the local db.
- sharedLatching = name != "local";
- }
- void lock() { if ( sharedLatching ) { rw.lock(); } else { m.lock(); } }
- void lock_shared() { if ( sharedLatching ) { rw.lock_shared(); } else { m.lock(); } }
- void unlock() { if ( sharedLatching ) { rw.unlock(); } else { m.unlock(); } }
- void unlock_shared() { if ( sharedLatching ) { rw.unlock_shared(); } else { m.unlock(); } }
- };
-
- class ScopedLock;
-
- class Acquiring {
- public:
- Acquiring( Lock::ScopedLock* lock, LockState& ls );
- ~Acquiring();
- private:
- Lock::ScopedLock* _lock;
- LockState& _ls;
- };
class AcquiringParallelWriter {
public:
diff --git a/src/mongo/db/concurrency/lock_state_test.cpp b/src/mongo/db/concurrency/lock_state_test.cpp
index 29bf759c458..28cb9c700da 100644
--- a/src/mongo/db/concurrency/lock_state_test.cpp
+++ b/src/mongo/db/concurrency/lock_state_test.cpp
@@ -1,30 +1,32 @@
/**
-* Copyright (C) 2014 MongoDB Inc.
-*
-* This program is free software: you can redistribute it and/or modify
-* it under the terms of the GNU Affero General Public License, version 3,
-* as published by the Free Software Foundation.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU Affero General Public License for more details.
-*
-* You should have received a copy of the GNU Affero General Public License
-* along with this program. If not, see <http://www.gnu.org/licenses/>.
-*
-* As a special exception, the copyright holders give permission to link the
-* code of portions of this program with the OpenSSL library under certain
-* conditions as described in each individual source file and distribute
-* linked combinations including the program with the OpenSSL library. You
-* must comply with the GNU Affero General Public License in all respects for
-* all of the code used other than as permitted herein. If you modify file(s)
-* with this exception, you may extend this exception to your version of the
-* file(s), but you are not obligated to do so. If you do not wish to do so,
-* delete this exception statement from your version. If you delete this
-* exception statement from all source files in the program, then also delete
-* it in the license file.
-*/
+ * Copyright (C) 2014 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
#include <boost/scoped_ptr.hpp>
#include <boost/thread/thread.hpp>
diff --git a/src/mongo/db/instance.cpp b/src/mongo/db/instance.cpp
index 6c8a8f5d61e..fb827ad9b99 100644
--- a/src/mongo/db/instance.cpp
+++ b/src/mongo/db/instance.cpp
@@ -603,7 +603,7 @@ namespace mongo {
UpdateExecutor executor(&request, &op.debug());
uassertStatusOK(executor.prepare());
- Lock::DBWrite lk(txn->lockState(), ns.ns(), useExperimentalDocLocking);
+ Lock::DBWrite lk(txn->lockState(), ns.ns());
// if this ever moves to outside of lock, need to adjust check
// Client::Context::_finishInit
diff --git a/src/mongo/dbtests/framework.cpp b/src/mongo/dbtests/framework.cpp
index c0704f60e2d..2604571300c 100644
--- a/src/mongo/dbtests/framework.cpp
+++ b/src/mongo/dbtests/framework.cpp
@@ -28,7 +28,7 @@
* then also delete it in the license file.
*/
-#include "mongo/pch.h"
+#include "mongo/platform/basic.h"
#include "mongo/dbtests/framework.h"
@@ -40,6 +40,7 @@
#include "mongo/base/initializer.h"
#include "mongo/base/status.h"
#include "mongo/db/client.h"
+#include "mongo/db/concurrency/lock_state.h"
#include "mongo/db/ops/update.h"
#include "mongo/db/storage/storage_engine.h"
#include "mongo/dbtests/dbtests.h"
@@ -92,6 +93,9 @@ namespace mongo {
}
else if (minutesRunning > 1){
warning() << currentTestName << " has been running for more than " << minutesRunning-1 << " minutes." << endl;
+
+ // See what is stuck
+ newlm::Locker::dumpGlobalLockManager();
}
}
}
diff --git a/src/mongo/dbtests/threadedtests.cpp b/src/mongo/dbtests/threadedtests.cpp
index 425409d3848..ff03f10d65f 100644
--- a/src/mongo/dbtests/threadedtests.cpp
+++ b/src/mongo/dbtests/threadedtests.cpp
@@ -128,7 +128,14 @@ namespace ThreadedTests {
virtual void subthread(int tnumber) {
Client::initThread("mongomutextest");
+
LockState lockState;
+ mongo::unittest::log().stream()
+ << "Thread "
+ << boost::this_thread::get_id()
+ << " has lock state "
+ << &lockState
+ << '\n';
sleepmillis(0);
for( int i = 0; i < N; i++ ) {