diff options
Diffstat (limited to 'src/mongo/db/concurrency')
-rw-r--r-- | src/mongo/db/concurrency/lock_state.cpp | 20 | ||||
-rw-r--r-- | src/mongo/db/concurrency/lock_state.h | 5 | ||||
-rw-r--r-- | src/mongo/db/concurrency/lock_state_test.cpp | 19 |
3 files changed, 44 insertions, 0 deletions
diff --git a/src/mongo/db/concurrency/lock_state.cpp b/src/mongo/db/concurrency/lock_state.cpp index 793a2a2dd7d..cd781a0cc40 100644 --- a/src/mongo/db/concurrency/lock_state.cpp +++ b/src/mongo/db/concurrency/lock_state.cpp @@ -230,6 +230,21 @@ void LockerImpl::dump() const { "requests"_attr = entries); } +void LockerImpl::_dumpLockerAndLockManagerRequests() { + // Log the _requests that this locker holds. This will provide identifying information to cross + // reference with the LockManager dump below for extra information. + dump(); + + // Log the LockManager's lock information. Given the locker 'dump()' above, we should be able to + // easily cross reference to find the lock info matching this operation. The LockManager can + // safely access (under internal locks) the LockRequest data that the locker cannot. + BSONObjBuilder builder; + auto lockToClientMap = LockManager::getLockToClientMap(getGlobalServiceContext()); + getGlobalLockManager()->getLockInfoBSON(lockToClientMap, &builder); + auto lockInfo = builder.done(); + LOGV2_ERROR(5736000, "Operation ending while holding locks.", "LockInfo"_attr = lockInfo); +} + // // CondVarLockGrantNotification @@ -311,7 +326,12 @@ LockerImpl::~LockerImpl() { // to delete with unaccounted locks anyways. invariant(!inAWriteUnitOfWork()); invariant(_numResourcesToUnlockAtEndUnitOfWork == 0); + + if (!_requests.empty()) { + _dumpLockerAndLockManagerRequests(); + } invariant(_requests.empty()); + invariant(_modeForTicket == MODE_NONE); // Reset the locking statistics so the object can be reused diff --git a/src/mongo/db/concurrency/lock_state.h b/src/mongo/db/concurrency/lock_state.h index d29f7817079..54a492ec99f 100644 --- a/src/mongo/db/concurrency/lock_state.h +++ b/src/mongo/db/concurrency/lock_state.h @@ -317,6 +317,11 @@ private: void _setWaitingResource(ResourceId resId); + /** + * Calls dump() on this locker instance and the lock manager. + */ + void _dumpLockerAndLockManagerRequests(); + // Used to disambiguate different lockers const LockerId _id; diff --git a/src/mongo/db/concurrency/lock_state_test.cpp b/src/mongo/db/concurrency/lock_state_test.cpp index 8a8db4217f5..b7dac6c19a8 100644 --- a/src/mongo/db/concurrency/lock_state_test.cpp +++ b/src/mongo/db/concurrency/lock_state_test.cpp @@ -41,6 +41,7 @@ #include "mongo/db/service_context_test_fixture.h" #include "mongo/transport/session.h" #include "mongo/transport/transport_layer_mock.h" +#include "mongo/unittest/death_test.h" #include "mongo/unittest/unittest.h" #include "mongo/util/fail_point.h" #include "mongo/util/timer.h" @@ -1216,4 +1217,22 @@ TEST_F(LockerImplTest, ConvertLockPendingUnlockAndUnlock) { locker.unlockGlobal(); } +// This test exercises the lock dumping code in ~LockerImpl in case locks are held on destruction. +DEATH_TEST_F(LockerImplTest, + LocksHeldOnDestructionCausesALocksDump, + "Operation ending while holding locks.") { + auto opCtx = makeOperationContext(); + + const ResourceId resId(RESOURCE_COLLECTION, "TestDB.collection"_sd); + + LockerImpl locker; + locker.lockGlobal(opCtx.get(), MODE_IX); + locker.lock(resId, MODE_X); + + ASSERT(locker.isLockHeldForMode(resId, MODE_X)); + ASSERT(locker.isLockHeldForMode(resId, MODE_S)); + + // 'locker' destructor should invariant because locks are still held. +} + } // namespace mongo |