diff options
21 files changed, 312 insertions, 126 deletions
diff --git a/src/mongo/client/embedded/embedded.cpp b/src/mongo/client/embedded/embedded.cpp index 6ad81043143..a77b8fcecd7 100644 --- a/src/mongo/client/embedded/embedded.cpp +++ b/src/mongo/client/embedded/embedded.cpp @@ -146,7 +146,7 @@ void shutdown(ServiceContext* srvContext) { auto shutdownOpCtx = serviceContext->makeOperationContext(client); { UninterruptibleLockGuard noInterrupt(shutdownOpCtx->lockState()); - Lock::GlobalLock lk(shutdownOpCtx.get(), MODE_X, Date_t::max()); + Lock::GlobalLock lk(shutdownOpCtx.get(), MODE_X); dbHolder().closeAll(shutdownOpCtx.get(), "shutdown"); // Shut down the background periodic task runner diff --git a/src/mongo/db/catalog/coll_mod.cpp b/src/mongo/db/catalog/coll_mod.cpp index a40e08bab4a..bbb6c25bee7 100644 --- a/src/mongo/db/catalog/coll_mod.cpp +++ b/src/mongo/db/catalog/coll_mod.cpp @@ -561,7 +561,7 @@ void addCollectionUUIDs(OperationContext* opCtx) { std::vector<std::string> dbNames; StorageEngine* storageEngine = opCtx->getServiceContext()->getGlobalStorageEngine(); { - Lock::GlobalLock lk(opCtx, MODE_IS, Date_t::max()); + Lock::GlobalLock lk(opCtx, MODE_IS); storageEngine->listDatabases(&dbNames); } diff --git a/src/mongo/db/catalog_raii_test.cpp b/src/mongo/db/catalog_raii_test.cpp index 75d7f74ec63..b824b445148 100644 --- a/src/mongo/db/catalog_raii_test.cpp +++ b/src/mongo/db/catalog_raii_test.cpp @@ -82,7 +82,8 @@ TEST_F(CatalogRAIITestFixture, AutoGetDBDeadline) { } TEST_F(CatalogRAIITestFixture, AutoGetDBGlobalLockDeadline) { - Lock::GlobalLock gLock1(client1.second.get(), MODE_X, Date_t::now()); + Lock::GlobalLock gLock1( + client1.second.get(), MODE_X, Date_t::now(), Lock::InterruptBehavior::kThrow); ASSERT(gLock1.isLocked()); failsWithLockTimeout( [&] { AutoGetDb db(client2.second.get(), nss.db(), MODE_X, Date_t::now() + timeoutMs); }, @@ -150,7 +151,8 @@ TEST_F(CatalogRAIITestFixture, AutoGetCollectionDBLockDeadline) { } TEST_F(CatalogRAIITestFixture, AutoGetCollectionGlobalLockDeadline) { - Lock::GlobalLock gLock1(client1.second.get(), MODE_X, Date_t::now()); + Lock::GlobalLock gLock1( + client1.second.get(), MODE_X, Date_t::now(), Lock::InterruptBehavior::kThrow); ASSERT(client1.second->lockState()->isLocked()); failsWithLockTimeout( [&] { diff --git a/src/mongo/db/commands/fsync.cpp b/src/mongo/db/commands/fsync.cpp index 2d98610310a..f945c2dddaa 100644 --- a/src/mongo/db/commands/fsync.cpp +++ b/src/mongo/db/commands/fsync.cpp @@ -147,7 +147,7 @@ public: } // Take a global IS lock to ensure the storage engine is not shutdown - Lock::GlobalLock global(opCtx, MODE_IS, Date_t::max()); + Lock::GlobalLock global(opCtx, MODE_IS); StorageEngine* storageEngine = getGlobalServiceContext()->getGlobalStorageEngine(); result.append("numFiles", storageEngine->flushAllFiles(opCtx, sync)); return true; diff --git a/src/mongo/db/commands/list_databases.cpp b/src/mongo/db/commands/list_databases.cpp index e4477c9a418..fc8068ad503 100644 --- a/src/mongo/db/commands/list_databases.cpp +++ b/src/mongo/db/commands/list_databases.cpp @@ -114,7 +114,7 @@ public: vector<string> dbNames; StorageEngine* storageEngine = getGlobalServiceContext()->getGlobalStorageEngine(); { - Lock::GlobalLock lk(opCtx, MODE_IS, Date_t::max()); + Lock::GlobalLock lk(opCtx, MODE_IS); storageEngine->listDatabases(&dbNames); } diff --git a/src/mongo/db/commands/oplog_note.cpp b/src/mongo/db/commands/oplog_note.cpp index 852a25b411a..8cd038204d4 100644 --- a/src/mongo/db/commands/oplog_note.cpp +++ b/src/mongo/db/commands/oplog_note.cpp @@ -56,7 +56,8 @@ Status _performNoopWrite(OperationContext* opCtx, BSONObj msgObj, StringData not // Use GlobalLock + lockMMAPV1Flush instead of DBLock to allow return when the lock is not // available. It may happen when the primary steps down and a shared global lock is // acquired. - Lock::GlobalLock lock(opCtx, MODE_IX, Date_t::now() + Milliseconds(1)); + Lock::GlobalLock lock( + opCtx, MODE_IX, Date_t::now() + Milliseconds(1), Lock::InterruptBehavior::kThrow); if (!lock.isLocked()) { LOG(1) << "Global lock is not available skipping noopWrite"; diff --git a/src/mongo/db/commands/restart_catalog_command.cpp b/src/mongo/db/commands/restart_catalog_command.cpp index a3aefcd3168..e8a37330d48 100644 --- a/src/mongo/db/commands/restart_catalog_command.cpp +++ b/src/mongo/db/commands/restart_catalog_command.cpp @@ -87,7 +87,7 @@ public: const std::string& db, const BSONObj& cmdObj, BSONObjBuilder& result) final { - Lock::GlobalLock global(opCtx, MODE_X, Date_t::max()); + Lock::GlobalLock global(opCtx, MODE_X); // This command will fail without modifying the catalog if there are any databases that are // marked drop-pending. (Otherwise, the Database object will be reconstructed when diff --git a/src/mongo/db/commands/set_feature_compatibility_version_command.cpp b/src/mongo/db/commands/set_feature_compatibility_version_command.cpp index c377eb1ef53..8cc02a107b2 100644 --- a/src/mongo/db/commands/set_feature_compatibility_version_command.cpp +++ b/src/mongo/db/commands/set_feature_compatibility_version_command.cpp @@ -167,7 +167,7 @@ public: // - The global IX/X locked operation began prior to the FCV change, is acting on // that assumption and will finish before upgrade procedures begin right after // this. - Lock::GlobalLock lk(opCtx, MODE_S, Date_t::max()); + Lock::GlobalLock lk(opCtx, MODE_S); } // Upgrade shards before config finishes its upgrade. @@ -249,7 +249,7 @@ public: // - The global IX/X locked operation began prior to the FCV change, is acting on // that assumption and will finish before downgrade procedures begin right after // this. - Lock::GlobalLock lk(opCtx, MODE_S, Date_t::max()); + Lock::GlobalLock lk(opCtx, MODE_S); } // Downgrade shards before config finishes its downgrade. diff --git a/src/mongo/db/commands/snapshot_management.cpp b/src/mongo/db/commands/snapshot_management.cpp index 565d01412a0..7059f937318 100644 --- a/src/mongo/db/commands/snapshot_management.cpp +++ b/src/mongo/db/commands/snapshot_management.cpp @@ -77,7 +77,7 @@ public: {ErrorCodes::CommandNotSupported, ""}); } - Lock::GlobalLock lk(opCtx, MODE_IX, Date_t::max()); + Lock::GlobalLock lk(opCtx, MODE_IX); const auto name = repl::ReplicationCoordinator::get(opCtx)->getMinimumVisibleSnapshot(opCtx); @@ -123,7 +123,7 @@ public: {ErrorCodes::CommandNotSupported, ""}); } - Lock::GlobalLock lk(opCtx, MODE_IX, Date_t::max()); + Lock::GlobalLock lk(opCtx, MODE_IX); auto timestamp = Timestamp(cmdObj.firstElement().Long()); snapshotManager->setCommittedSnapshot(timestamp); return true; diff --git a/src/mongo/db/concurrency/d_concurrency.cpp b/src/mongo/db/concurrency/d_concurrency.cpp index 77458fa1882..ca9e9f44691 100644 --- a/src/mongo/db/concurrency/d_concurrency.cpp +++ b/src/mongo/db/concurrency/d_concurrency.cpp @@ -136,18 +136,23 @@ bool Lock::ResourceMutex::isAtLeastReadLocked(Locker* locker) { return locker->isLockHeldForMode(_rid, MODE_IS); } -Lock::GlobalLock::GlobalLock(OperationContext* opCtx, LockMode lockMode, Date_t deadline) - : GlobalLock(opCtx, lockMode, deadline, EnqueueOnly()) { +Lock::GlobalLock::GlobalLock(OperationContext* opCtx, + LockMode lockMode, + Date_t deadline, + InterruptBehavior behavior) + : GlobalLock(opCtx, lockMode, deadline, behavior, EnqueueOnly()) { waitForLockUntil(deadline); } Lock::GlobalLock::GlobalLock(OperationContext* opCtx, LockMode lockMode, Date_t deadline, + InterruptBehavior behavior, EnqueueOnly enqueueOnly) : _opCtx(opCtx), _result(LOCK_INVALID), _pbwm(opCtx->lockState(), resourceIdParallelBatchWriterMode), + _interruptBehavior(behavior), _isOutermostLock(!opCtx->lockState()->isLocked()) { _enqueue(lockMode, deadline); } @@ -156,26 +161,40 @@ Lock::GlobalLock::GlobalLock(GlobalLock&& otherLock) : _opCtx(otherLock._opCtx), _result(otherLock._result), _pbwm(std::move(otherLock._pbwm)), + _interruptBehavior(otherLock._interruptBehavior), _isOutermostLock(otherLock._isOutermostLock) { // Mark as moved so the destructor doesn't invalidate the newly-constructed lock. otherLock._result = LOCK_INVALID; } void Lock::GlobalLock::_enqueue(LockMode lockMode, Date_t deadline) { - if (_opCtx->lockState()->shouldConflictWithSecondaryBatchApplication()) { - _pbwm.lock(MODE_IS); - } + try { + if (_opCtx->lockState()->shouldConflictWithSecondaryBatchApplication()) { + _pbwm.lock(MODE_IS); + } - _result = _opCtx->lockState()->lockGlobalBegin(_opCtx, lockMode, deadline); + _result = _opCtx->lockState()->lockGlobalBegin(_opCtx, lockMode, deadline); + } catch (const ExceptionForCat<ErrorCategory::Interruption>&) { + // The kLeaveUnlocked behavior suppresses this exception. + if (_interruptBehavior == InterruptBehavior::kThrow) + throw; + } } void Lock::GlobalLock::waitForLockUntil(Date_t deadline) { - if (_result == LOCK_WAITING) { - _result = _opCtx->lockState()->lockGlobalComplete(_opCtx, deadline); - } + try { + if (_result == LOCK_WAITING) { + _result = _opCtx->lockState()->lockGlobalComplete(_opCtx, deadline); + } - if (_result != LOCK_OK && _opCtx->lockState()->shouldConflictWithSecondaryBatchApplication()) { - _pbwm.unlock(); + if (_result != LOCK_OK && + _opCtx->lockState()->shouldConflictWithSecondaryBatchApplication()) { + _pbwm.unlock(); + } + } catch (const ExceptionForCat<ErrorCategory::Interruption>&) { + // The kLeaveUnlocked behavior suppresses this exception. + if (_interruptBehavior == InterruptBehavior::kThrow) + throw; } if (_opCtx->lockState()->isWriteLocked()) { @@ -193,7 +212,8 @@ Lock::DBLock::DBLock(OperationContext* opCtx, StringData db, LockMode mode, Date _opCtx(opCtx), _result(LOCK_INVALID), _mode(mode), - _globalLock(opCtx, isSharedLockMode(_mode) ? MODE_IS : MODE_IX, deadline) { + _globalLock( + opCtx, isSharedLockMode(_mode) ? MODE_IS : MODE_IX, deadline, InterruptBehavior::kThrow) { massert(28539, "need a valid database name", !db.empty() && nsIsDbOnly(db)); if (!_globalLock.isLocked()) diff --git a/src/mongo/db/concurrency/d_concurrency.h b/src/mongo/db/concurrency/d_concurrency.h index 6ec596daf4c..9491969733e 100644 --- a/src/mongo/db/concurrency/d_concurrency.h +++ b/src/mongo/db/concurrency/d_concurrency.h @@ -167,6 +167,15 @@ public: }; /** + * The interrupt behavior is used to tell a lock how to handle an interrupted lock acquisition. + */ + enum class InterruptBehavior { + kThrow, // Throw the interruption exception. + kLeaveUnlocked // Suppress the exception, but leave unlocked such that a call to isLocked() + // returns false. + }; + + /** * Global lock. * * Grabs global resource lock. Allows further (recursive) acquisition of the global lock @@ -180,7 +189,21 @@ public: public: class EnqueueOnly {}; - GlobalLock(OperationContext* opCtx, LockMode lockMode, Date_t deadline); + /** + * A GlobalLock without a deadline defaults to Date_t::max() and an InterruptBehavior of + * kThrow. + */ + GlobalLock(OperationContext* opCtx, LockMode lockMode) + : GlobalLock(opCtx, lockMode, Date_t::max(), InterruptBehavior::kThrow) {} + + /** + * A GlobalLock with a deadline requires the interrupt behavior to be explicitly defined. + */ + GlobalLock(OperationContext* opCtx, + LockMode lockMode, + Date_t deadline, + InterruptBehavior behavior); + GlobalLock(GlobalLock&&); /** @@ -193,6 +216,7 @@ public: GlobalLock(OperationContext* opCtx, LockMode lockMode, Date_t deadline, + InterruptBehavior behavior, EnqueueOnly enqueueOnly); ~GlobalLock() { @@ -226,6 +250,7 @@ public: OperationContext* const _opCtx; LockResult _result; ResourceLock _pbwm; + InterruptBehavior _interruptBehavior; const bool _isOutermostLock; }; @@ -238,8 +263,10 @@ public: */ class GlobalWrite : public GlobalLock { public: - explicit GlobalWrite(OperationContext* opCtx, Date_t deadline = Date_t::max()) - : GlobalLock(opCtx, MODE_X, deadline) { + explicit GlobalWrite(OperationContext* opCtx) + : GlobalWrite(opCtx, Date_t::max(), InterruptBehavior::kThrow) {} + explicit GlobalWrite(OperationContext* opCtx, Date_t deadline, InterruptBehavior behavior) + : GlobalLock(opCtx, MODE_X, deadline, behavior) { if (isLocked()) { opCtx->lockState()->lockMMAPV1Flush(); } @@ -255,8 +282,10 @@ public: */ class GlobalRead : public GlobalLock { public: - explicit GlobalRead(OperationContext* opCtx, Date_t deadline = Date_t::max()) - : GlobalLock(opCtx, MODE_S, deadline) { + explicit GlobalRead(OperationContext* opCtx) + : GlobalRead(opCtx, Date_t::max(), InterruptBehavior::kThrow) {} + explicit GlobalRead(OperationContext* opCtx, Date_t deadline, InterruptBehavior behavior) + : GlobalLock(opCtx, MODE_S, deadline, behavior) { if (isLocked()) { opCtx->lockState()->lockMMAPV1Flush(); } diff --git a/src/mongo/db/concurrency/d_concurrency_test.cpp b/src/mongo/db/concurrency/d_concurrency_test.cpp index 5cddeda4c74..2dbc045f4ed 100644 --- a/src/mongo/db/concurrency/d_concurrency_test.cpp +++ b/src/mongo/db/concurrency/d_concurrency_test.cpp @@ -440,21 +440,27 @@ TEST_F(DConcurrencyTestFixture, TEST_F(DConcurrencyTestFixture, GlobalLockS_Timeout) { auto clients = makeKClientsWithLockers<MMAPV1LockerImpl>(2); - Lock::GlobalLock globalWrite(clients[0].second.get(), MODE_X, Date_t::now()); + Lock::GlobalLock globalWrite( + clients[0].second.get(), MODE_X, Date_t::now(), Lock::InterruptBehavior::kThrow); ASSERT(globalWrite.isLocked()); - Lock::GlobalLock globalReadTry( - clients[1].second.get(), MODE_S, Date_t::now() + Milliseconds(1)); + Lock::GlobalLock globalReadTry(clients[1].second.get(), + MODE_S, + Date_t::now() + Milliseconds(1), + Lock::InterruptBehavior::kThrow); ASSERT(!globalReadTry.isLocked()); } TEST_F(DConcurrencyTestFixture, GlobalLockX_Timeout) { auto clients = makeKClientsWithLockers<MMAPV1LockerImpl>(2); - Lock::GlobalLock globalWrite(clients[0].second.get(), MODE_X, Date_t::now()); + Lock::GlobalLock globalWrite( + clients[0].second.get(), MODE_X, Date_t::now(), Lock::InterruptBehavior::kThrow); ASSERT(globalWrite.isLocked()); - Lock::GlobalLock globalWriteTry( - clients[1].second.get(), MODE_X, Date_t::now() + Milliseconds(1)); + Lock::GlobalLock globalWriteTry(clients[1].second.get(), + MODE_X, + Date_t::now() + Milliseconds(1), + Lock::InterruptBehavior::kThrow); ASSERT(!globalWriteTry.isLocked()); } @@ -464,7 +470,7 @@ TEST_F(DConcurrencyTestFixture, GlobalLockXSetsGlobalLockTakenOnOperationContext ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalExclusiveLockTaken()); { - Lock::GlobalLock globalWrite(opCtx, MODE_X, Date_t::now()); + Lock::GlobalLock globalWrite(opCtx, MODE_X, Date_t::now(), Lock::InterruptBehavior::kThrow); ASSERT(globalWrite.isLocked()); } ASSERT_TRUE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalExclusiveLockTaken()); @@ -475,7 +481,8 @@ TEST_F(DConcurrencyTestFixture, GlobalLockIXSetsGlobalLockTakenOnOperationContex auto opCtx = clients[0].second.get(); ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalExclusiveLockTaken()); { - Lock::GlobalLock globalWrite(opCtx, MODE_IX, Date_t::now()); + Lock::GlobalLock globalWrite( + opCtx, MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow); ASSERT(globalWrite.isLocked()); } ASSERT_TRUE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalExclusiveLockTaken()); @@ -486,7 +493,7 @@ TEST_F(DConcurrencyTestFixture, GlobalLockSDoesNotSetGlobalLockTakenOnOperationC auto opCtx = clients[0].second.get(); ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalExclusiveLockTaken()); { - Lock::GlobalLock globalRead(opCtx, MODE_S, Date_t::now()); + Lock::GlobalLock globalRead(opCtx, MODE_S, Date_t::now(), Lock::InterruptBehavior::kThrow); ASSERT(globalRead.isLocked()); } ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalExclusiveLockTaken()); @@ -497,7 +504,7 @@ TEST_F(DConcurrencyTestFixture, GlobalLockISDoesNotSetGlobalLockTakenOnOperation auto opCtx = clients[0].second.get(); ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalExclusiveLockTaken()); { - Lock::GlobalLock globalRead(opCtx, MODE_IS, Date_t::now()); + Lock::GlobalLock globalRead(opCtx, MODE_IS, Date_t::now(), Lock::InterruptBehavior::kThrow); ASSERT(globalRead.isLocked()); } ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalExclusiveLockTaken()); @@ -525,13 +532,15 @@ TEST_F(DConcurrencyTestFixture, GlobalLockXDoesNotSetGlobalLockTakenWhenLockAcqu auto clients = makeKClientsWithLockers<MMAPV1LockerImpl>(2); // Take a global lock so that the next one times out. - Lock::GlobalLock globalWrite0(clients[0].second.get(), MODE_X, Date_t::now()); + Lock::GlobalLock globalWrite0( + clients[0].second.get(), MODE_X, Date_t::now(), Lock::InterruptBehavior::kThrow); ASSERT(globalWrite0.isLocked()); auto opCtx = clients[1].second.get(); ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalExclusiveLockTaken()); { - Lock::GlobalLock globalWrite1(opCtx, MODE_X, Date_t::now() + Milliseconds(1)); + Lock::GlobalLock globalWrite1( + opCtx, MODE_X, Date_t::now() + Milliseconds(1), Lock::InterruptBehavior::kThrow); ASSERT_FALSE(globalWrite1.isLocked()); } ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalExclusiveLockTaken()); @@ -541,8 +550,10 @@ TEST_F(DConcurrencyTestFixture, GlobalLockS_NoTimeoutDueToGlobalLockS) { auto clients = makeKClientsWithLockers<MMAPV1LockerImpl>(2); Lock::GlobalRead globalRead(clients[0].second.get()); - Lock::GlobalLock globalReadTry( - clients[1].second.get(), MODE_S, Date_t::now() + Milliseconds(1)); + Lock::GlobalLock globalReadTry(clients[1].second.get(), + MODE_S, + Date_t::now() + Milliseconds(1), + Lock::InterruptBehavior::kThrow); ASSERT(globalReadTry.isLocked()); } @@ -551,8 +562,10 @@ TEST_F(DConcurrencyTestFixture, GlobalLockX_TimeoutDueToGlobalLockS) { auto clients = makeKClientsWithLockers<MMAPV1LockerImpl>(2); Lock::GlobalRead globalRead(clients[0].second.get()); - Lock::GlobalLock globalWriteTry( - clients[1].second.get(), MODE_X, Date_t::now() + Milliseconds(1)); + Lock::GlobalLock globalWriteTry(clients[1].second.get(), + MODE_X, + Date_t::now() + Milliseconds(1), + Lock::InterruptBehavior::kThrow); ASSERT(!globalWriteTry.isLocked()); } @@ -561,8 +574,10 @@ TEST_F(DConcurrencyTestFixture, GlobalLockS_TimeoutDueToGlobalLockX) { auto clients = makeKClientsWithLockers<MMAPV1LockerImpl>(2); Lock::GlobalWrite globalWrite(clients[0].second.get()); - Lock::GlobalLock globalReadTry( - clients[1].second.get(), MODE_S, Date_t::now() + Milliseconds(1)); + Lock::GlobalLock globalReadTry(clients[1].second.get(), + MODE_S, + Date_t::now() + Milliseconds(1), + Lock::InterruptBehavior::kThrow); ASSERT(!globalReadTry.isLocked()); } @@ -571,8 +586,10 @@ TEST_F(DConcurrencyTestFixture, GlobalLockX_TimeoutDueToGlobalLockX) { auto clients = makeKClientsWithLockers<MMAPV1LockerImpl>(2); Lock::GlobalWrite globalWrite(clients[0].second.get()); - Lock::GlobalLock globalWriteTry( - clients[1].second.get(), MODE_X, Date_t::now() + Milliseconds(1)); + Lock::GlobalLock globalWriteTry(clients[1].second.get(), + MODE_X, + Date_t::now() + Milliseconds(1), + Lock::InterruptBehavior::kThrow); ASSERT(!globalWriteTry.isLocked()); } @@ -614,11 +631,11 @@ TEST_F(DConcurrencyTestFixture, GlobalLockWaitIsInterruptible) { // The main thread takes an exclusive lock, causing the spawned thread to wait when it attempts // to acquire a conflicting lock. - Lock::GlobalLock GlobalLock(opCtx1, MODE_X, Date_t::max()); + Lock::GlobalLock GlobalLock(opCtx1, MODE_X); auto result = runTaskAndKill(opCtx2, [&]() { // Killing the lock wait should throw an exception. - Lock::GlobalLock g(opCtx2, MODE_S, Date_t::max()); + Lock::GlobalLock g(opCtx2, MODE_S); }); ASSERT_THROWS_CODE(result.get(), AssertionException, ErrorCodes::Interrupted); @@ -632,18 +649,85 @@ TEST_F(DConcurrencyTestFixture, GlobalLockWaitIsInterruptibleMMAP) { // The main thread takes an exclusive lock, causing the spawned thread to wait when it attempts // to acquire a conflicting lock. - Lock::GlobalLock GlobalLock(opCtx1, MODE_X, Date_t::max()); + Lock::GlobalLock GlobalLock(opCtx1, MODE_X); // This thread attemps to acquire a conflicting lock, which will block until the first // unlocks. auto result = runTaskAndKill(opCtx2, [&]() { // Killing the lock wait should throw an exception. - Lock::GlobalLock g(opCtx2, MODE_S, Date_t::max()); + Lock::GlobalLock g(opCtx2, MODE_S); }); ASSERT_THROWS_CODE(result.get(), AssertionException, ErrorCodes::Interrupted); } +TEST_F(DConcurrencyTestFixture, GlobalLockWaitNotInterruptedWithLeaveUnlockedBehavior) { + auto clients = makeKClientsWithLockers<DefaultLockerImpl>(2); + auto opCtx1 = clients[0].second.get(); + auto opCtx2 = clients[1].second.get(); + + // The main thread takes an exclusive lock, causing the spawned thread to wait when it attempts + // to acquire a conflicting lock. + Lock::GlobalLock g1(opCtx1, MODE_X); + // Acquire this later to confirm that it stays unlocked. + boost::optional<Lock::GlobalLock> g2 = boost::none; + + // Killing the lock wait should not interrupt it, but rather leave it lock unlocked. + auto result = runTaskAndKill(opCtx2, [&]() { + g2.emplace(opCtx2, MODE_S, Date_t::max(), Lock::InterruptBehavior::kLeaveUnlocked); + }); + ASSERT(g1.isLocked()); + ASSERT(g2 != boost::none); + ASSERT(!g2->isLocked()); + + // Should not throw an exception. + result.get(); +} + +TEST_F(DConcurrencyTestFixture, GlobalLockEnqueueOnlyNotInterruptedWithLeaveUnlockedBehavior) { + auto clients = makeKClientsWithLockers<DefaultLockerImpl>(2); + auto opCtx1 = clients[0].second.get(); + + // Kill the operation before acquiring the uncontested lock. + { + stdx::lock_guard<Client> clientLock(*opCtx1->getClient()); + opCtx1->markKilled(); + } + // This should not throw or acquire the lock. + Lock::GlobalLock g1(opCtx1, + MODE_S, + Date_t::max(), + Lock::InterruptBehavior::kLeaveUnlocked, + Lock::GlobalLock::EnqueueOnly()); + ASSERT(!g1.isLocked()); +} + +TEST_F(DConcurrencyTestFixture, GlobalLockWaitForLockUntilNotInterruptedWithLeaveUnlockedBehavior) { + auto clients = makeKClientsWithLockers<DefaultLockerImpl>(2); + auto opCtx1 = clients[0].second.get(); + auto opCtx2 = clients[1].second.get(); + + // The main thread takes an exclusive lock, causing the spawned thread to wait when it attempts + // to acquire a conflicting lock. + Lock::GlobalLock g1(opCtx1, MODE_X); + // Enqueue now so waitForLockUntil can be interrupted. + Lock::GlobalLock g2(opCtx2, + MODE_S, + Date_t::max(), + Lock::InterruptBehavior::kLeaveUnlocked, + Lock::GlobalLock::EnqueueOnly()); + + ASSERT(g1.isLocked()); + ASSERT(!g2.isLocked()); + + // Killing the lock wait should not interrupt it, but rather leave it lock unlocked. + auto result = runTaskAndKill(opCtx2, [&]() { g2.waitForLockUntil(Date_t::max()); }); + + ASSERT(!g2.isLocked()); + // Should not throw an exception. + result.get(); +} + TEST_F(DConcurrencyTestFixture, DBLockWaitIsInterruptible) { auto clients = makeKClientsWithLockers<DefaultLockerImpl>(2); auto opCtx1 = clients[0].second.get(); @@ -668,13 +752,13 @@ TEST_F(DConcurrencyTestFixture, GlobalLockWaitIsNotInterruptibleWithLockGuard) { auto opCtx2 = clients[1].second.get(); // The main thread takes an exclusive lock, causing the spawned thread wait when it attempts to // acquire a conflicting lock. - boost::optional<Lock::GlobalLock> globalLock = Lock::GlobalLock(opCtx1, MODE_X, Date_t::max()); + boost::optional<Lock::GlobalLock> globalLock = Lock::GlobalLock(opCtx1, MODE_X); // Killing the lock wait should not interrupt it. auto result = runTaskAndKill(opCtx2, [&]() { UninterruptibleLockGuard noInterrupt(opCtx2->lockState()); - Lock::GlobalLock g(opCtx2, MODE_S, Date_t::max()); + Lock::GlobalLock g(opCtx2, MODE_S); }, [&]() { globalLock.reset(); }); // Should not throw an exception. @@ -1055,12 +1139,13 @@ TEST_F(DConcurrencyTestFixture, Throttling) { do { // Test that throttling will correctly handle timeouts. - Lock::GlobalRead R1(opctx1, Date_t::now()); + Lock::GlobalRead R1(opctx1, Date_t::now(), Lock::InterruptBehavior::kThrow); ASSERT(R1.isLocked()); Date_t t1 = Date_t::now(); { - Lock::GlobalRead R2(opctx2, Date_t::now() + timeoutMillis); + Lock::GlobalRead R2( + opctx2, Date_t::now() + timeoutMillis, Lock::InterruptBehavior::kThrow); ASSERT(!R2.isLocked()); } Date_t t2 = Date_t::now(); @@ -1086,10 +1171,10 @@ TEST_F(DConcurrencyTestFixture, NoThrottlingWhenNotAcquiringTickets) { opctx1->lockState()->setShouldAcquireTicket(false); // Both locks should be acquired immediately because there is no throttling. - Lock::GlobalRead R1(opctx1, Date_t::now()); + Lock::GlobalRead R1(opctx1, Date_t::now(), Lock::InterruptBehavior::kThrow); ASSERT(R1.isLocked()); - Lock::GlobalRead R2(opctx2, Date_t::now()); + Lock::GlobalRead R2(opctx2, Date_t::now(), Lock::InterruptBehavior::kThrow); ASSERT(R2.isLocked()); } @@ -1100,12 +1185,12 @@ TEST_F(DConcurrencyTestFixture, ReleaseAndReacquireTicket) { // Limit the locker to 1 ticket at a time. UseGlobalThrottling throttle(opctx1, 1); - Lock::GlobalRead R1(opctx1, Date_t::now()); + Lock::GlobalRead R1(opctx1, Date_t::now(), Lock::InterruptBehavior::kThrow); ASSERT(R1.isLocked()); { // A second Locker should not be able to acquire a ticket. - Lock::GlobalRead R2(opctx2, Date_t::now()); + Lock::GlobalRead R2(opctx2, Date_t::now(), Lock::InterruptBehavior::kThrow); ASSERT(!R2.isLocked()); } @@ -1113,7 +1198,7 @@ TEST_F(DConcurrencyTestFixture, ReleaseAndReacquireTicket) { { // Now a second Locker can acquire a ticket. - Lock::GlobalRead R2(opctx2, Date_t::now()); + Lock::GlobalRead R2(opctx2, Date_t::now(), Lock::InterruptBehavior::kThrow); ASSERT(R2.isLocked()); } @@ -1121,7 +1206,7 @@ TEST_F(DConcurrencyTestFixture, ReleaseAndReacquireTicket) { { // Now a second Locker cannot acquire a ticket. - Lock::GlobalRead R2(opctx2, Date_t::now()); + Lock::GlobalRead R2(opctx2, Date_t::now(), Lock::InterruptBehavior::kThrow); ASSERT(!R2.isLocked()); } } @@ -1130,7 +1215,7 @@ TEST_F(DConcurrencyTestFixture, LockerWithReleasedTicketCanBeUnlocked) { auto clientOpctxPairs = makeKClientsWithLockers<DefaultLockerImpl>(2); auto opctx1 = clientOpctxPairs[0].second.get(); - Lock::GlobalRead R1(opctx1, Date_t::now()); + Lock::GlobalRead R1(opctx1, Date_t::now(), Lock::InterruptBehavior::kThrow); ASSERT(R1.isLocked()); opctx1->lockState()->releaseTicket(); @@ -1143,7 +1228,7 @@ TEST_F(DConcurrencyTestFixture, TicketAcquireCanBeInterrupted) { UseGlobalThrottling throttle(opctx1, 0); // This thread should block because it cannot acquire a ticket. - auto result = runTaskAndKill(opctx1, [&] { Lock::GlobalRead R2(opctx1, Date_t::max()); }); + auto result = runTaskAndKill(opctx1, [&] { Lock::GlobalRead R2(opctx1); }); ASSERT_THROWS_CODE(result.get(), AssertionException, ErrorCodes::Interrupted); } @@ -1155,19 +1240,19 @@ TEST_F(DConcurrencyTestFixture, TicketReacquireCanBeInterrupted) { // Limit the locker to 1 ticket at a time. UseGlobalThrottling throttle(opctx1, 1); - Lock::GlobalRead R1(opctx1, Date_t::now()); + Lock::GlobalRead R1(opctx1, Date_t::now(), Lock::InterruptBehavior::kThrow); ASSERT(R1.isLocked()); { // A second Locker should not be able to acquire a ticket. - Lock::GlobalRead R2(opctx2, Date_t::now()); + Lock::GlobalRead R2(opctx2, Date_t::now(), Lock::InterruptBehavior::kThrow); ASSERT(!R2.isLocked()); } opctx1->lockState()->releaseTicket(); // Now a second Locker can acquire a ticket. - Lock::GlobalRead R2(opctx2, Date_t::now()); + Lock::GlobalRead R2(opctx2, Date_t::now(), Lock::InterruptBehavior::kThrow); ASSERT(R2.isLocked()); // This thread should block because it cannot acquire a ticket. @@ -1184,22 +1269,25 @@ TEST_F(DConcurrencyTestFixture, GlobalLockInInterruptedContextThrowsEvenWhenUnco boost::optional<Lock::GlobalRead> globalReadLock; ASSERT_THROWS_CODE( - globalReadLock.emplace(opCtx, Date_t::now()), AssertionException, ErrorCodes::Interrupted); + globalReadLock.emplace(opCtx, Date_t::now(), Lock::InterruptBehavior::kThrow), + AssertionException, + ErrorCodes::Interrupted); } TEST_F(DConcurrencyTestFixture, GlobalLockInInterruptedContextThrowsEvenAcquiringRecursively) { auto clients = makeKClientsWithLockers<DefaultLockerImpl>(1); auto opCtx = clients[0].second.get(); - Lock::GlobalWrite globalWriteLock(opCtx, Date_t::now()); + Lock::GlobalWrite globalWriteLock(opCtx, Date_t::now(), Lock::InterruptBehavior::kThrow); opCtx->markKilled(); { boost::optional<Lock::GlobalWrite> recursiveGlobalWriteLock; - ASSERT_THROWS_CODE(recursiveGlobalWriteLock.emplace(opCtx, Date_t::now()), - AssertionException, - ErrorCodes::Interrupted); + ASSERT_THROWS_CODE( + recursiveGlobalWriteLock.emplace(opCtx, Date_t::now(), Lock::InterruptBehavior::kThrow), + AssertionException, + ErrorCodes::Interrupted); } } @@ -1210,7 +1298,8 @@ TEST_F(DConcurrencyTestFixture, GlobalLockInInterruptedContextRespectsUninterrup opCtx->markKilled(); UninterruptibleLockGuard noInterrupt(opCtx->lockState()); - Lock::GlobalRead globalReadLock(opCtx, Date_t::now()); // Does not throw. + Lock::GlobalRead globalReadLock( + opCtx, Date_t::now(), Lock::InterruptBehavior::kThrow); // Does not throw. } TEST_F(DConcurrencyTestFixture, DBLockInInterruptedContextThrowsEvenWhenUncontested) { @@ -1275,7 +1364,7 @@ TEST_F(DConcurrencyTestFixture, DBLockTimeoutDueToGlobalLock) { const Milliseconds timeoutMillis = Milliseconds(1500); - Lock::GlobalLock G1(opctx1, MODE_X, Date_t::max()); + Lock::GlobalLock G1(opctx1, MODE_X); ASSERT(G1.isLocked()); Date_t t1 = Date_t::now(); @@ -1317,11 +1406,15 @@ TEST_F(DConcurrencyTestFixture, CompatibleFirstWithSXIS) { // Build a queue of MODE_S <- MODE_X <- MODE_IS, with MODE_S granted. Lock::GlobalRead lockS(opctx1); ASSERT(lockS.isLocked()); - Lock::GlobalLock lockX(opctx2, MODE_X, Date_t::max(), Lock::GlobalLock::EnqueueOnly()); + Lock::GlobalLock lockX(opctx2, + MODE_X, + Date_t::max(), + Lock::InterruptBehavior::kThrow, + Lock::GlobalLock::EnqueueOnly()); ASSERT(!lockX.isLocked()); // A MODE_IS should be granted due to compatibleFirst policy. - Lock::GlobalLock lockIS(opctx3, MODE_IS, Date_t::now()); + Lock::GlobalLock lockIS(opctx3, MODE_IS, Date_t::now(), Lock::InterruptBehavior::kThrow); ASSERT(lockIS.isLocked()); lockX.waitForLockUntil(Date_t::now()); @@ -1341,11 +1434,23 @@ TEST_F(DConcurrencyTestFixture, CompatibleFirstWithXSIXIS) { lockX.emplace(opctx1); ASSERT(lockX->isLocked()); boost::optional<Lock::GlobalLock> lockS; - lockS.emplace(opctx2, MODE_S, Date_t::max(), Lock::GlobalLock::EnqueueOnly()); + lockS.emplace(opctx2, + MODE_S, + Date_t::max(), + Lock::InterruptBehavior::kThrow, + Lock::GlobalLock::EnqueueOnly()); ASSERT(!lockS->isLocked()); - Lock::GlobalLock lockIX(opctx3, MODE_IX, Date_t::max(), Lock::GlobalLock::EnqueueOnly()); + Lock::GlobalLock lockIX(opctx3, + MODE_IX, + Date_t::max(), + Lock::InterruptBehavior::kThrow, + Lock::GlobalLock::EnqueueOnly()); ASSERT(!lockIX.isLocked()); - Lock::GlobalLock lockIS(opctx4, MODE_IS, Date_t::max(), Lock::GlobalLock::EnqueueOnly()); + Lock::GlobalLock lockIS(opctx4, + MODE_IS, + Date_t::max(), + Lock::InterruptBehavior::kThrow, + Lock::GlobalLock::EnqueueOnly()); ASSERT(!lockIS.isLocked()); @@ -1378,17 +1483,33 @@ TEST_F(DConcurrencyTestFixture, CompatibleFirstWithXSXIXIS) { ASSERT(lockXgranted->isLocked()); boost::optional<Lock::GlobalLock> lockX; - lockX.emplace(opctx3, MODE_X, Date_t::max(), Lock::GlobalLock::EnqueueOnly()); + lockX.emplace(opctx3, + MODE_X, + Date_t::max(), + Lock::InterruptBehavior::kThrow, + Lock::GlobalLock::EnqueueOnly()); ASSERT(!lockX->isLocked()); // Now request MODE_S: it will be first in the pending list due to EnqueueAtFront policy. boost::optional<Lock::GlobalLock> lockS; - lockS.emplace(opctx2, MODE_S, Date_t::max(), Lock::GlobalLock::EnqueueOnly()); + lockS.emplace(opctx2, + MODE_S, + Date_t::max(), + Lock::InterruptBehavior::kThrow, + Lock::GlobalLock::EnqueueOnly()); ASSERT(!lockS->isLocked()); - Lock::GlobalLock lockIX(opctx4, MODE_IX, Date_t::max(), Lock::GlobalLock::EnqueueOnly()); + Lock::GlobalLock lockIX(opctx4, + MODE_IX, + Date_t::max(), + Lock::InterruptBehavior::kThrow, + Lock::GlobalLock::EnqueueOnly()); ASSERT(!lockIX.isLocked()); - Lock::GlobalLock lockIS(opctx5, MODE_IS, Date_t::max(), Lock::GlobalLock::EnqueueOnly()); + Lock::GlobalLock lockIS(opctx5, + MODE_IS, + Date_t::max(), + Lock::InterruptBehavior::kThrow, + Lock::GlobalLock::EnqueueOnly()); ASSERT(!lockIS.isLocked()); @@ -1437,7 +1558,8 @@ TEST_F(DConcurrencyTestFixture, CompatibleFirstStress) { OperationContext* opCtx = clientOpctxPairs[0].second.get(); for (int iters = 0; (t.micros() < endTime); iters++) { busyWait(0, iters % 20); - Lock::GlobalRead readLock(opCtx, Date_t::now() + Milliseconds(iters % 2)); + Lock::GlobalRead readLock( + opCtx, Date_t::now() + Milliseconds(iters % 2), Lock::InterruptBehavior::kThrow); if (!readLock.isLocked()) { timeoutCount[0]++; continue; @@ -1470,6 +1592,7 @@ TEST_F(DConcurrencyTestFixture, CompatibleFirstStress) { lock.emplace(opCtx, iters % 20 ? MODE_IS : MODE_S, Date_t::now(), + Lock::InterruptBehavior::kThrow, Lock::GlobalLock::EnqueueOnly()); // If thread 0 is holding the MODE_S lock while we tried to acquire a // MODE_IS or MODE_S lock, the CompatibleFirst policy guarantees success. @@ -1480,18 +1603,25 @@ TEST_F(DConcurrencyTestFixture, CompatibleFirstStress) { } case 5: busyWait(threadId, iters % 150); - lock.emplace(opCtx, MODE_X, Date_t::now() + Milliseconds(iters % 2)); + lock.emplace(opCtx, + MODE_X, + Date_t::now() + Milliseconds(iters % 2), + Lock::InterruptBehavior::kThrow); busyWait(threadId, iters % 10); break; case 6: lock.emplace(opCtx, iters % 25 ? MODE_IX : MODE_S, - Date_t::now() + Milliseconds(iters % 2)); + Date_t::now() + Milliseconds(iters % 2), + Lock::InterruptBehavior::kThrow); busyWait(threadId, iters % 100); break; case 7: busyWait(threadId, iters % 100); - lock.emplace(opCtx, iters % 20 ? MODE_IS : MODE_X, Date_t::now()); + lock.emplace(opCtx, + iters % 20 ? MODE_IS : MODE_X, + Date_t::now(), + Lock::InterruptBehavior::kThrow); break; default: MONGO_UNREACHABLE; @@ -1534,12 +1664,12 @@ TEST_F(DConcurrencyTestFixture, TestGlobalLockAbandonsSnapshotWhenNotInWriteUnit WriteUnitOfWork::RecoveryUnitState::kNotInUnitOfWork); { - Lock::GlobalLock gw1(opCtx, MODE_IS, Date_t::now()); + Lock::GlobalLock gw1(opCtx, MODE_IS, Date_t::now(), Lock::InterruptBehavior::kThrow); ASSERT(gw1.isLocked()); ASSERT(recovUnitBorrowed->activeTransaction); { - Lock::GlobalLock gw2(opCtx, MODE_S, Date_t::now()); + Lock::GlobalLock gw2(opCtx, MODE_S, Date_t::now(), Lock::InterruptBehavior::kThrow); ASSERT(gw2.isLocked()); ASSERT(recovUnitBorrowed->activeTransaction); } @@ -1560,12 +1690,12 @@ TEST_F(DConcurrencyTestFixture, TestGlobalLockDoesNotAbandonSnapshotWhenInWriteU opCtx->lockState()->beginWriteUnitOfWork(); { - Lock::GlobalLock gw1(opCtx, MODE_IX, Date_t::now()); + Lock::GlobalLock gw1(opCtx, MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow); ASSERT(gw1.isLocked()); ASSERT(recovUnitBorrowed->activeTransaction); { - Lock::GlobalLock gw2(opCtx, MODE_X, Date_t::now()); + Lock::GlobalLock gw2(opCtx, MODE_X, Date_t::now(), Lock::InterruptBehavior::kThrow); ASSERT(gw2.isLocked()); ASSERT(recovUnitBorrowed->activeTransaction); } diff --git a/src/mongo/db/repl/do_txn.cpp b/src/mongo/db/repl/do_txn.cpp index cfb968c71e6..52a1f36fb31 100644 --- a/src/mongo/db/repl/do_txn.cpp +++ b/src/mongo/db/repl/do_txn.cpp @@ -289,7 +289,7 @@ Status doTxn(OperationContext* opCtx, // Acquire global lock in IX mode so that the replication state check will remain valid. - Lock::GlobalLock globalLock(opCtx, MODE_IX, Date_t::max()); + Lock::GlobalLock globalLock(opCtx, MODE_IX); auto replCoord = repl::ReplicationCoordinator::get(opCtx); bool userInitiatedWritesAndNotPrimary = diff --git a/src/mongo/db/repl/noop_writer.cpp b/src/mongo/db/repl/noop_writer.cpp index 12af44fb559..27a24c011ec 100644 --- a/src/mongo/db/repl/noop_writer.cpp +++ b/src/mongo/db/repl/noop_writer.cpp @@ -141,12 +141,10 @@ void NoopWriter::stopWritingPeriodicNoops() { } void NoopWriter::_writeNoop(OperationContext* opCtx) { - // Ensure that we don't trigger an exception when attempting to take locks. - UninterruptibleLockGuard noInterrupt(opCtx->lockState()); - // Use GlobalLock + lockMMAPV1Flush instead of DBLock to allow return when the lock is not // available. It may happen when the primary steps down and a shared global lock is acquired. - Lock::GlobalLock lock(opCtx, MODE_IX, Date_t::now() + Milliseconds(1)); + Lock::GlobalLock lock( + opCtx, MODE_IX, Date_t::now() + Milliseconds(1), Lock::InterruptBehavior::kLeaveUnlocked); if (!lock.isLocked()) { LOG(1) << "Global lock is not available skipping noopWrite"; return; diff --git a/src/mongo/db/repl/replication_coordinator_impl.cpp b/src/mongo/db/repl/replication_coordinator_impl.cpp index 1ecb7e6fee2..4f3e7d58526 100644 --- a/src/mongo/db/repl/replication_coordinator_impl.cpp +++ b/src/mongo/db/repl/replication_coordinator_impl.cpp @@ -1606,8 +1606,11 @@ Status ReplicationCoordinatorImpl::stepDown(OperationContext* opCtx, return {ErrorCodes::NotMaster, "not primary so can't step down"}; } - auto globalLock = stdx::make_unique<Lock::GlobalLock>( - opCtx, MODE_X, stepDownUntil, Lock::GlobalLock::EnqueueOnly()); + auto globalLock = stdx::make_unique<Lock::GlobalLock>(opCtx, + MODE_X, + stepDownUntil, + Lock::InterruptBehavior::kThrow, + Lock::GlobalLock::EnqueueOnly()); // We've requested the global exclusive lock which will stop new operations from coming in, // but existing operations could take a long time to finish, so kill all user operations @@ -1718,7 +1721,7 @@ Status ReplicationCoordinatorImpl::stepDown(OperationContext* opCtx, // to acquire it now. For the same reason, we also disable lock acquisition // interruption, to guarantee that we get the lock eventually. UninterruptibleLockGuard noInterrupt(opCtx->lockState()); - globalLock.reset(new Lock::GlobalLock(opCtx, MODE_X, Date_t::max())); + globalLock.reset(new Lock::GlobalLock(opCtx, MODE_X)); invariant(globalLock->isLocked()); lk.lock(); }); diff --git a/src/mongo/db/repl/replication_coordinator_impl_elect_v1_test.cpp b/src/mongo/db/repl/replication_coordinator_impl_elect_v1_test.cpp index fd48c7c71cf..df475771ed0 100644 --- a/src/mongo/db/repl/replication_coordinator_impl_elect_v1_test.cpp +++ b/src/mongo/db/repl/replication_coordinator_impl_elect_v1_test.cpp @@ -2271,7 +2271,7 @@ TEST_F(PrimaryCatchUpTest, PrimaryDoesNotNeedToCatchUp) { ASSERT_EQ(1, countLogLinesContaining("Caught up to the latest optime known via heartbeats")); auto opCtx = makeOperationContext(); signalDrainComplete(opCtx.get()); - Lock::GlobalLock lock(opCtx.get(), MODE_IX, Date_t::max()); + Lock::GlobalLock lock(opCtx.get(), MODE_IX); ASSERT_TRUE(getReplCoord()->canAcceptWritesForDatabase(opCtx.get(), "test")); } @@ -2294,7 +2294,7 @@ TEST_F(PrimaryCatchUpTest, CatchupSucceeds) { ASSERT_EQUALS(1, countLogLinesContaining("Caught up to the latest known optime successfully")); auto opCtx = makeOperationContext(); signalDrainComplete(opCtx.get()); - Lock::GlobalLock lock(opCtx.get(), MODE_IX, Date_t::max()); + Lock::GlobalLock lock(opCtx.get(), MODE_IX); ASSERT_TRUE(getReplCoord()->canAcceptWritesForDatabase(opCtx.get(), "test")); } @@ -2314,7 +2314,7 @@ TEST_F(PrimaryCatchUpTest, CatchupTimeout) { ASSERT_EQUALS(1, countLogLinesContaining("Catchup timed out")); auto opCtx = makeOperationContext(); signalDrainComplete(opCtx.get()); - Lock::GlobalLock lock(opCtx.get(), MODE_IX, Date_t::max()); + Lock::GlobalLock lock(opCtx.get(), MODE_IX); ASSERT_TRUE(getReplCoord()->canAcceptWritesForDatabase(opCtx.get(), "test")); } @@ -2339,7 +2339,7 @@ TEST_F(PrimaryCatchUpTest, CannotSeeAllNodes) { ASSERT_EQ(1, countLogLinesContaining("Caught up to the latest optime known via heartbeats")); auto opCtx = makeOperationContext(); signalDrainComplete(opCtx.get()); - Lock::GlobalLock lock(opCtx.get(), MODE_IX, Date_t::max()); + Lock::GlobalLock lock(opCtx.get(), MODE_IX); ASSERT_TRUE(getReplCoord()->canAcceptWritesForDatabase(opCtx.get(), "test")); } @@ -2364,7 +2364,7 @@ TEST_F(PrimaryCatchUpTest, HeartbeatTimeout) { ASSERT_EQ(1, countLogLinesContaining("Caught up to the latest optime known via heartbeats")); auto opCtx = makeOperationContext(); signalDrainComplete(opCtx.get()); - Lock::GlobalLock lock(opCtx.get(), MODE_IX, Date_t::max()); + Lock::GlobalLock lock(opCtx.get(), MODE_IX); ASSERT_TRUE(getReplCoord()->canAcceptWritesForDatabase(opCtx.get(), "test")); } @@ -2387,7 +2387,7 @@ TEST_F(PrimaryCatchUpTest, PrimaryStepsDownBeforeHeartbeatRefreshing) { ASSERT_EQUALS(0, countLogLinesContaining("Caught up to the latest")); ASSERT_EQUALS(0, countLogLinesContaining("Catchup timed out")); auto opCtx = makeOperationContext(); - Lock::GlobalLock lock(opCtx.get(), MODE_IX, Date_t::max()); + Lock::GlobalLock lock(opCtx.get(), MODE_IX); ASSERT_FALSE(getReplCoord()->canAcceptWritesForDatabase(opCtx.get(), "test")); } @@ -2416,7 +2416,7 @@ TEST_F(PrimaryCatchUpTest, PrimaryStepsDownDuringCatchUp) { ASSERT_EQUALS(0, countLogLinesContaining("Caught up to the latest")); ASSERT_EQUALS(0, countLogLinesContaining("Catchup timed out")); auto opCtx = makeOperationContext(); - Lock::GlobalLock lock(opCtx.get(), MODE_IX, Date_t::max()); + Lock::GlobalLock lock(opCtx.get(), MODE_IX); ASSERT_FALSE(getReplCoord()->canAcceptWritesForDatabase(opCtx.get(), "test")); } @@ -2459,11 +2459,11 @@ TEST_F(PrimaryCatchUpTest, PrimaryStepsDownDuringDrainMode) { ASSERT(replCoord->getApplierState() == ApplierState::Draining); auto opCtx = makeOperationContext(); { - Lock::GlobalLock lock(opCtx.get(), MODE_IX, Date_t::max()); + Lock::GlobalLock lock(opCtx.get(), MODE_IX); ASSERT_FALSE(replCoord->canAcceptWritesForDatabase(opCtx.get(), "test")); } signalDrainComplete(opCtx.get()); - Lock::GlobalLock lock(opCtx.get(), MODE_IX, Date_t::max()); + Lock::GlobalLock lock(opCtx.get(), MODE_IX); ASSERT(replCoord->getApplierState() == ApplierState::Stopped); ASSERT_TRUE(replCoord->canAcceptWritesForDatabase(opCtx.get(), "test")); } @@ -2527,7 +2527,7 @@ TEST_F(PrimaryCatchUpTest, FreshestNodeBecomesAvailableLater) { ASSERT_EQ(1, countLogLinesContaining("Caught up to the latest")); auto opCtx = makeOperationContext(); signalDrainComplete(opCtx.get()); - Lock::GlobalLock lock(opCtx.get(), MODE_IX, Date_t::max()); + Lock::GlobalLock lock(opCtx.get(), MODE_IX); ASSERT_TRUE(getReplCoord()->canAcceptWritesForDatabase(opCtx.get(), "test")); } @@ -2572,7 +2572,7 @@ TEST_F(PrimaryCatchUpTest, InfiniteTimeoutAndAbort) { ASSERT_EQUALS(0, countLogLinesContaining("Catchup timed out")); auto opCtx = makeOperationContext(); signalDrainComplete(opCtx.get()); - Lock::GlobalLock lock(opCtx.get(), MODE_IX, Date_t::max()); + Lock::GlobalLock lock(opCtx.get(), MODE_IX); ASSERT_TRUE(getReplCoord()->canAcceptWritesForDatabase(opCtx.get(), "test")); } @@ -2586,7 +2586,7 @@ TEST_F(PrimaryCatchUpTest, ZeroTimeout) { ASSERT_EQUALS(1, countLogLinesContaining("Skipping primary catchup")); auto opCtx = makeOperationContext(); signalDrainComplete(opCtx.get()); - Lock::GlobalLock lock(opCtx.get(), MODE_IX, Date_t::max()); + Lock::GlobalLock lock(opCtx.get(), MODE_IX); ASSERT_TRUE(getReplCoord()->canAcceptWritesForDatabase(opCtx.get(), "test")); } diff --git a/src/mongo/db/repl/replication_coordinator_impl_heartbeat.cpp b/src/mongo/db/repl/replication_coordinator_impl_heartbeat.cpp index dd731fdcacb..849fc58cdc1 100644 --- a/src/mongo/db/repl/replication_coordinator_impl_heartbeat.cpp +++ b/src/mongo/db/repl/replication_coordinator_impl_heartbeat.cpp @@ -394,8 +394,11 @@ void ReplicationCoordinatorImpl::_stepDownFinish( } auto opCtx = cc().makeOperationContext(); - Lock::GlobalLock globalExclusiveLock{ - opCtx.get(), MODE_X, Date_t::max(), Lock::GlobalLock::EnqueueOnly()}; + Lock::GlobalLock globalExclusiveLock{opCtx.get(), + MODE_X, + Date_t::max(), + Lock::InterruptBehavior::kThrow, + Lock::GlobalLock::EnqueueOnly()}; _externalState->killAllUserOperations(opCtx.get()); globalExclusiveLock.waitForLockUntil(Date_t::max()); invariant(globalExclusiveLock.isLocked()); diff --git a/src/mongo/db/repl/replication_coordinator_impl_test.cpp b/src/mongo/db/repl/replication_coordinator_impl_test.cpp index 82ae756925d..41b572c0ef0 100644 --- a/src/mongo/db/repl/replication_coordinator_impl_test.cpp +++ b/src/mongo/db/repl/replication_coordinator_impl_test.cpp @@ -2161,7 +2161,7 @@ TEST_F(StepDownTest, InterruptingStepDownCommandRestoresWriteAvailability) { // This is the important check, that we didn't accidentally step back up when aborting the // stepdown command attempt. const auto opCtx = makeOperationContext(); - Lock::GlobalLock lock(opCtx.get(), MODE_IX, Date_t::max()); + Lock::GlobalLock lock(opCtx.get(), MODE_IX); ASSERT_TRUE(getReplCoord()->canAcceptWritesForDatabase(opCtx.get(), "admin")); } @@ -2217,7 +2217,7 @@ TEST_F(StepDownTest, InterruptingAfterUnconditionalStepdownDoesNotRestoreWriteAv // This is the important check, that we didn't accidentally step back up when aborting the // stepdown command attempt. - Lock::GlobalLock lock(opCtx.get(), MODE_IX, Date_t::max()); + Lock::GlobalLock lock(opCtx.get(), MODE_IX); ASSERT_FALSE(getReplCoord()->canAcceptWritesForDatabase(opCtx.get(), "admin")); } diff --git a/src/mongo/db/repl/rollback_impl.cpp b/src/mongo/db/repl/rollback_impl.cpp index e92b0c9ab2d..ed65f5a0150 100644 --- a/src/mongo/db/repl/rollback_impl.cpp +++ b/src/mongo/db/repl/rollback_impl.cpp @@ -302,7 +302,7 @@ Status RollbackImpl::_awaitBgIndexCompletion(OperationContext* opCtx) { StorageEngine* storageEngine = opCtx->getServiceContext()->getGlobalStorageEngine(); std::vector<std::string> dbs; { - Lock::GlobalLock lk(opCtx, MODE_IS, Date_t::max()); + Lock::GlobalLock lk(opCtx, MODE_IS); storageEngine->listDatabases(&dbs); } diff --git a/src/mongo/db/session_test.cpp b/src/mongo/db/session_test.cpp index ea040fffdd7..ab0c4a4e32a 100644 --- a/src/mongo/db/session_test.cpp +++ b/src/mongo/db/session_test.cpp @@ -627,7 +627,7 @@ TEST_F(SessionTest, StashAndUnstashResources) { ASSERT(opCtx()->getWriteUnitOfWork()); // Take a lock. This is expected in order to stash resources. - Lock::GlobalRead lk(opCtx(), Date_t::now()); + Lock::GlobalRead lk(opCtx(), Date_t::now(), Lock::InterruptBehavior::kThrow); ASSERT(lk.isLocked()); // Stash resources. The original Locker and RecoveryUnit now belong to the stash. @@ -679,7 +679,7 @@ TEST_F(SessionTest, ReportStashedResources) { ASSERT(opCtx()->getWriteUnitOfWork()); // Take a lock. This is expected in order to stash resources. - Lock::GlobalRead lk(opCtx(), Date_t::now()); + Lock::GlobalRead lk(opCtx(), Date_t::now(), Lock::InterruptBehavior::kThrow); ASSERT(lk.isLocked()); // Build a BSONObj containing the details which we expect to see reported when we call @@ -762,7 +762,7 @@ TEST_F(SessionTest, AutocommitRequiredOnEveryTxnOp) { // We must have stashed transaction resources to do a second operation on the transaction. session.unstashTransactionResources(opCtx(), "insert"); // The transaction machinery cannot store an empty locker. - { Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now()); } + { Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow); } session.stashTransactionResources(opCtx()); // Autocommit should be set to false @@ -864,7 +864,7 @@ TEST_F(SessionTest, SameTransactionPreservesStoredStatements) { session.addTransactionOperation(opCtx(), operation); ASSERT_BSONOBJ_EQ(operation.toBSON(), session.transactionOperationsForTest()[0].toBSON()); // The transaction machinery cannot store an empty locker. - { Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now()); } + { Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow); } session.stashTransactionResources(opCtx()); // Check the transaction operations before re-opening the transaction. @@ -891,7 +891,7 @@ TEST_F(SessionTest, AbortClearsStoredStatements) { session.addTransactionOperation(opCtx(), operation); ASSERT_BSONOBJ_EQ(operation.toBSON(), session.transactionOperationsForTest()[0].toBSON()); // The transaction machinery cannot store an empty locker. - { Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now()); } + { Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow); } session.stashTransactionResources(opCtx()); session.abortArbitraryTransaction(opCtx(), kKillCursors); ASSERT_TRUE(session.transactionOperationsForTest().empty()); @@ -913,7 +913,7 @@ TEST_F(SessionTest, EmptyTransactionCommit) { session.beginOrContinueTxn(opCtx(), txnNum, false, true); session.unstashTransactionResources(opCtx(), "commitTransaction"); // The transaction machinery cannot store an empty locker. - Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now()); + Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow); session.commitTransaction(opCtx()); session.stashTransactionResources(opCtx()); ASSERT_TRUE(session.transactionIsCommitted()); @@ -934,7 +934,7 @@ TEST_F(SessionTest, EmptyTransactionAbort) { session.beginOrContinueTxn(opCtx(), txnNum, false, true); session.unstashTransactionResources(opCtx(), "abortTransaction"); // The transaction machinery cannot store an empty locker. - { Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now()); } + { Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow); } session.stashTransactionResources(opCtx()); session.abortArbitraryTransaction(opCtx(), kKillCursors); ASSERT_TRUE(session.transactionIsAborted()); @@ -975,7 +975,7 @@ TEST_F(SessionTest, ConcurrencyOfUnstashAndMigration) { session.unstashTransactionResources(opCtx(), "insert"); // The transaction machinery cannot store an empty locker. - { Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now()); } + { Lock::GlobalLock lk(opCtx(), MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow); } auto operation = repl::OplogEntry::makeInsertOperation(kNss, kUUID, BSON("TestValue" << 0)); session.addTransactionOperation(opCtx(), operation); session.stashTransactionResources(opCtx()); diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_server_status.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_server_status.cpp index 395c9fcf0e5..3a58b4f91d9 100644 --- a/src/mongo/db/storage/wiredtiger/wiredtiger_server_status.cpp +++ b/src/mongo/db/storage/wiredtiger/wiredtiger_server_status.cpp @@ -57,7 +57,7 @@ bool WiredTigerServerStatusSection::includeByDefault() const { BSONObj WiredTigerServerStatusSection::generateSection(OperationContext* opCtx, const BSONElement& configElement) const { - Lock::GlobalLock lk(opCtx, LockMode::MODE_IS, Date_t::max()); + Lock::GlobalLock lk(opCtx, LockMode::MODE_IS); // The session does not open a transaction here as one is not needed and opening one would // mean that execution could become blocked when a new transaction cannot be allocated |