summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorGeert Bosch <geert@mongodb.com>2015-09-15 18:09:27 -0400
committerKaloian Manassiev <kaloian.manassiev@mongodb.com>2015-09-23 09:25:59 -0400
commit532ae7b3b973ecbd1065a68ce5e9baf4e9d90930 (patch)
treeeea92f257a0ee18a3dfaf7f1d02bc9948c31e424 /src/mongo
parente09718c973fe08b967cebbe549d1a0e7a8aa8c3d (diff)
downloadmongo-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.cpp2
-rw-r--r--src/mongo/db/concurrency/lock_state_test.cpp48
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.