diff options
Diffstat (limited to 'src/mongo/db/concurrency/lock_manager_test.cpp')
-rw-r--r-- | src/mongo/db/concurrency/lock_manager_test.cpp | 1289 |
1 files changed, 643 insertions, 646 deletions
diff --git a/src/mongo/db/concurrency/lock_manager_test.cpp b/src/mongo/db/concurrency/lock_manager_test.cpp index 50fc9826a9b..ce722b6f572 100644 --- a/src/mongo/db/concurrency/lock_manager_test.cpp +++ b/src/mongo/db/concurrency/lock_manager_test.cpp @@ -31,792 +31,789 @@ namespace mongo { - TEST(ResourceId, Semantics) { - ResourceId resIdDb(RESOURCE_DATABASE, 324334234); - ASSERT(resIdDb.getType() == RESOURCE_DATABASE); - ASSERT(resIdDb.getHashId() == 324334234); - - ResourceId resIdColl(RESOURCE_COLLECTION, std::string("TestDB.collection")); - ASSERT(resIdColl.getType() == RESOURCE_COLLECTION); - - // Comparison functions - - // Make sure the operator < is defined. - ASSERT(resIdDb < resIdColl || resIdColl < resIdDb); - - ResourceId resId(RESOURCE_DATABASE, 324334234); - ASSERT_EQUALS(resIdDb, resId); - - // Assignment functions - resId = resIdColl; - ASSERT_EQUALS(resId, resIdColl); - } - - TEST(ResourceId, Constructors) { - ResourceId resIdString(RESOURCE_COLLECTION, std::string("TestDB.collection")); - ResourceId resIdStringData(RESOURCE_COLLECTION, StringData("TestDB.collection")); - - ASSERT_EQUALS(resIdString, resIdStringData); - } - - TEST(ResourceId, Masking) { - const ResourceType maxRes = static_cast<ResourceType>(ResourceTypesCount - 1); - const uint64_t maxHash = (1ULL<<61) - 1; // Only 61 bits usable for hash - ResourceType resources[3] = { maxRes, RESOURCE_GLOBAL, RESOURCE_METADATA }; - uint64_t hashes[3] = {maxHash, maxHash / 3, maxHash / 3 * 2}; - - // The test below verifies that types/hashes are stored/retrieved unchanged - for (int h = 0; h < 3; h++) { - for (int r = 0; r < 3; r++) { - ResourceId id(resources[r], hashes[h]); - ASSERT_EQUALS(id.getHashId(), hashes[h]); - ASSERT_EQUALS(id.getType(), resources[r]); - } +TEST(ResourceId, Semantics) { + ResourceId resIdDb(RESOURCE_DATABASE, 324334234); + ASSERT(resIdDb.getType() == RESOURCE_DATABASE); + ASSERT(resIdDb.getHashId() == 324334234); + + ResourceId resIdColl(RESOURCE_COLLECTION, std::string("TestDB.collection")); + ASSERT(resIdColl.getType() == RESOURCE_COLLECTION); + + // Comparison functions + + // Make sure the operator < is defined. + ASSERT(resIdDb < resIdColl || resIdColl < resIdDb); + + ResourceId resId(RESOURCE_DATABASE, 324334234); + ASSERT_EQUALS(resIdDb, resId); + + // Assignment functions + resId = resIdColl; + ASSERT_EQUALS(resId, resIdColl); +} + +TEST(ResourceId, Constructors) { + ResourceId resIdString(RESOURCE_COLLECTION, std::string("TestDB.collection")); + ResourceId resIdStringData(RESOURCE_COLLECTION, StringData("TestDB.collection")); + + ASSERT_EQUALS(resIdString, resIdStringData); +} + +TEST(ResourceId, Masking) { + const ResourceType maxRes = static_cast<ResourceType>(ResourceTypesCount - 1); + const uint64_t maxHash = (1ULL << 61) - 1; // Only 61 bits usable for hash + ResourceType resources[3] = {maxRes, RESOURCE_GLOBAL, RESOURCE_METADATA}; + uint64_t hashes[3] = {maxHash, maxHash / 3, maxHash / 3 * 2}; + + // The test below verifies that types/hashes are stored/retrieved unchanged + for (int h = 0; h < 3; h++) { + for (int r = 0; r < 3; r++) { + ResourceId id(resources[r], hashes[h]); + ASSERT_EQUALS(id.getHashId(), hashes[h]); + ASSERT_EQUALS(id.getType(), resources[r]); } } +} - // - // LockManager - // +// +// LockManager +// - TEST(LockManager, Grant) { - LockManager lockMgr; - const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); +TEST(LockManager, Grant) { + LockManager lockMgr; + const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); - MMAPV1LockerImpl locker; - TrackingLockGrantNotification notify; + MMAPV1LockerImpl locker; + TrackingLockGrantNotification notify; - LockRequest request; - request.initNew(&locker, ¬ify); + LockRequest request; + request.initNew(&locker, ¬ify); - ASSERT(LOCK_OK == lockMgr.lock(resId, &request, MODE_S)); - ASSERT(request.mode == MODE_S); - ASSERT(request.recursiveCount == 1); - ASSERT(notify.numNotifies == 0); + ASSERT(LOCK_OK == lockMgr.lock(resId, &request, MODE_S)); + ASSERT(request.mode == MODE_S); + ASSERT(request.recursiveCount == 1); + ASSERT(notify.numNotifies == 0); - lockMgr.unlock(&request); - ASSERT(request.recursiveCount == 0); - } + lockMgr.unlock(&request); + ASSERT(request.recursiveCount == 0); +} - TEST(LockManager, GrantMultipleNoConflict) { - LockManager lockMgr; - const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); +TEST(LockManager, GrantMultipleNoConflict) { + LockManager lockMgr; + const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); - MMAPV1LockerImpl locker; - TrackingLockGrantNotification notify; + MMAPV1LockerImpl locker; + TrackingLockGrantNotification notify; - LockRequest request[6]; - for (int i = 0; i < 6; i++) { - request[i].initNew(&locker, ¬ify); - ASSERT(LOCK_OK == lockMgr.lock(resId, &request[i], MODE_S)); + LockRequest request[6]; + for (int i = 0; i < 6; i++) { + request[i].initNew(&locker, ¬ify); + ASSERT(LOCK_OK == lockMgr.lock(resId, &request[i], MODE_S)); - ASSERT(request[i].mode == MODE_S); - ASSERT(request[i].recursiveCount == 1); - } - - ASSERT(notify.numNotifies == 0); - - // Free the first - lockMgr.unlock(&request[0]); - - // Free the last - lockMgr.unlock(&request[5]); - - // Free one in the middle - lockMgr.unlock(&request[3]); - - // Free the remaining so the LockMgr does not compain about leaked locks - lockMgr.unlock(&request[1]); - lockMgr.unlock(&request[2]); - lockMgr.unlock(&request[4]); + ASSERT(request[i].mode == MODE_S); + ASSERT(request[i].recursiveCount == 1); } - TEST(LockManager, GrantMultipleFIFOOrder) { - LockManager lockMgr; - const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); + ASSERT(notify.numNotifies == 0); - std::unique_ptr<MMAPV1LockerImpl> locker[6]; - for (int i = 0; i < 6; i++) { - locker[i].reset(new MMAPV1LockerImpl()); - } + // Free the first + lockMgr.unlock(&request[0]); - TrackingLockGrantNotification notify[6]; + // Free the last + lockMgr.unlock(&request[5]); - LockRequest request[6]; - for (int i = 0; i < 6; i++) { - request[i].initNew(locker[i].get(), ¬ify[i]); - lockMgr.lock(resId, &request[i], MODE_X); + // Free one in the middle + lockMgr.unlock(&request[3]); - ASSERT(request[i].mode == MODE_X); - ASSERT(request[i].recursiveCount == 1); - } + // Free the remaining so the LockMgr does not compain about leaked locks + lockMgr.unlock(&request[1]); + lockMgr.unlock(&request[2]); + lockMgr.unlock(&request[4]); +} - // Release the last held lock and ensure the next one, based on time is granted - for (int i = 0; i < 5; i++) { - lockMgr.unlock(&request[i]); - - ASSERT(notify[i + 1].numNotifies == 1); - ASSERT(notify[i + 1].lastResId == resId); - ASSERT(notify[i + 1].lastResult == LOCK_OK); - } +TEST(LockManager, GrantMultipleFIFOOrder) { + LockManager lockMgr; + const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); - // Release the last one - lockMgr.unlock(&request[5]); + std::unique_ptr<MMAPV1LockerImpl> locker[6]; + for (int i = 0; i < 6; i++) { + locker[i].reset(new MMAPV1LockerImpl()); } - TEST(LockManager, GrantRecursive) { - LockManager lockMgr; - const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); + TrackingLockGrantNotification notify[6]; - MMAPV1LockerImpl locker; - LockRequestCombo request(&locker); + LockRequest request[6]; + for (int i = 0; i < 6; i++) { + request[i].initNew(locker[i].get(), ¬ify[i]); + lockMgr.lock(resId, &request[i], MODE_X); - ASSERT(LOCK_OK == lockMgr.lock(resId, &request, MODE_S)); - ASSERT(request.mode == MODE_S); - ASSERT(request.recursiveCount == 1); - ASSERT(request.numNotifies == 0); - - // Acquire again, in the same mode - ASSERT(LOCK_OK == lockMgr.convert(resId, &request, MODE_S)); - ASSERT(request.mode == MODE_S); - ASSERT(request.recursiveCount == 2); - ASSERT(request.numNotifies == 0); - - // Release first acquire - lockMgr.unlock(&request); - ASSERT(request.mode == MODE_S); - ASSERT(request.recursiveCount == 1); - - // Release second acquire - lockMgr.unlock(&request); - ASSERT(request.recursiveCount == 0); + ASSERT(request[i].mode == MODE_X); + ASSERT(request[i].recursiveCount == 1); } - TEST(LockManager, GrantRecursiveCompatibleConvertUp) { - LockManager lockMgr; - const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); - - MMAPV1LockerImpl locker; - LockRequestCombo request(&locker); - - ASSERT(LOCK_OK == lockMgr.lock(resId, &request, MODE_IS)); - ASSERT(request.mode == MODE_IS); - ASSERT(request.recursiveCount == 1); - ASSERT(request.numNotifies == 0); + // Release the last held lock and ensure the next one, based on time is granted + for (int i = 0; i < 5; i++) { + lockMgr.unlock(&request[i]); - // Acquire again, in *compatible*, but stricter mode - ASSERT(LOCK_OK == lockMgr.convert(resId, &request, MODE_S)); - ASSERT(request.mode == MODE_S); - ASSERT(request.recursiveCount == 2); - ASSERT(request.numNotifies == 0); - - // Release the first acquire - lockMgr.unlock(&request); - ASSERT(request.mode == MODE_S); - ASSERT(request.recursiveCount == 1); - - // Release the second acquire - lockMgr.unlock(&request); - ASSERT(request.recursiveCount == 0); + ASSERT(notify[i + 1].numNotifies == 1); + ASSERT(notify[i + 1].lastResId == resId); + ASSERT(notify[i + 1].lastResult == LOCK_OK); } - TEST(LockManager, GrantRecursiveNonCompatibleConvertUp) { - LockManager lockMgr; - const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); - - MMAPV1LockerImpl locker; - LockRequestCombo request(&locker); - - ASSERT(LOCK_OK == lockMgr.lock(resId, &request, MODE_S)); - ASSERT(request.mode == MODE_S); - ASSERT(request.recursiveCount == 1); - ASSERT(request.numNotifies == 0); - - // Acquire again, in *non-compatible*, but stricter mode - ASSERT(LOCK_OK == lockMgr.convert(resId, &request, MODE_X)); - ASSERT(request.mode == MODE_X); - ASSERT(request.recursiveCount == 2); - ASSERT(request.numNotifies == 0); - - // Release first acquire - lockMgr.unlock(&request); - ASSERT(request.mode == MODE_X); - ASSERT(request.recursiveCount == 1); + // Release the last one + lockMgr.unlock(&request[5]); +} + +TEST(LockManager, GrantRecursive) { + LockManager lockMgr; + const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); + + MMAPV1LockerImpl locker; + LockRequestCombo request(&locker); + + ASSERT(LOCK_OK == lockMgr.lock(resId, &request, MODE_S)); + ASSERT(request.mode == MODE_S); + ASSERT(request.recursiveCount == 1); + ASSERT(request.numNotifies == 0); + + // Acquire again, in the same mode + ASSERT(LOCK_OK == lockMgr.convert(resId, &request, MODE_S)); + ASSERT(request.mode == MODE_S); + ASSERT(request.recursiveCount == 2); + ASSERT(request.numNotifies == 0); + + // Release first acquire + lockMgr.unlock(&request); + ASSERT(request.mode == MODE_S); + ASSERT(request.recursiveCount == 1); + + // Release second acquire + lockMgr.unlock(&request); + ASSERT(request.recursiveCount == 0); +} + +TEST(LockManager, GrantRecursiveCompatibleConvertUp) { + LockManager lockMgr; + const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); + + MMAPV1LockerImpl locker; + LockRequestCombo request(&locker); + + ASSERT(LOCK_OK == lockMgr.lock(resId, &request, MODE_IS)); + ASSERT(request.mode == MODE_IS); + ASSERT(request.recursiveCount == 1); + ASSERT(request.numNotifies == 0); + + // Acquire again, in *compatible*, but stricter mode + ASSERT(LOCK_OK == lockMgr.convert(resId, &request, MODE_S)); + ASSERT(request.mode == MODE_S); + ASSERT(request.recursiveCount == 2); + ASSERT(request.numNotifies == 0); + + // Release the first acquire + lockMgr.unlock(&request); + ASSERT(request.mode == MODE_S); + ASSERT(request.recursiveCount == 1); + + // Release the second acquire + lockMgr.unlock(&request); + ASSERT(request.recursiveCount == 0); +} + +TEST(LockManager, GrantRecursiveNonCompatibleConvertUp) { + LockManager lockMgr; + const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); + + MMAPV1LockerImpl locker; + LockRequestCombo request(&locker); + + ASSERT(LOCK_OK == lockMgr.lock(resId, &request, MODE_S)); + ASSERT(request.mode == MODE_S); + ASSERT(request.recursiveCount == 1); + ASSERT(request.numNotifies == 0); + + // Acquire again, in *non-compatible*, but stricter mode + ASSERT(LOCK_OK == lockMgr.convert(resId, &request, MODE_X)); + ASSERT(request.mode == MODE_X); + ASSERT(request.recursiveCount == 2); + ASSERT(request.numNotifies == 0); + + // Release first acquire + lockMgr.unlock(&request); + ASSERT(request.mode == MODE_X); + ASSERT(request.recursiveCount == 1); + + // Release second acquire + lockMgr.unlock(&request); + ASSERT(request.recursiveCount == 0); +} + +TEST(LockManager, GrantRecursiveNonCompatibleConvertDown) { + LockManager lockMgr; + const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); + + MMAPV1LockerImpl locker; + LockRequestCombo request(&locker); + + ASSERT(LOCK_OK == lockMgr.lock(resId, &request, MODE_X)); + ASSERT(request.mode == MODE_X); + ASSERT(request.recursiveCount == 1); + ASSERT(request.numNotifies == 0); + + // Acquire again, in *non-compatible*, but less strict mode + ASSERT(LOCK_OK == lockMgr.convert(resId, &request, MODE_S)); + ASSERT(request.mode == MODE_X); + ASSERT(request.recursiveCount == 2); + ASSERT(request.numNotifies == 0); + + // Release first acquire + lockMgr.unlock(&request); + ASSERT(request.mode == MODE_X); + ASSERT(request.recursiveCount == 1); + + // Release second acquire + lockMgr.unlock(&request); + ASSERT(request.recursiveCount == 0); +} + +TEST(LockManager, Conflict) { + LockManager lockMgr; + const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); + + MMAPV1LockerImpl locker1; + MMAPV1LockerImpl locker2; + + LockRequestCombo request1(&locker1); + LockRequestCombo request2(&locker2); + + // First request granted right away + ASSERT(LOCK_OK == lockMgr.lock(resId, &request1, MODE_S)); + ASSERT(request1.recursiveCount == 1); + ASSERT(request1.numNotifies == 0); + + // Second request must block + ASSERT(LOCK_WAITING == lockMgr.lock(resId, &request2, MODE_X)); + ASSERT(request2.mode == MODE_X); + ASSERT(request2.recursiveCount == 1); + ASSERT(request2.numNotifies == 0); + + // Release first request + lockMgr.unlock(&request1); + ASSERT(request1.recursiveCount == 0); + ASSERT(request1.numNotifies == 0); + + ASSERT(request2.mode == MODE_X); + ASSERT(request2.recursiveCount == 1); + ASSERT(request2.numNotifies == 1); + ASSERT(request2.lastResult == LOCK_OK); + + // Release second acquire + lockMgr.unlock(&request2); + ASSERT(request2.recursiveCount == 0); + + ASSERT(request1.numNotifies == 0); + ASSERT(request2.numNotifies == 1); +} + +TEST(LockManager, MultipleConflict) { + LockManager lockMgr; + const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); + + MMAPV1LockerImpl locker; + TrackingLockGrantNotification notify; + + LockRequest request[6]; + for (int i = 0; i < 6; i++) { + request[i].initNew(&locker, ¬ify); + + if (i == 0) { + ASSERT(LOCK_OK == lockMgr.lock(resId, &request[i], MODE_X)); + } else { + ASSERT(LOCK_WAITING == lockMgr.lock(resId, &request[i], MODE_X)); + } - // Release second acquire - lockMgr.unlock(&request); - ASSERT(request.recursiveCount == 0); + ASSERT(request[i].mode == MODE_X); + ASSERT(request[i].recursiveCount == 1); } - TEST(LockManager, GrantRecursiveNonCompatibleConvertDown) { - LockManager lockMgr; - const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); - - MMAPV1LockerImpl locker; - LockRequestCombo request(&locker); - - ASSERT(LOCK_OK == lockMgr.lock(resId, &request, MODE_X)); - ASSERT(request.mode == MODE_X); - ASSERT(request.recursiveCount == 1); - ASSERT(request.numNotifies == 0); - - // Acquire again, in *non-compatible*, but less strict mode - ASSERT(LOCK_OK == lockMgr.convert(resId, &request, MODE_S)); - ASSERT(request.mode == MODE_X); - ASSERT(request.recursiveCount == 2); - ASSERT(request.numNotifies == 0); + ASSERT(notify.numNotifies == 0); - // Release first acquire - lockMgr.unlock(&request); - ASSERT(request.mode == MODE_X); - ASSERT(request.recursiveCount == 1); + // Free them one by one and make sure they get granted in the correct order + for (int i = 0; i < 6; i++) { + lockMgr.unlock(&request[i]); - // Release second acquire - lockMgr.unlock(&request); - ASSERT(request.recursiveCount == 0); + if (i < 5) { + ASSERT(notify.numNotifies == i + 1); + } } +} - TEST(LockManager, Conflict) { - LockManager lockMgr; - const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); - - MMAPV1LockerImpl locker1; - MMAPV1LockerImpl locker2; - - LockRequestCombo request1(&locker1); - LockRequestCombo request2(&locker2); +TEST(LockManager, ConflictCancelWaiting) { + LockManager lockMgr; + const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); - // First request granted right away - ASSERT(LOCK_OK == lockMgr.lock(resId, &request1, MODE_S)); - ASSERT(request1.recursiveCount == 1); - ASSERT(request1.numNotifies == 0); + MMAPV1LockerImpl locker1; + TrackingLockGrantNotification notify1; - // Second request must block - ASSERT(LOCK_WAITING == lockMgr.lock(resId, &request2, MODE_X)); - ASSERT(request2.mode == MODE_X); - ASSERT(request2.recursiveCount == 1); - ASSERT(request2.numNotifies == 0); + MMAPV1LockerImpl locker2; + TrackingLockGrantNotification notify2; - // Release first request - lockMgr.unlock(&request1); - ASSERT(request1.recursiveCount == 0); - ASSERT(request1.numNotifies == 0); + LockRequest request1; + request1.initNew(&locker1, ¬ify1); - ASSERT(request2.mode == MODE_X); - ASSERT(request2.recursiveCount == 1); - ASSERT(request2.numNotifies == 1); - ASSERT(request2.lastResult == LOCK_OK); + LockRequest request2; + request2.initNew(&locker2, ¬ify2); - // Release second acquire - lockMgr.unlock(&request2); - ASSERT(request2.recursiveCount == 0); - - ASSERT(request1.numNotifies == 0); - ASSERT(request2.numNotifies == 1); - } + // First request granted right away + ASSERT(LOCK_OK == lockMgr.lock(resId, &request1, MODE_S)); + ASSERT(notify1.numNotifies == 0); - TEST(LockManager, MultipleConflict) { - LockManager lockMgr; - const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); + ASSERT(LOCK_WAITING == lockMgr.lock(resId, &request2, MODE_X)); - MMAPV1LockerImpl locker; - TrackingLockGrantNotification notify; + // Release second request (which is still in the WAITING mode) + lockMgr.unlock(&request2); + ASSERT(notify2.numNotifies == 0); - LockRequest request[6]; - for (int i = 0; i < 6; i++) { - request[i].initNew(&locker, ¬ify); + ASSERT(request1.mode == MODE_S); + ASSERT(request1.recursiveCount == 1); - if (i == 0) { - ASSERT(LOCK_OK == lockMgr.lock(resId, &request[i], MODE_X)); - } - else { - ASSERT(LOCK_WAITING == lockMgr.lock(resId, &request[i], MODE_X)); - } + // Release second acquire + lockMgr.unlock(&request1); +} - ASSERT(request[i].mode == MODE_X); - ASSERT(request[i].recursiveCount == 1); - } +TEST(LockManager, ConflictCancelMultipleWaiting) { + LockManager lockMgr; + const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); - ASSERT(notify.numNotifies == 0); + MMAPV1LockerImpl locker; + TrackingLockGrantNotification notify; - // Free them one by one and make sure they get granted in the correct order - for (int i = 0; i < 6; i++) { - lockMgr.unlock(&request[i]); + LockRequest request[6]; + for (int i = 0; i < 6; i++) { + request[i].initNew(&locker, ¬ify); + lockMgr.lock(resId, &request[i], MODE_X); - if (i < 5) { - ASSERT(notify.numNotifies == i + 1); - } - } + ASSERT(request[i].mode == MODE_X); + ASSERT(request[i].recursiveCount == 1); } - TEST(LockManager, ConflictCancelWaiting) { - LockManager lockMgr; - const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); - - MMAPV1LockerImpl locker1; - TrackingLockGrantNotification notify1; + ASSERT(notify.numNotifies == 0); - MMAPV1LockerImpl locker2; - TrackingLockGrantNotification notify2; + // Free the second (waiting) + lockMgr.unlock(&request[1]); - LockRequest request1; - request1.initNew(&locker1, ¬ify1); + // Free the last + lockMgr.unlock(&request[5]); - LockRequest request2; - request2.initNew(&locker2, ¬ify2); + // Free one in the middle + lockMgr.unlock(&request[3]); - // First request granted right away - ASSERT(LOCK_OK == lockMgr.lock(resId, &request1, MODE_S)); - ASSERT(notify1.numNotifies == 0); + // Free the remaining so the LockMgr does not compain about leaked locks + lockMgr.unlock(&request[2]); + lockMgr.unlock(&request[4]); + lockMgr.unlock(&request[0]); +} - ASSERT(LOCK_WAITING == lockMgr.lock(resId, &request2, MODE_X)); +TEST(LockManager, ConflictCancelWaitingConversion) { + LockManager lockMgr; + const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); - // Release second request (which is still in the WAITING mode) - lockMgr.unlock(&request2); - ASSERT(notify2.numNotifies == 0); + MMAPV1LockerImpl locker1; + MMAPV1LockerImpl locker2; - ASSERT(request1.mode == MODE_S); - ASSERT(request1.recursiveCount == 1); + LockRequestCombo request1(&locker1); + LockRequestCombo request2(&locker2); - // Release second acquire - lockMgr.unlock(&request1); - } + // First request granted right away + ASSERT(LOCK_OK == lockMgr.lock(resId, &request1, MODE_S)); + ASSERT(request1.numNotifies == 0); - TEST(LockManager, ConflictCancelMultipleWaiting) { - LockManager lockMgr; - const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); + // Second request is granted right away + ASSERT(LOCK_OK == lockMgr.lock(resId, &request2, MODE_S)); + ASSERT(request2.numNotifies == 0); - MMAPV1LockerImpl locker; - TrackingLockGrantNotification notify; + // Convert second request to conflicting + ASSERT(LOCK_WAITING == lockMgr.convert(resId, &request2, MODE_X)); + ASSERT(request2.mode == MODE_S); + ASSERT(request2.convertMode == MODE_X); + ASSERT(request2.numNotifies == 0); - LockRequest request[6]; - for (int i = 0; i < 6; i++) { - request[i].initNew(&locker, ¬ify); - lockMgr.lock(resId, &request[i], MODE_X); + // Cancel the conflicting upgrade + lockMgr.unlock(&request2); + ASSERT(request2.mode == MODE_S); + ASSERT(request2.convertMode == MODE_NONE); + ASSERT(request2.numNotifies == 0); - ASSERT(request[i].mode == MODE_X); - ASSERT(request[i].recursiveCount == 1); - } + // Free the remaining locks so the LockManager destructor does not complain + lockMgr.unlock(&request1); + lockMgr.unlock(&request2); +} - ASSERT(notify.numNotifies == 0); +TEST(LockManager, ConflictingConversion) { + LockManager lockMgr; + const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); - // Free the second (waiting) - lockMgr.unlock(&request[1]); + MMAPV1LockerImpl locker1; + MMAPV1LockerImpl locker2; - // Free the last - lockMgr.unlock(&request[5]); + LockRequestCombo request1(&locker1); + LockRequestCombo request2(&locker2); - // Free one in the middle - lockMgr.unlock(&request[3]); + // The S requests are granted right away + ASSERT(LOCK_OK == lockMgr.lock(resId, &request1, MODE_S)); + ASSERT(request1.numNotifies == 0); - // Free the remaining so the LockMgr does not compain about leaked locks - lockMgr.unlock(&request[2]); - lockMgr.unlock(&request[4]); - lockMgr.unlock(&request[0]); - } + ASSERT(LOCK_OK == lockMgr.lock(resId, &request2, MODE_S)); + ASSERT(request2.numNotifies == 0); - TEST(LockManager, ConflictCancelWaitingConversion) { - LockManager lockMgr; - const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); + // Convert first request to conflicting + ASSERT(LOCK_WAITING == lockMgr.convert(resId, &request1, MODE_X)); + ASSERT(request1.numNotifies == 0); - MMAPV1LockerImpl locker1; - MMAPV1LockerImpl locker2; + // Free the second lock and make sure the first is granted + lockMgr.unlock(&request2); + ASSERT(request1.mode == MODE_X); + ASSERT(request1.numNotifies == 1); + ASSERT(request2.numNotifies == 0); - LockRequestCombo request1(&locker1); - LockRequestCombo request2(&locker2); + // Frees the first reference, mode remains X + lockMgr.unlock(&request1); + ASSERT(request1.mode == MODE_X); + ASSERT(request1.recursiveCount == 1); - // First request granted right away - ASSERT(LOCK_OK == lockMgr.lock(resId, &request1, MODE_S)); - ASSERT(request1.numNotifies == 0); + lockMgr.unlock(&request1); +} - // Second request is granted right away - ASSERT(LOCK_OK == lockMgr.lock(resId, &request2, MODE_S)); - ASSERT(request2.numNotifies == 0); +TEST(LockManager, ConflictingConversionInTheMiddle) { + LockManager lockMgr; + const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); - // Convert second request to conflicting - ASSERT(LOCK_WAITING == lockMgr.convert(resId, &request2, MODE_X)); - ASSERT(request2.mode == MODE_S); - ASSERT(request2.convertMode == MODE_X); - ASSERT(request2.numNotifies == 0); + MMAPV1LockerImpl locker; + TrackingLockGrantNotification notify; - // Cancel the conflicting upgrade - lockMgr.unlock(&request2); - ASSERT(request2.mode == MODE_S); - ASSERT(request2.convertMode == MODE_NONE); - ASSERT(request2.numNotifies == 0); - - // Free the remaining locks so the LockManager destructor does not complain - lockMgr.unlock(&request1); - lockMgr.unlock(&request2); + LockRequest request[3]; + for (int i = 0; i < 3; i++) { + request[i].initNew(&locker, ¬ify); + lockMgr.lock(resId, &request[i], MODE_S); } - TEST(LockManager, ConflictingConversion) { - LockManager lockMgr; - const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); + // Upgrade the one in the middle (not the first one) + ASSERT(LOCK_WAITING == lockMgr.convert(resId, &request[1], MODE_X)); - MMAPV1LockerImpl locker1; - MMAPV1LockerImpl locker2; + ASSERT(notify.numNotifies == 0); - LockRequestCombo request1(&locker1); - LockRequestCombo request2(&locker2); + // Release the two shared modes + lockMgr.unlock(&request[0]); + ASSERT(notify.numNotifies == 0); - // The S requests are granted right away - ASSERT(LOCK_OK == lockMgr.lock(resId, &request1, MODE_S)); - ASSERT(request1.numNotifies == 0); + lockMgr.unlock(&request[2]); + ASSERT(notify.numNotifies == 1); - ASSERT(LOCK_OK == lockMgr.lock(resId, &request2, MODE_S)); - ASSERT(request2.numNotifies == 0); + ASSERT(request[1].mode == MODE_X); - // Convert first request to conflicting - ASSERT(LOCK_WAITING == lockMgr.convert(resId, &request1, MODE_X)); - ASSERT(request1.numNotifies == 0); + // Request 1 should be unlocked twice + lockMgr.unlock(&request[1]); + lockMgr.unlock(&request[1]); +} - // Free the second lock and make sure the first is granted - lockMgr.unlock(&request2); - ASSERT(request1.mode == MODE_X); - ASSERT(request1.numNotifies == 1); - ASSERT(request2.numNotifies == 0); +TEST(LockManager, ConvertUpgrade) { + LockManager lockMgr; + const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); - // Frees the first reference, mode remains X - lockMgr.unlock(&request1); - ASSERT(request1.mode == MODE_X); - ASSERT(request1.recursiveCount == 1); + MMAPV1LockerImpl locker1; + LockRequestCombo request1(&locker1); + ASSERT(LOCK_OK == lockMgr.lock(resId, &request1, MODE_S)); - lockMgr.unlock(&request1); - } + MMAPV1LockerImpl locker2; + LockRequestCombo request2(&locker2); + ASSERT(LOCK_OK == lockMgr.lock(resId, &request2, MODE_S)); - TEST(LockManager, ConflictingConversionInTheMiddle) { - LockManager lockMgr; - const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); + // Upgrade the S lock to X + ASSERT(LOCK_WAITING == lockMgr.convert(resId, &request1, MODE_X)); - MMAPV1LockerImpl locker; - TrackingLockGrantNotification notify; + ASSERT(!lockMgr.unlock(&request1)); + ASSERT(lockMgr.unlock(&request1)); - LockRequest request[3]; - for (int i = 0; i < 3; i++) { - request[i].initNew(&locker, ¬ify); - lockMgr.lock(resId, &request[i], MODE_S); - } + ASSERT(lockMgr.unlock(&request2)); +} - // Upgrade the one in the middle (not the first one) - ASSERT(LOCK_WAITING == lockMgr.convert(resId, &request[1], MODE_X)); +TEST(LockManager, Downgrade) { + LockManager lockMgr; + const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); - ASSERT(notify.numNotifies == 0); + MMAPV1LockerImpl locker1; + LockRequestCombo request1(&locker1); + ASSERT(LOCK_OK == lockMgr.lock(resId, &request1, MODE_X)); - // Release the two shared modes - lockMgr.unlock(&request[0]); - ASSERT(notify.numNotifies == 0); + MMAPV1LockerImpl locker2; + LockRequestCombo request2(&locker2); + ASSERT(LOCK_WAITING == lockMgr.lock(resId, &request2, MODE_S)); - lockMgr.unlock(&request[2]); - ASSERT(notify.numNotifies == 1); + // Downgrade the X request to S + lockMgr.downgrade(&request1, MODE_S); - ASSERT(request[1].mode == MODE_X); + ASSERT(request2.numNotifies == 1); + ASSERT(request2.lastResult == LOCK_OK); + ASSERT(request2.recursiveCount == 1); - // Request 1 should be unlocked twice - lockMgr.unlock(&request[1]); - lockMgr.unlock(&request[1]); - } + ASSERT(lockMgr.unlock(&request1)); + ASSERT(lockMgr.unlock(&request2)); +} - TEST(LockManager, ConvertUpgrade) { - LockManager lockMgr; - const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); - MMAPV1LockerImpl locker1; - LockRequestCombo request1(&locker1); - ASSERT(LOCK_OK == lockMgr.lock(resId, &request1, MODE_S)); +// Lock conflict matrix tests +static void checkConflict(LockMode existingMode, LockMode newMode, bool hasConflict) { + LockManager lockMgr; + const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); - MMAPV1LockerImpl locker2; - LockRequestCombo request2(&locker2); - ASSERT(LOCK_OK == lockMgr.lock(resId, &request2, MODE_S)); + MMAPV1LockerImpl lockerExisting; + TrackingLockGrantNotification notifyExisting; + LockRequest requestExisting; + requestExisting.initNew(&lockerExisting, ¬ifyExisting); - // Upgrade the S lock to X - ASSERT(LOCK_WAITING == lockMgr.convert(resId, &request1, MODE_X)); + ASSERT(LOCK_OK == lockMgr.lock(resId, &requestExisting, existingMode)); - ASSERT(!lockMgr.unlock(&request1)); - ASSERT(lockMgr.unlock(&request1)); + MMAPV1LockerImpl lockerNew; + TrackingLockGrantNotification notifyNew; + LockRequest requestNew; + requestNew.initNew(&lockerNew, ¬ifyNew); - ASSERT(lockMgr.unlock(&request2)); + LockResult result = lockMgr.lock(resId, &requestNew, newMode); + if (hasConflict) { + ASSERT_EQUALS(LOCK_WAITING, result); + } else { + ASSERT_EQUALS(LOCK_OK, result); } - TEST(LockManager, Downgrade) { - LockManager lockMgr; - const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); + lockMgr.unlock(&requestNew); + lockMgr.unlock(&requestExisting); +} - MMAPV1LockerImpl locker1; - LockRequestCombo request1(&locker1); - ASSERT(LOCK_OK == lockMgr.lock(resId, &request1, MODE_X)); +TEST(LockManager, ValidateConflictMatrix) { + checkConflict(MODE_IS, MODE_IS, false); + checkConflict(MODE_IS, MODE_IX, false); + checkConflict(MODE_IS, MODE_S, false); + checkConflict(MODE_IS, MODE_X, true); - MMAPV1LockerImpl locker2; - LockRequestCombo request2(&locker2); - ASSERT(LOCK_WAITING == lockMgr.lock(resId, &request2, MODE_S)); + checkConflict(MODE_IX, MODE_IS, false); + checkConflict(MODE_IX, MODE_IX, false); + checkConflict(MODE_IX, MODE_S, true); + checkConflict(MODE_IX, MODE_X, true); - // Downgrade the X request to S - lockMgr.downgrade(&request1, MODE_S); + checkConflict(MODE_S, MODE_IS, false); + checkConflict(MODE_S, MODE_IX, true); + checkConflict(MODE_S, MODE_S, false); + checkConflict(MODE_S, MODE_X, true); - ASSERT(request2.numNotifies == 1); - ASSERT(request2.lastResult == LOCK_OK); - ASSERT(request2.recursiveCount == 1); + checkConflict(MODE_X, MODE_IS, true); + checkConflict(MODE_X, MODE_IX, true); + checkConflict(MODE_X, MODE_S, true); + checkConflict(MODE_X, MODE_X, true); +} - ASSERT(lockMgr.unlock(&request1)); - ASSERT(lockMgr.unlock(&request2)); - } +TEST(LockManager, EnqueueAtFront) { + LockManager lockMgr; + const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); + MMAPV1LockerImpl lockerX; + LockRequestCombo requestX(&lockerX); + ASSERT(LOCK_OK == lockMgr.lock(resId, &requestX, MODE_X)); - // Lock conflict matrix tests - static void checkConflict(LockMode existingMode, LockMode newMode, bool hasConflict) { - LockManager lockMgr; - const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); + // The subsequent request will block + MMAPV1LockerImpl lockerLow; + LockRequestCombo requestLow(&lockerLow); - MMAPV1LockerImpl lockerExisting; - TrackingLockGrantNotification notifyExisting; - LockRequest requestExisting; - requestExisting.initNew(&lockerExisting, ¬ifyExisting); + ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestLow, MODE_X)); - ASSERT(LOCK_OK == lockMgr.lock(resId, &requestExisting, existingMode)); + // This is a "queue jumping request", which will go before locker 2 above + MMAPV1LockerImpl lockerHi; + LockRequestCombo requestHi(&lockerHi); + requestHi.enqueueAtFront = true; - MMAPV1LockerImpl lockerNew; - TrackingLockGrantNotification notifyNew; - LockRequest requestNew; - requestNew.initNew(&lockerNew, ¬ifyNew); + ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestHi, MODE_X)); - LockResult result = lockMgr.lock(resId, &requestNew, newMode); - if (hasConflict) { - ASSERT_EQUALS(LOCK_WAITING, result); - } - else { - ASSERT_EQUALS(LOCK_OK, result); - } - - lockMgr.unlock(&requestNew); - lockMgr.unlock(&requestExisting); - } - - TEST(LockManager, ValidateConflictMatrix) { - checkConflict(MODE_IS, MODE_IS, false); - checkConflict(MODE_IS, MODE_IX, false); - checkConflict(MODE_IS, MODE_S, false); - checkConflict(MODE_IS, MODE_X, true); - - checkConflict(MODE_IX, MODE_IS, false); - checkConflict(MODE_IX, MODE_IX, false); - checkConflict(MODE_IX, MODE_S, true); - checkConflict(MODE_IX, MODE_X, true); - - checkConflict(MODE_S, MODE_IS, false); - checkConflict(MODE_S, MODE_IX, true); - checkConflict(MODE_S, MODE_S, false); - checkConflict(MODE_S, MODE_X, true); - - checkConflict(MODE_X, MODE_IS, true); - checkConflict(MODE_X, MODE_IX, true); - checkConflict(MODE_X, MODE_S, true); - checkConflict(MODE_X, MODE_X, true); - } + // Once the X request is gone, lockerHi should be granted, because it's queue jumping + ASSERT(lockMgr.unlock(&requestX)); - TEST(LockManager, EnqueueAtFront) { - LockManager lockMgr; - const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); + ASSERT(requestHi.lastResId == resId); + ASSERT(requestHi.lastResult == LOCK_OK); - MMAPV1LockerImpl lockerX; - LockRequestCombo requestX(&lockerX); + // Finally lockerLow should be granted + ASSERT(lockMgr.unlock(&requestHi)); - ASSERT(LOCK_OK == lockMgr.lock(resId, &requestX, MODE_X)); + ASSERT(requestLow.lastResId == resId); + ASSERT(requestLow.lastResult == LOCK_OK); - // The subsequent request will block - MMAPV1LockerImpl lockerLow; - LockRequestCombo requestLow(&lockerLow); + // This avoids the lock manager asserting on leaked locks + ASSERT(lockMgr.unlock(&requestLow)); +} - ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestLow, MODE_X)); +TEST(LockManager, CompatibleFirstImmediateGrant) { + LockManager lockMgr; + const ResourceId resId(RESOURCE_GLOBAL, 0); - // This is a "queue jumping request", which will go before locker 2 above - MMAPV1LockerImpl lockerHi; - LockRequestCombo requestHi(&lockerHi); - requestHi.enqueueAtFront = true; + MMAPV1LockerImpl locker1; + LockRequestCombo request1(&locker1); - ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestHi, MODE_X)); + MMAPV1LockerImpl locker2; + LockRequestCombo request2(&locker2); + request2.compatibleFirst = true; - // Once the X request is gone, lockerHi should be granted, because it's queue jumping - ASSERT(lockMgr.unlock(&requestX)); + MMAPV1LockerImpl locker3; + LockRequestCombo request3(&locker3); - ASSERT(requestHi.lastResId == resId); - ASSERT(requestHi.lastResult == LOCK_OK); + // Lock all in IS mode + ASSERT(LOCK_OK == lockMgr.lock(resId, &request1, MODE_IS)); + ASSERT(LOCK_OK == lockMgr.lock(resId, &request2, MODE_IS)); + ASSERT(LOCK_OK == lockMgr.lock(resId, &request3, MODE_IS)); - // Finally lockerLow should be granted - ASSERT(lockMgr.unlock(&requestHi)); + // Now an exclusive mode comes, which would block + MMAPV1LockerImpl lockerX; + LockRequestCombo requestX(&lockerX); - ASSERT(requestLow.lastResId == resId); - ASSERT(requestLow.lastResult == LOCK_OK); + ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestX, MODE_X)); - // This avoids the lock manager asserting on leaked locks - ASSERT(lockMgr.unlock(&requestLow)); + // If an S comes, it should be granted, because of request2 + { + MMAPV1LockerImpl lockerS; + LockRequestCombo requestS(&lockerS); + ASSERT(LOCK_OK == lockMgr.lock(resId, &requestS, MODE_S)); + ASSERT(lockMgr.unlock(&requestS)); } - TEST(LockManager, CompatibleFirstImmediateGrant) { - LockManager lockMgr; - const ResourceId resId(RESOURCE_GLOBAL, 0); - - MMAPV1LockerImpl locker1; - LockRequestCombo request1(&locker1); - - MMAPV1LockerImpl locker2; - LockRequestCombo request2(&locker2); - request2.compatibleFirst = true; - - MMAPV1LockerImpl locker3; - LockRequestCombo request3(&locker3); - - // Lock all in IS mode - ASSERT(LOCK_OK == lockMgr.lock(resId, &request1, MODE_IS)); - ASSERT(LOCK_OK == lockMgr.lock(resId, &request2, MODE_IS)); - ASSERT(LOCK_OK == lockMgr.lock(resId, &request3, MODE_IS)); + // If request1 goes away, the policy should still be compatible-first, because of request2 + ASSERT(lockMgr.unlock(&request1)); - // Now an exclusive mode comes, which would block - MMAPV1LockerImpl lockerX; - LockRequestCombo requestX(&lockerX); - - ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestX, MODE_X)); - - // If an S comes, it should be granted, because of request2 - { - MMAPV1LockerImpl lockerS; - LockRequestCombo requestS(&lockerS); - ASSERT(LOCK_OK == lockMgr.lock(resId, &requestS, MODE_S)); - ASSERT(lockMgr.unlock(&requestS)); - } - - // If request1 goes away, the policy should still be compatible-first, because of request2 - ASSERT(lockMgr.unlock(&request1)); - - // If S comes again, it should be granted, because of request2 still there - { - MMAPV1LockerImpl lockerS; - LockRequestCombo requestS(&lockerS); - ASSERT(LOCK_OK == lockMgr.lock(resId, &requestS, MODE_S)); - ASSERT(lockMgr.unlock(&requestS)); - } + // If S comes again, it should be granted, because of request2 still there + { + MMAPV1LockerImpl lockerS; + LockRequestCombo requestS(&lockerS); + ASSERT(LOCK_OK == lockMgr.lock(resId, &requestS, MODE_S)); + ASSERT(lockMgr.unlock(&requestS)); + } - // With request2 gone the policy should go back to FIFO, even though request3 is active - ASSERT(lockMgr.unlock(&request2)); + // With request2 gone the policy should go back to FIFO, even though request3 is active + ASSERT(lockMgr.unlock(&request2)); - { - MMAPV1LockerImpl lockerS; - LockRequestCombo requestS(&lockerS); - ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestS, MODE_S)); - ASSERT(lockMgr.unlock(&requestS)); - } - - // Unlock request3 to keep the lock mgr not assert for leaked locks - ASSERT(lockMgr.unlock(&request3)); - ASSERT(lockMgr.unlock(&requestX)); + { + MMAPV1LockerImpl lockerS; + LockRequestCombo requestS(&lockerS); + ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestS, MODE_S)); + ASSERT(lockMgr.unlock(&requestS)); } - TEST(LockManager, CompatibleFirstDelayedGrant) { - LockManager lockMgr; - const ResourceId resId(RESOURCE_GLOBAL, 0); - - MMAPV1LockerImpl lockerXInitial; - LockRequestCombo requestXInitial(&lockerXInitial); - ASSERT(LOCK_OK == lockMgr.lock(resId, &requestXInitial, MODE_X)); - - MMAPV1LockerImpl locker1; - LockRequestCombo request1(&locker1); - - MMAPV1LockerImpl locker2; - LockRequestCombo request2(&locker2); - request2.compatibleFirst = true; - - MMAPV1LockerImpl locker3; - LockRequestCombo request3(&locker3); - - // Lock all in IS mode (should block behind the global lock) - ASSERT(LOCK_WAITING == lockMgr.lock(resId, &request1, MODE_IS)); - ASSERT(LOCK_WAITING == lockMgr.lock(resId, &request2, MODE_IS)); - ASSERT(LOCK_WAITING == lockMgr.lock(resId, &request3, MODE_IS)); - - // Now an exclusive mode comes, which would block behind the IS modes - MMAPV1LockerImpl lockerX; - LockRequestCombo requestX(&lockerX); - ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestX, MODE_X)); - - // Free the first X lock so all IS modes are granted - ASSERT(lockMgr.unlock(&requestXInitial)); - ASSERT(request1.lastResult == LOCK_OK); - ASSERT(request2.lastResult == LOCK_OK); - ASSERT(request3.lastResult == LOCK_OK); - - // If an S comes, it should be granted, because of request2 - { - MMAPV1LockerImpl lockerS; - LockRequestCombo requestS(&lockerS); - ASSERT(LOCK_OK == lockMgr.lock(resId, &requestS, MODE_S)); - ASSERT(lockMgr.unlock(&requestS)); - } - - // If request1 goes away, the policy should still be compatible-first, because of request2 - ASSERT(lockMgr.unlock(&request1)); + // Unlock request3 to keep the lock mgr not assert for leaked locks + ASSERT(lockMgr.unlock(&request3)); + ASSERT(lockMgr.unlock(&requestX)); +} + +TEST(LockManager, CompatibleFirstDelayedGrant) { + LockManager lockMgr; + const ResourceId resId(RESOURCE_GLOBAL, 0); + + MMAPV1LockerImpl lockerXInitial; + LockRequestCombo requestXInitial(&lockerXInitial); + ASSERT(LOCK_OK == lockMgr.lock(resId, &requestXInitial, MODE_X)); + + MMAPV1LockerImpl locker1; + LockRequestCombo request1(&locker1); + + MMAPV1LockerImpl locker2; + LockRequestCombo request2(&locker2); + request2.compatibleFirst = true; + + MMAPV1LockerImpl locker3; + LockRequestCombo request3(&locker3); + + // Lock all in IS mode (should block behind the global lock) + ASSERT(LOCK_WAITING == lockMgr.lock(resId, &request1, MODE_IS)); + ASSERT(LOCK_WAITING == lockMgr.lock(resId, &request2, MODE_IS)); + ASSERT(LOCK_WAITING == lockMgr.lock(resId, &request3, MODE_IS)); + + // Now an exclusive mode comes, which would block behind the IS modes + MMAPV1LockerImpl lockerX; + LockRequestCombo requestX(&lockerX); + ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestX, MODE_X)); + + // Free the first X lock so all IS modes are granted + ASSERT(lockMgr.unlock(&requestXInitial)); + ASSERT(request1.lastResult == LOCK_OK); + ASSERT(request2.lastResult == LOCK_OK); + ASSERT(request3.lastResult == LOCK_OK); + + // If an S comes, it should be granted, because of request2 + { + MMAPV1LockerImpl lockerS; + LockRequestCombo requestS(&lockerS); + ASSERT(LOCK_OK == lockMgr.lock(resId, &requestS, MODE_S)); + ASSERT(lockMgr.unlock(&requestS)); + } - // If S comes again, it should be granted, because of request2 still there - { - MMAPV1LockerImpl lockerS; - LockRequestCombo requestS(&lockerS); - ASSERT(LOCK_OK == lockMgr.lock(resId, &requestS, MODE_S)); - ASSERT(lockMgr.unlock(&requestS)); - } + // If request1 goes away, the policy should still be compatible-first, because of request2 + ASSERT(lockMgr.unlock(&request1)); - // With request2 gone the policy should go back to FIFO, even though request3 is active - ASSERT(lockMgr.unlock(&request2)); + // If S comes again, it should be granted, because of request2 still there + { + MMAPV1LockerImpl lockerS; + LockRequestCombo requestS(&lockerS); + ASSERT(LOCK_OK == lockMgr.lock(resId, &requestS, MODE_S)); + ASSERT(lockMgr.unlock(&requestS)); + } - { - MMAPV1LockerImpl lockerS; - LockRequestCombo requestS(&lockerS); - ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestS, MODE_S)); - ASSERT(lockMgr.unlock(&requestS)); - } + // With request2 gone the policy should go back to FIFO, even though request3 is active + ASSERT(lockMgr.unlock(&request2)); - // Unlock request3 to keep the lock mgr not assert for leaked locks - ASSERT(lockMgr.unlock(&request3)); - ASSERT(lockMgr.unlock(&requestX)); + { + MMAPV1LockerImpl lockerS; + LockRequestCombo requestS(&lockerS); + ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestS, MODE_S)); + ASSERT(lockMgr.unlock(&requestS)); } - TEST(LockManager, CompatibleFirstCancelWaiting) { - LockManager lockMgr; - const ResourceId resId(RESOURCE_GLOBAL, 0); - - MMAPV1LockerImpl lockerSInitial; - LockRequestCombo requestSInitial(&lockerSInitial); - ASSERT(LOCK_OK == lockMgr.lock(resId, &requestSInitial, MODE_S)); - - MMAPV1LockerImpl lockerX; - LockRequestCombo requestX(&lockerX); - ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestX, MODE_X)); - - MMAPV1LockerImpl lockerPending; - LockRequestCombo requestPending(&lockerPending); - requestPending.compatibleFirst = true; - ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestPending, MODE_S)); - - // S1 is not granted yet, so the policy should still be FIFO - { - MMAPV1LockerImpl lockerS; - LockRequestCombo requestS(&lockerS); - ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestS, MODE_S)); - ASSERT(lockMgr.unlock(&requestS)); - } + // Unlock request3 to keep the lock mgr not assert for leaked locks + ASSERT(lockMgr.unlock(&request3)); + ASSERT(lockMgr.unlock(&requestX)); +} + +TEST(LockManager, CompatibleFirstCancelWaiting) { + LockManager lockMgr; + const ResourceId resId(RESOURCE_GLOBAL, 0); + + MMAPV1LockerImpl lockerSInitial; + LockRequestCombo requestSInitial(&lockerSInitial); + ASSERT(LOCK_OK == lockMgr.lock(resId, &requestSInitial, MODE_S)); + + MMAPV1LockerImpl lockerX; + LockRequestCombo requestX(&lockerX); + ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestX, MODE_X)); + + MMAPV1LockerImpl lockerPending; + LockRequestCombo requestPending(&lockerPending); + requestPending.compatibleFirst = true; + ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestPending, MODE_S)); + + // S1 is not granted yet, so the policy should still be FIFO + { + MMAPV1LockerImpl lockerS; + LockRequestCombo requestS(&lockerS); + ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestS, MODE_S)); + ASSERT(lockMgr.unlock(&requestS)); + } - // Unlock S1, the policy should still be FIFO - ASSERT(lockMgr.unlock(&requestPending)); + // Unlock S1, the policy should still be FIFO + ASSERT(lockMgr.unlock(&requestPending)); - { - MMAPV1LockerImpl lockerS; - LockRequestCombo requestS(&lockerS); - ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestS, MODE_S)); - ASSERT(lockMgr.unlock(&requestS)); - } - - // Unlock remaining locks to keep the leak detection logic happy - ASSERT(lockMgr.unlock(&requestSInitial)); - ASSERT(lockMgr.unlock(&requestX)); + { + MMAPV1LockerImpl lockerS; + LockRequestCombo requestS(&lockerS); + ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestS, MODE_S)); + ASSERT(lockMgr.unlock(&requestS)); } -} // namespace mongo + // Unlock remaining locks to keep the leak detection logic happy + ASSERT(lockMgr.unlock(&requestSInitial)); + ASSERT(lockMgr.unlock(&requestX)); +} + +} // namespace mongo |