summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLouis Williams <louis.williams@mongodb.com>2018-03-28 13:39:45 -0400
committerLouis Williams <louis.williams@mongodb.com>2018-04-30 11:50:37 -0400
commit07d7a7095a7ebb116b0d02a4ac396620710e9e77 (patch)
treef17d474d953d49cc1d5d130a8a6c38fd975d10a2
parent3d43d9420c12c2f47d614fc6f2546cf80742817e (diff)
downloadmongo-07d7a7095a7ebb116b0d02a4ac396620710e9e77.tar.gz
SERVER-33674 Require GlobalLocks with deadlines to specify interrupt behavior when interrupted
-rw-r--r--src/mongo/client/embedded/embedded.cpp2
-rw-r--r--src/mongo/db/catalog/coll_mod.cpp2
-rw-r--r--src/mongo/db/catalog_raii_test.cpp6
-rw-r--r--src/mongo/db/commands/fsync.cpp2
-rw-r--r--src/mongo/db/commands/list_databases.cpp2
-rw-r--r--src/mongo/db/commands/oplog_note.cpp3
-rw-r--r--src/mongo/db/commands/restart_catalog_command.cpp2
-rw-r--r--src/mongo/db/commands/set_feature_compatibility_version_command.cpp4
-rw-r--r--src/mongo/db/commands/snapshot_management.cpp4
-rw-r--r--src/mongo/db/concurrency/d_concurrency.cpp44
-rw-r--r--src/mongo/db/concurrency/d_concurrency.h39
-rw-r--r--src/mongo/db/concurrency/d_concurrency_test.cpp256
-rw-r--r--src/mongo/db/repl/do_txn.cpp2
-rw-r--r--src/mongo/db/repl/noop_writer.cpp6
-rw-r--r--src/mongo/db/repl/replication_coordinator_impl.cpp9
-rw-r--r--src/mongo/db/repl/replication_coordinator_impl_elect_v1_test.cpp24
-rw-r--r--src/mongo/db/repl/replication_coordinator_impl_heartbeat.cpp7
-rw-r--r--src/mongo/db/repl/replication_coordinator_impl_test.cpp4
-rw-r--r--src/mongo/db/repl/rollback_impl.cpp2
-rw-r--r--src/mongo/db/session_test.cpp16
-rw-r--r--src/mongo/db/storage/wiredtiger/wiredtiger_server_status.cpp2
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