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)); // 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)); } // 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)); }
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, 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)); // The subsequent request will block MMAPV1LockerImpl lockerLow; LockRequestCombo requestLow(&lockerLow); ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestLow, MODE_X)); // This is a "queue jumping request", which will go before locker 2 above MMAPV1LockerImpl lockerHi; LockRequestCombo requestHi(&lockerHi); requestHi.enqueueAtFront = true; ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestHi, MODE_X)); // Once the X request is gone, lockerHi should be granted, because it's queue jumping ASSERT(lockMgr.unlock(&requestX)); ASSERT(requestHi.lastResId == resId); ASSERT(requestHi.lastResult == LOCK_OK); // Finally lockerLow should be granted ASSERT(lockMgr.unlock(&requestHi)); ASSERT(requestLow.lastResId == resId); ASSERT(requestLow.lastResult == LOCK_OK); // This avoids the lock manager asserting on leaked locks ASSERT(lockMgr.unlock(&requestLow)); }
TEST(LockManager, Grant) { LockManager lockMgr; const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); MMAPV1LockerImpl locker; TrackingLockGrantNotification notify; 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); lockMgr.unlock(&request); ASSERT(request.recursiveCount == 0); }
TEST(LockManager, Downgrade) { LockManager lockMgr; const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); MMAPV1LockerImpl locker1; LockRequestCombo request1(&locker1); ASSERT(LOCK_OK == lockMgr.lock(resId, &request1, MODE_X)); MMAPV1LockerImpl locker2; LockRequestCombo request2(&locker2); ASSERT(LOCK_WAITING == lockMgr.lock(resId, &request2, MODE_S)); // Downgrade the X request to S lockMgr.downgrade(&request1, MODE_S); ASSERT(request2.numNotifies == 1); ASSERT(request2.lastResult == LOCK_OK); ASSERT(request2.recursiveCount == 1); ASSERT(lockMgr.unlock(&request1)); ASSERT(lockMgr.unlock(&request2)); }