diff options
author | Geert Bosch <geert@mongodb.com> | 2015-09-15 18:09:27 -0400 |
---|---|---|
committer | Kaloian Manassiev <kaloian.manassiev@mongodb.com> | 2015-09-23 09:25:59 -0400 |
commit | 532ae7b3b973ecbd1065a68ce5e9baf4e9d90930 (patch) | |
tree | eea92f257a0ee18a3dfaf7f1d02bc9948c31e424 /src/mongo | |
parent | e09718c973fe08b967cebbe549d1a0e7a8aa8c3d (diff) | |
download | mongo-532ae7b3b973ecbd1065a68ce5e9baf4e9d90930.tar.gz |
SERVER-20429: Correctly unblock requests after canceled lock attempt
Conflicts:
src/mongo/db/concurrency/lock_state_test.cpp
Diffstat (limited to 'src/mongo')
-rw-r--r-- | src/mongo/db/concurrency/lock_manager.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/concurrency/lock_state_test.cpp | 48 |
2 files changed, 50 insertions, 0 deletions
diff --git a/src/mongo/db/concurrency/lock_manager.cpp b/src/mongo/db/concurrency/lock_manager.cpp index 83a88c9bfa9..9c63b2f0603 100644 --- a/src/mongo/db/concurrency/lock_manager.cpp +++ b/src/mongo/db/concurrency/lock_manager.cpp @@ -610,6 +610,8 @@ bool LockManager::unlock(LockRequest* request) { lock->conflictList.remove(request); lock->decConflictModeCount(request->mode); + + _onLockModeChanged(lock, true); } else if (request->status == LockRequest::STATUS_CONVERTING) { // This cancels a pending convert request invariant(request->recursiveCount > 0); diff --git a/src/mongo/db/concurrency/lock_state_test.cpp b/src/mongo/db/concurrency/lock_state_test.cpp index 96bf5004e4c..69621c8636b 100644 --- a/src/mongo/db/concurrency/lock_state_test.cpp +++ b/src/mongo/db/concurrency/lock_state_test.cpp @@ -31,6 +31,7 @@ #include "mongo/platform/basic.h" #include <boost/shared_ptr.hpp> +#include <string> #include <vector> #include "mongo/db/concurrency/lock_manager_test_help.h" @@ -117,6 +118,7 @@ TEST(LockerImpl, ConflictUpgradeWithTimeout) { locker2.unlockAll(); } + TEST(LockerImpl, ReadTransaction) { DefaultLockerImpl locker; @@ -253,6 +255,52 @@ TEST(LockerImpl, MMAPV1Locker) { ASSERT(locker.unlockAll()); } +TEST(LockerImpl, CanceledDeadlockUnblocks) { + const ResourceId db1(RESOURCE_DATABASE, std::string("db1")); + const ResourceId db2(RESOURCE_DATABASE, std::string("db2")); + + DefaultLockerImpl locker1; + DefaultLockerImpl locker2; + DefaultLockerImpl locker3; + + ASSERT(LOCK_OK == locker1.lockGlobal(MODE_IX)); + ASSERT(LOCK_OK == locker1.lock(db1, MODE_S)); + + ASSERT(LOCK_OK == locker2.lockGlobal(MODE_IX)); + ASSERT(LOCK_OK == locker2.lock(db2, MODE_X)); + + // Set up locker1 and locker2 for deadlock + ASSERT(LOCK_WAITING == locker1.lockBegin(db2, MODE_X)); + ASSERT(LOCK_WAITING == locker2.lockBegin(db1, MODE_X)); + + // Locker3 blocks behind locker 2 + ASSERT(LOCK_OK == locker3.lockGlobal(MODE_IX)); + ASSERT(LOCK_WAITING == locker3.lockBegin(db1, MODE_S)); + + // Detect deadlock, canceling our request + ASSERT(LOCK_DEADLOCK == locker2.lockComplete(db1, MODE_X, 1, /*checkDeadlock*/ true)); + + // Now locker3 must be able to complete its request + ASSERT(LOCK_OK == locker3.lockComplete(db1, MODE_S, 1, /*checkDeadlock*/ false)); + + // Locker1 still can't complete its request + ASSERT(LOCK_TIMEOUT == locker1.lockComplete(db2, MODE_X, 1, false)); + + // Check ownership for db1 + ASSERT(locker1.getLockMode(db1) == MODE_S); + ASSERT(locker2.getLockMode(db1) == MODE_NONE); + ASSERT(locker3.getLockMode(db1) == MODE_S); + + // Check ownership for db2 + ASSERT(locker1.getLockMode(db2) == MODE_NONE); + ASSERT(locker2.getLockMode(db2) == MODE_X); + ASSERT(locker3.getLockMode(db2) == MODE_NONE); + + ASSERT(locker1.unlockAll()); + ASSERT(locker2.unlockAll()); + ASSERT(locker3.unlockAll()); +} + // These two tests exercise single-threaded performance of uncontended lock acquisition. It // is not practical to run them on debug builds. |