summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenety Goh <benety@mongodb.com>2017-09-02 21:38:35 -0400
committerBenety Goh <benety@mongodb.com>2017-09-05 14:26:34 -0400
commit4db291425761c1b557f10b643242ed08eb542df7 (patch)
treef4ea93eec15cea00066384d0353cdcc336322f92
parenta644bf58213f0786b8415277c1d54277b4f14581 (diff)
downloadmongo-4db291425761c1b557f10b643242ed08eb542df7.tar.gz
SERVER-30371 add tests for downgrading global lock from MODE_X to MODE_IX
-rw-r--r--src/mongo/db/concurrency/d_concurrency_test.cpp146
1 files changed, 146 insertions, 0 deletions
diff --git a/src/mongo/db/concurrency/d_concurrency_test.cpp b/src/mongo/db/concurrency/d_concurrency_test.cpp
index 3b012a12e5e..d4dcc226635 100644
--- a/src/mongo/db/concurrency/d_concurrency_test.cpp
+++ b/src/mongo/db/concurrency/d_concurrency_test.cpp
@@ -279,6 +279,152 @@ TEST_F(DConcurrencyTestFixture, GlobalWriteAndGlobalRead) {
ASSERT(lockState->isW());
}
+TEST_F(DConcurrencyTestFixture,
+ GlobalWriteRequiresExplicitDowngradeToIntentWriteModeIfDestroyedWhileHoldingDatabaseLock) {
+ auto opCtx = makeOpCtx();
+ opCtx->setLockState(stdx::make_unique<MMAPV1LockerImpl>());
+ auto lockState = opCtx->lockState();
+
+ const ResourceId globalId(RESOURCE_GLOBAL, ResourceId::SINGLETON_GLOBAL);
+ const ResourceId mmapId(RESOURCE_MMAPV1_FLUSH, ResourceId::SINGLETON_MMAPV1_FLUSH);
+
+ auto globalWrite = stdx::make_unique<Lock::GlobalWrite>(opCtx.get());
+ ASSERT(lockState->isW());
+ ASSERT(MODE_X == lockState->getLockMode(globalId))
+ << "unexpected global lock mode " << modeName(lockState->getLockMode(globalId));
+ ASSERT(MODE_IX == lockState->getLockMode(mmapId)) << "unexpected MMAPv1 flush lock mode "
+ << modeName(lockState->getLockMode(mmapId));
+
+ {
+ Lock::DBLock dbWrite(opCtx.get(), "db", MODE_IX);
+ ASSERT(lockState->isW());
+ ASSERT(MODE_X == lockState->getLockMode(globalId))
+ << "unexpected global lock mode " << modeName(lockState->getLockMode(globalId));
+ ASSERT(MODE_IX == lockState->getLockMode(mmapId))
+ << "unexpected MMAPv1 flush lock mode " << modeName(lockState->getLockMode(mmapId));
+
+ // If we destroy the GlobalWrite out of order relative to the DBLock, we will leave the
+ // global lock resource locked in MODE_X. We have to explicitly downgrade this resource to
+ // MODE_IX to allow other write operations to make progress.
+ // This test case illustrates non-recommended usage of the RAII types. See SERVER-30948.
+ globalWrite = {};
+ ASSERT(lockState->isW());
+ lockState->downgrade(globalId, MODE_IX);
+ ASSERT_FALSE(lockState->isW());
+ ASSERT(lockState->isWriteLocked());
+ ASSERT(MODE_IX == lockState->getLockMode(globalId))
+ << "unexpected global lock mode " << modeName(lockState->getLockMode(globalId));
+ ASSERT(MODE_IX == lockState->getLockMode(mmapId))
+ << "unexpected MMAPv1 flush lock mode " << modeName(lockState->getLockMode(mmapId));
+ }
+
+
+ ASSERT_FALSE(lockState->isW());
+ ASSERT_FALSE(lockState->isWriteLocked());
+ ASSERT(MODE_NONE == lockState->getLockMode(globalId))
+ << "unexpected global lock mode " << modeName(lockState->getLockMode(globalId));
+ ASSERT(MODE_NONE == lockState->getLockMode(mmapId)) << "unexpected MMAPv1 flush lock mode "
+ << modeName(lockState->getLockMode(mmapId));
+}
+
+TEST_F(DConcurrencyTestFixture,
+ GlobalWriteRequiresSupportsDowngradeToIntentWriteModeWhileHoldingDatabaseLock) {
+ auto opCtx = makeOpCtx();
+ opCtx->setLockState(stdx::make_unique<MMAPV1LockerImpl>());
+ auto lockState = opCtx->lockState();
+
+ const ResourceId globalId(RESOURCE_GLOBAL, ResourceId::SINGLETON_GLOBAL);
+ const ResourceId mmapId(RESOURCE_MMAPV1_FLUSH, ResourceId::SINGLETON_MMAPV1_FLUSH);
+
+ auto globalWrite = stdx::make_unique<Lock::GlobalWrite>(opCtx.get());
+ ASSERT(lockState->isW());
+ ASSERT(MODE_X == lockState->getLockMode(globalId))
+ << "unexpected global lock mode " << modeName(lockState->getLockMode(globalId));
+ ASSERT(MODE_IX == lockState->getLockMode(mmapId)) << "unexpected MMAPv1 flush lock mode "
+ << modeName(lockState->getLockMode(mmapId));
+
+ {
+ Lock::DBLock dbWrite(opCtx.get(), "db", MODE_IX);
+ ASSERT(lockState->isW());
+ ASSERT(MODE_X == lockState->getLockMode(globalId))
+ << "unexpected global lock mode " << modeName(lockState->getLockMode(globalId));
+ ASSERT(MODE_IX == lockState->getLockMode(mmapId))
+ << "unexpected MMAPv1 flush lock mode " << modeName(lockState->getLockMode(mmapId));
+
+ // Downgrade global lock resource to MODE_IX to allow other write operations to make
+ // progress.
+ lockState->downgrade(globalId, MODE_IX);
+ ASSERT_FALSE(lockState->isW());
+ ASSERT(lockState->isWriteLocked());
+ ASSERT(MODE_IX == lockState->getLockMode(globalId))
+ << "unexpected global lock mode " << modeName(lockState->getLockMode(globalId));
+ ASSERT(MODE_IX == lockState->getLockMode(mmapId))
+ << "unexpected MMAPv1 flush lock mode " << modeName(lockState->getLockMode(mmapId));
+ }
+
+ ASSERT_FALSE(lockState->isW());
+ ASSERT(lockState->isWriteLocked());
+
+ globalWrite = {};
+ ASSERT_FALSE(lockState->isW());
+ ASSERT_FALSE(lockState->isWriteLocked());
+ ASSERT(MODE_NONE == lockState->getLockMode(globalId))
+ << "unexpected global lock mode " << modeName(lockState->getLockMode(globalId));
+ ASSERT(MODE_NONE == lockState->getLockMode(mmapId)) << "unexpected MMAPv1 flush lock mode "
+ << modeName(lockState->getLockMode(mmapId));
+}
+
+TEST_F(DConcurrencyTestFixture,
+ NestedGlobalWriteSupportsDowngradeToIntentWriteModeWhileHoldingDatabaseLock) {
+ auto opCtx = makeOpCtx();
+ opCtx->setLockState(stdx::make_unique<MMAPV1LockerImpl>());
+ auto lockState = opCtx->lockState();
+
+ const ResourceId globalId(RESOURCE_GLOBAL, ResourceId::SINGLETON_GLOBAL);
+ const ResourceId mmapId(RESOURCE_MMAPV1_FLUSH, ResourceId::SINGLETON_MMAPV1_FLUSH);
+
+ auto outerGlobalWrite = stdx::make_unique<Lock::GlobalWrite>(opCtx.get());
+ auto innerGlobalWrite = stdx::make_unique<Lock::GlobalWrite>(opCtx.get());
+
+ {
+ Lock::DBLock dbWrite(opCtx.get(), "db", MODE_IX);
+ ASSERT(lockState->isW());
+ ASSERT(MODE_X == lockState->getLockMode(globalId))
+ << "unexpected global lock mode " << modeName(lockState->getLockMode(globalId));
+ ASSERT(MODE_IX == lockState->getLockMode(mmapId))
+ << "unexpected MMAPv1 flush lock mode " << modeName(lockState->getLockMode(mmapId));
+
+ // Downgrade global lock resource to MODE_IX to allow other write operations to make
+ // progress.
+ lockState->downgrade(globalId, MODE_IX);
+ ASSERT_FALSE(lockState->isW());
+ ASSERT(lockState->isWriteLocked());
+ ASSERT(MODE_IX == lockState->getLockMode(globalId))
+ << "unexpected global lock mode " << modeName(lockState->getLockMode(globalId));
+ ASSERT(MODE_IX == lockState->getLockMode(mmapId))
+ << "unexpected MMAPv1 flush lock mode " << modeName(lockState->getLockMode(mmapId));
+ }
+
+ ASSERT_FALSE(lockState->isW());
+ ASSERT(lockState->isWriteLocked());
+
+ innerGlobalWrite = {};
+ ASSERT_FALSE(lockState->isW());
+ ASSERT(lockState->isWriteLocked());
+ ASSERT(MODE_IX == lockState->getLockMode(globalId))
+ << "unexpected global lock mode " << modeName(lockState->getLockMode(globalId));
+ ASSERT(MODE_IX == lockState->getLockMode(mmapId)) << "unexpected MMAPv1 flush lock mode "
+ << modeName(lockState->getLockMode(mmapId));
+
+ outerGlobalWrite = {};
+ ASSERT_FALSE(lockState->isW());
+ ASSERT_FALSE(lockState->isWriteLocked());
+ ASSERT(MODE_NONE == lockState->getLockMode(globalId))
+ << "unexpected global lock mode " << modeName(lockState->getLockMode(globalId));
+ ASSERT(MODE_NONE == lockState->getLockMode(mmapId)) << "unexpected MMAPv1 flush lock mode "
+ << modeName(lockState->getLockMode(mmapId));
+}
+
TEST_F(DConcurrencyTestFixture, GlobalLockS_Timeout) {
auto clients = makeKClientsWithLockers<MMAPV1LockerImpl>(2);