TEST(LockManager, GrantMultipleFIFOOrder) { LockManager lockMgr; const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); std::unique_ptr<MMAPV1LockerImpl> locker[6]; for (int i = 0; i < 6; i++) { locker[i].reset(new MMAPV1LockerImpl()); } TrackingLockGrantNotification notify[6]; 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(request[i].mode == MODE_X); ASSERT(request[i].recursiveCount == 1); } // 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); } // Release the last one lockMgr.unlock(&request[5]); }
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, 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)); } ASSERT(request[i].mode == MODE_X); ASSERT(request[i].recursiveCount == 1); } ASSERT(notify.numNotifies == 0); // 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]); if (i < 5) { ASSERT(notify.numNotifies == i + 1); } } }
TEST(LockManager, ConflictCancelWaiting) { LockManager lockMgr; const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); MMAPV1LockerImpl locker1; TrackingLockGrantNotification notify1; MMAPV1LockerImpl locker2; TrackingLockGrantNotification notify2; LockRequest request1; request1.initNew(&locker1, ¬ify1); LockRequest request2; request2.initNew(&locker2, ¬ify2); // First request granted right away ASSERT(LOCK_OK == lockMgr.lock(resId, &request1, MODE_S)); ASSERT(notify1.numNotifies == 0); ASSERT(LOCK_WAITING == lockMgr.lock(resId, &request2, MODE_X)); // Release second request (which is still in the WAITING mode) lockMgr.unlock(&request2); ASSERT(notify2.numNotifies == 0); ASSERT(request1.mode == MODE_S); ASSERT(request1.recursiveCount == 1); // Release second acquire lockMgr.unlock(&request1); }
TEST(LockManager, GrantRecursiveNonCompatibleConvertDown) { LockManager lockMgr; const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); LockState locker; TrackingLockGrantNotification notify; LockRequest request; request.initNew(&locker, ¬ify); ASSERT(LOCK_OK == lockMgr.lock(resId, &request, MODE_X)); ASSERT(request.mode == MODE_X); ASSERT(request.recursiveCount == 1); ASSERT(notify.numNotifies == 0); // Acquire again, in *non-compatible*, but less strict mode ASSERT(LOCK_OK == lockMgr.lock(resId, &request, MODE_S)); ASSERT(request.mode == MODE_X); ASSERT(request.recursiveCount == 2); ASSERT(notify.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); }
// 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 lockerExisting; TrackingLockGrantNotification notifyExisting; LockRequest requestExisting; requestExisting.initNew(&lockerExisting, ¬ifyExisting); ASSERT(LOCK_OK == lockMgr.lock(resId, &requestExisting, existingMode)); MMAPV1LockerImpl lockerNew; TrackingLockGrantNotification notifyNew; LockRequest requestNew; requestNew.initNew(&lockerNew, ¬ifyNew); 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); }
void TestOnMappedNetworkDrives::ActualTestImportDocument() { DocProviderWorker objWorker; WSDocNonCom wsDoc; LockManager lockMgr; CStdString sMappedDriveLetter = m_NetDriveHelper.GetMapNetworkDriveLetter(); assertMessage(!sMappedDriveLetter.IsEmpty(), _T("Cannot map network drive - used to just skip test")); CStdString sOrigFile = sMappedDriveLetter + _T("\\Original.doc"); DocumentID docImportID(DocumentID::GenerateIdFromFilePath(sOrigFile)); CStdString csLocalFile = docImportID.CreateLocalFile(); objWorker.PopulateDocumentProperties(docImportID, wsDoc, false); CStdString sOldDocID = wsDoc.GetDocId(); HRESULT hrImport = objWorker.ImportDocument(wsDoc, false); assertMessage(SUCCEEDED(hrImport),_T("Failure code returned from ImportDocument()")); if(sOldDocID.CompareNoCase( CStdString(wsDoc.GetDocId()))==0) { assertMessage(false,_T("The in and out DocID's are the same after import")); } // check lock=false CStdString sLockedBy; if(lockMgr.IsDocIDLocked(wsDoc.GetDocId(), sLockedBy) ) { assertMessage(false, _T("Imported file is locked when it was requested not to be")); } if(docImportID.GetDescription() != wsDoc.GetDescription()) { assertMessage(false, _T("The document Description does not match the original file used for the import.")); } assertTest((wsDoc.GetFlags() & DOCUMENT_LOCKED_BY_US)==0); LFSDMSHelper dmsHelp; DocumentID docID(wsDoc.GetDocId()); CStdString sLocalName(docID.GetWorkingFile()); int iFileNameStartsAt = sLocalName.ReverseFind('\\'); if ((iFileNameStartsAt != CStdString::npos) && (dmsHelp.DoesFileExist(sLocalName))) { sLocalName.resize(iFileNameStartsAt); assertTest(dmsHelp.DeleteDirectoryAndContents(sLocalName)); } ::DeleteFile(csLocalFile.c_str()); }
void TestCloseDocument::TestUnlocksFileOnClose() { DocProviderWorker objWorker; WSDocNonCom wsDoc; DocumentID docTarget(DocumentID::GenerateIdFromFilePath(sTestOriginalFileName)); wsDoc.SetDescription(docTarget.GetDescription()); wsDoc.SetLocalFile(docTarget.GetWorkingFile()); assertTest(objWorker.ImportDocument(wsDoc, true) == S_OK); m_wsImportedDoc = wsDoc; assertTest(objWorker.CloseDocument(wsDoc,0) == S_OK); LockManager lckMngr; CStdString sLockedBy; assertTest(lckMngr.IsDocIDLocked(wsDoc.GetDocId(),sLockedBy) == false); }
TEST(LockManager, ConflictingConversion) { LockManager lockMgr; const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); MMAPV1LockerImpl locker1; MMAPV1LockerImpl locker2; LockRequestCombo request1(&locker1); LockRequestCombo request2(&locker2); // The S requests are granted right away ASSERT(LOCK_OK == lockMgr.lock(resId, &request1, MODE_S)); ASSERT(request1.numNotifies == 0); ASSERT(LOCK_OK == lockMgr.lock(resId, &request2, MODE_S)); ASSERT(request2.numNotifies == 0); // Convert first request to conflicting ASSERT(LOCK_WAITING == lockMgr.convert(resId, &request1, MODE_X)); ASSERT(request1.numNotifies == 0); // 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); // Frees the first reference, mode remains X lockMgr.unlock(&request1); ASSERT(request1.mode == MODE_X); ASSERT(request1.recursiveCount == 1); lockMgr.unlock(&request1); }
TEST(LockManager, Upgrade) { LockManager lockMgr; const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); LockState locker1; TrackingLockGrantNotification notify1; LockRequest request1; request1.initNew(&locker1, ¬ify1); ASSERT(LOCK_OK == lockMgr.lock(resId, &request1, MODE_IS)); LockState locker2; TrackingLockGrantNotification notify2; LockRequest request2; request2.initNew(&locker2, ¬ify2); ASSERT(LOCK_OK == lockMgr.lock(resId, &request2, MODE_S)); ASSERT(request2.recursiveCount == 1); // Upgrade the IS lock to X ASSERT(LOCK_WAITING == lockMgr.lock(resId, &request1, MODE_IX)); ASSERT(!lockMgr.unlock(&request1)); ASSERT(lockMgr.unlock(&request1)); ASSERT(lockMgr.unlock(&request2)); }
TEST(LockManager, ConflictCancelMultipleWaiting) { 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); lockMgr.lock(resId, &request[i], MODE_X); ASSERT(request[i].mode == MODE_X); ASSERT(request[i].recursiveCount == 1); } ASSERT(notify.numNotifies == 0); // Free the second (waiting) lockMgr.unlock(&request[1]); // 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[2]); lockMgr.unlock(&request[4]); lockMgr.unlock(&request[0]); }
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, Conflict) { LockManager lockMgr; const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); LockState locker1; TrackingLockGrantNotification notify1; LockState locker2; TrackingLockGrantNotification notify2; LockRequest request1; request1.initNew(&locker1, ¬ify1); LockRequest request2; request2.initNew(&locker2, ¬ify2); // First request granted right away ASSERT(LOCK_OK == lockMgr.lock(resId, &request1, MODE_S)); ASSERT(request1.recursiveCount == 1); ASSERT(notify1.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(notify2.numNotifies == 0); // Release first request lockMgr.unlock(&request1); ASSERT(request1.recursiveCount == 0); ASSERT(notify1.numNotifies == 0); ASSERT(request2.mode == MODE_X); ASSERT(request2.recursiveCount == 1); ASSERT(notify2.numNotifies == 1); ASSERT(notify2.lastResult == LOCK_OK); // Release second acquire lockMgr.unlock(&request2); ASSERT(request2.recursiveCount == 0); ASSERT(notify1.numNotifies == 0); ASSERT(notify2.numNotifies == 1); }
TEST(LockManagerTest, TxShutdown) { LockManager lm; ClientTransaction t1(&lm, 1); ClientTransaction t2(&lm, 2); t1.acquire(kShared, 1, ACQUIRED); lm.shutdown(3000); // t1 can still do work while quiescing t1.release(kShared, 1); t1.acquire(kShared, 2, ACQUIRED); #ifdef TRANSACTION_REGISTRATION // t2 is new and should be refused t2.acquire(kShared, 3, ABORTED); #else t2.quit(); #endif // after the quiescing period, t1's request should be refused sleepsecs(3); t1.acquire(kShared, 4, ABORTED); }
void TestLockDocument::TestLockAttachment() { DocProviderWorker objWorker; LockManager docLock; CStdString sDocID(DocumentID::GetDocProviderId() + TEST_DIRECTORY + TEST_DOC + _T(".doc")); DocumentID docID(sDocID); CStdString szDummyUser(_T("HighHouseShadow")); CStdString sLockFileName(docID.GetLockFileName(szDummyUser, false)); WSDocNonCom wsDoc; objWorker.GetDocument(sDocID,NULL,wsDoc); HRESULT hrLock = docLock.CreateLock(sDocID, szDummyUser); if(FAILED(hrLock)) { CStdString szErr; szErr.Format(_T("CreateLock returned a Failure Error code - %08x"),hrLock); assertMessage(false, szErr); } if (_taccess(sLockFileName, 0) == -1) { assertMessage(false, _T("We didn't create a lock file as HighHouseShadow")); } HRESULT hrUnlock = docLock.RemoveLock(sDocID,szDummyUser); if(FAILED(hrUnlock)) { CStdString szErr; szErr.Format(_T("CreateLock returned a Failure Error code - %08x"),hrUnlock); assertMessage(false,szErr); } if (_taccess(sLockFileName, 0) != -1) { assertMessage(false, _T("We didn't remove the HighHouseShadow lock")); } }
TEST(LockManager, CancelWaitingConversionStrongModes) { 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.numNotifies == 0); // Second request is granted right away ASSERT(LOCK_OK == lockMgr.lock(resId, &request2, MODE_S)); ASSERT(request2.numNotifies == 0); // 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); // 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); }
TEST(LockManager, ConflictingConversionInTheMiddle) { LockManager lockMgr; const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); MMAPV1LockerImpl locker; TrackingLockGrantNotification notify; LockRequest request[3]; for (int i = 0; i < 3; i++) { request[i].initNew(&locker, ¬ify); lockMgr.lock(resId, &request[i], MODE_S); } // Upgrade the one in the middle (not the first one) ASSERT(LOCK_WAITING == lockMgr.convert(resId, &request[1], MODE_X)); ASSERT(notify.numNotifies == 0); // Release the two shared modes lockMgr.unlock(&request[0]); ASSERT(notify.numNotifies == 0); lockMgr.unlock(&request[2]); ASSERT(notify.numNotifies == 1); ASSERT(request[1].mode == MODE_X); // Request 1 should be unlocked twice lockMgr.unlock(&request[1]); lockMgr.unlock(&request[1]); }
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)); }
void TestCloseDocument::TestFailIfLockedByOther() { DocProviderWorker objWorker; WSDocNonCom wsDoc; DocumentID docTarget(DocumentID::GenerateIdFromFilePath(sTestOriginalFileName)); wsDoc.SetDescription(docTarget.GetDescription()); wsDoc.SetLocalFile(docTarget.GetWorkingFile()); assertTest(objWorker.ImportDocument(wsDoc, false) == S_OK); m_wsImportedDoc = wsDoc; LockManager lckMngr; assertTest(lckMngr.CreateLock(wsDoc.GetDocId(),_T("poptart")) == S_OK); assertTest(objWorker.CloseDocument(wsDoc,0) == S_OK); wsDoc.SetFlags(wsDoc.GetFlags()|DOCUMENT_LOCKED_BY_US); assertTest(objWorker.CloseDocument(wsDoc,0) == E_DOCUMENT_LOCKED_BY_ANOTHER); lckMngr.RemoveLock(wsDoc.GetDocId(),_T("poptart")); }
void TestLockDocument::TestAlreadyLockedByOther() { DocProviderWorker objWorker; CStdString sDocID(DocumentID::GetDocProviderId() + TEST_DIRECTORY + TEST_DOC + _T(".doc")); DocumentID docID(sDocID); CStdString sLockFileName(docID.GetLockFileName(_T("apple"), false)); WSDocNonCom wsDoc; LockManager docLock; objWorker.GetDocument(sDocID, 0, wsDoc); docLock.CreateLock(sDocID, _T("apple")); if (_taccess(sLockFileName, 0) == -1) { assertMessage(false, _T("Unable to create lock file")); } assertTest(objWorker.LockDocument(sDocID, LOCK_DOCUMENT) == E_DOCUMENT_LOCKED_BY_ANOTHER); assertMessage(docLock.RemoveLock(docID, _T("apple")) == S_OK, _T("Failed to unlock document at end of test")); }
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); }
void TestCloseDocument::TestDontDeleteLocal() { DocProviderWorker objWorker; WSDocNonCom wsDoc; DocumentID docTarget(DocumentID::GenerateIdFromFilePath(sTestOriginalFileName)); wsDoc.SetDescription(docTarget.GetDescription()); wsDoc.SetLocalFile(docTarget.GetWorkingFile()); assertTest(objWorker.ImportDocument(wsDoc, true) == S_OK); m_wsImportedDoc = wsDoc; assertTest(objWorker.CloseDocument(wsDoc, DF_UNLOCK_ONLY) == S_OK); LockManager lckMngr; assertTest(lckMngr.IsDocIDLockedByUs(wsDoc.GetDocId()) == false); CStdString sLocalFile = wsDoc.GetLocalFile(); if (_taccess(sLocalFile, 0) == -1) { assertMessage(false, _T("CloseDocument deleted local file when it shouldn't have")); } ::DeleteFile(sLocalFile); }
void TestLockDocument::TestLockNonExistentAttachment() { DocProviderWorker objWorker; CStdString sDocID(DocumentID::GetDocProviderId() + TEST_DIRECTORY + TEST_DOC _T(".doc") + _T("?attach.xdf")); CStdString sLockFileName(TEST_DIRECTORY + TEST_DOC _T(".doc") _T("." TEST_DMS_EXT _T("\\Version 1\\attach.xdf_Bones.lock"))); CStdString sFile(_T("c:\\Not\\here.doc")); ::DeleteFile(sFile.c_str()); ::DeleteFile(sLockFileName.c_str()); WSDocNonCom wsDoc; LockManager docLock; const CStdString sUsername(_T("Bones")); HRESULT hrLock = docLock.CreateLock(sDocID, sUsername); if(SUCCEEDED(hrLock)) assertMessage(false,_T("Reported Succeeded in creating lock on non existent file")); if (_taccess(sLockFileName, 0) != -1 ) { assertMessage(false, _T("Lock file exists for a non existent document")); } }
TEST(LockManager, Fairness) { LockManager lockMgr; const ResourceId resId(RESOURCE_GLOBAL, 0); // Start with some 'regular' intent locks MMAPV1LockerImpl lockerIS; LockRequestCombo requestIS(&lockerIS); ASSERT(LOCK_OK == lockMgr.lock(resId, &requestIS, MODE_IS)); MMAPV1LockerImpl lockerIX; LockRequestCombo requestIX(&lockerIX); ASSERT(LOCK_OK == lockMgr.lock(resId, &requestIX, MODE_IX)); // Now a conflicting lock comes MMAPV1LockerImpl lockerX; LockRequestCombo requestX(&lockerX); ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestX, MODE_X)); // Now, whoever comes next should be blocked MMAPV1LockerImpl lockerIX1; LockRequestCombo requestIX1(&lockerIX1); ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestIX1, MODE_IX)); // Freeing the first two locks should grant the X lock ASSERT(lockMgr.unlock(&requestIS)); ASSERT(lockMgr.unlock(&requestIX)); ASSERT_EQ(LOCK_OK, requestX.lastResult); ASSERT_EQ(1, requestX.numNotifies); ASSERT_EQ(LOCK_INVALID, requestIX1.lastResult); ASSERT_EQ(0, requestIX1.numNotifies); ASSERT(lockMgr.unlock(&requestX)); ASSERT_EQ(LOCK_OK, requestIX1.lastResult); ASSERT_EQ(1, requestIX1.numNotifies); // Unlock all locks so we don't assert for leaked locks ASSERT(lockMgr.unlock(&requestIX1)); }
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)); MMAPV1LockerImpl locker2; LockRequestCombo request2(&locker2); ASSERT(LOCK_OK == lockMgr.lock(resId, &request2, MODE_S)); // Upgrade the S lock to X ASSERT(LOCK_WAITING == lockMgr.convert(resId, &request1, MODE_X)); ASSERT(!lockMgr.unlock(&request1)); ASSERT(lockMgr.unlock(&request1)); ASSERT(lockMgr.unlock(&request2)); }
TEST(LockManager, CompatibleFirstGrantAlreadyQueued) { LockManager lockMgr; const ResourceId resId(RESOURCE_GLOBAL, 0); // This tests the following behavior: // Lock held in X, queue: S IX IS, where S is compatibleFirst. // Once X unlocks both the S and IS requests should proceed. MMAPV1LockerImpl locker1; LockRequestCombo request1(&locker1); MMAPV1LockerImpl locker2; LockRequestCombo request2(&locker2); request2.compatibleFirst = true; MMAPV1LockerImpl locker3; LockRequestCombo request3(&locker3); MMAPV1LockerImpl locker4; LockRequestCombo request4(&locker4); // Hold the lock in X and establish the S IX IS queue. ASSERT(LOCK_OK == lockMgr.lock(resId, &request1, MODE_X)); ASSERT(LOCK_WAITING == lockMgr.lock(resId, &request2, MODE_S)); ASSERT(LOCK_WAITING == lockMgr.lock(resId, &request3, MODE_IX)); ASSERT(LOCK_WAITING == lockMgr.lock(resId, &request4, MODE_IS)); // Now unlock, so all readers should be able to proceed, while the IX remains queued. ASSERT(lockMgr.unlock(&request1)); ASSERT(request2.lastResult == LOCK_OK); ASSERT(request3.lastResult == LOCK_INVALID); ASSERT(request4.lastResult == LOCK_OK); // Now unlock the S lock, and the IX succeeds as well. ASSERT(lockMgr.unlock(&request2)); ASSERT(request3.lastResult == LOCK_OK); // Unlock remaining ASSERT(lockMgr.unlock(&request4)); ASSERT(lockMgr.unlock(&request3)); }
TEST(LockManager, ConflictingConversion) { LockManager lockMgr; const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); LockState locker1; TrackingLockGrantNotification notify1; LockState locker2; TrackingLockGrantNotification notify2; LockRequest request1; request1.initNew(&locker1, ¬ify1); LockRequest request2; request2.initNew(&locker2, ¬ify2); // First request granted right away ASSERT(LOCK_OK == lockMgr.lock(resId, &request1, MODE_S)); ASSERT(notify1.numNotifies == 0); ASSERT(LOCK_OK == lockMgr.lock(resId, &request2, MODE_S)); ASSERT(notify2.numNotifies == 0); // Convert first request to conflicting ASSERT(LOCK_WAITING == lockMgr.lock(resId, &request1, MODE_X)); ASSERT(notify1.numNotifies == 0); // Free the second lock and make sure the first is granted lockMgr.unlock(&request2); ASSERT(request1.mode == MODE_X); ASSERT(notify1.numNotifies == 1); ASSERT(notify2.numNotifies == 0); // Frees the first reference, mode remains X lockMgr.unlock(&request1); ASSERT(request1.mode == MODE_X); ASSERT(request1.recursiveCount == 1); lockMgr.unlock(&request1); }
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, ConflictCancelWaitingConversion) { LockManager lockMgr; const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection")); LockState locker1; TrackingLockGrantNotification notify1; LockState locker2; TrackingLockGrantNotification notify2; LockRequest request1; request1.initNew(&locker1, ¬ify1); LockRequest request2; request2.initNew(&locker2, ¬ify2); // First request granted right away ASSERT(LOCK_OK == lockMgr.lock(resId, &request1, MODE_S)); ASSERT(notify1.numNotifies == 0); // Second request is granted right away ASSERT(LOCK_OK == lockMgr.lock(resId, &request2, MODE_S)); ASSERT(notify2.numNotifies == 0); // Convert second request to conflicting ASSERT(LOCK_WAITING == lockMgr.lock(resId, &request2, MODE_X)); ASSERT(request2.mode == MODE_S); ASSERT(request2.convertMode == MODE_X); ASSERT(notify2.numNotifies == 0); // Cancel the conflicting upgrade lockMgr.unlock(&request2); ASSERT(request2.mode == MODE_S); ASSERT(request2.convertMode == MODE_NONE); ASSERT(notify2.numNotifies == 0); // Free the remaining locks so the LockManager destructor does not complain lockMgr.unlock(&request1); lockMgr.unlock(&request2); }
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)); { 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)); }