summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorJudah Schvimer <judah@mongodb.com>2019-04-17 14:26:44 -0400
committerJudah Schvimer <judah@mongodb.com>2019-04-17 14:26:44 -0400
commit7a9f240519c55cc6af45e4783bfd54d81a79d1e0 (patch)
treeb9e3f0c8beb0c9557e94661092ad6244fa782a32 /src/mongo
parentd7fb557f6fc6d486fa7107a8f64342caf552eeb4 (diff)
downloadmongo-7a9f240519c55cc6af45e4783bfd54d81a79d1e0.tar.gz
SERVER-40069 Fix global lock tracking for txns
Includes SERVER-40084, stores global lock acquisition flag in an atomic.
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/db/concurrency/SConscript1
-rw-r--r--src/mongo/db/concurrency/d_concurrency.cpp3
-rw-r--r--src/mongo/db/concurrency/d_concurrency.h6
-rw-r--r--src/mongo/db/concurrency/d_concurrency_test.cpp83
-rw-r--r--src/mongo/db/concurrency/global_lock_acquisition_tracker.cpp54
-rw-r--r--src/mongo/db/concurrency/global_lock_acquisition_tracker.h75
-rw-r--r--src/mongo/db/concurrency/lock_state.cpp20
-rw-r--r--src/mongo/db/concurrency/lock_state.h15
-rw-r--r--src/mongo/db/concurrency/locker.h23
-rw-r--r--src/mongo/db/concurrency/locker_noop.h14
-rw-r--r--src/mongo/db/curop.cpp5
-rw-r--r--src/mongo/db/repl/replication_coordinator_impl.cpp7
-rw-r--r--src/mongo/db/service_entry_point_common.cpp1
-rw-r--r--src/mongo/db/service_entry_point_mongod.cpp14
14 files changed, 131 insertions, 190 deletions
diff --git a/src/mongo/db/concurrency/SConscript b/src/mongo/db/concurrency/SConscript
index cd43fc4fb44..f8e5c142eec 100644
--- a/src/mongo/db/concurrency/SConscript
+++ b/src/mongo/db/concurrency/SConscript
@@ -40,7 +40,6 @@ env.Library(
target='lock_manager',
source=[
'd_concurrency.cpp',
- 'global_lock_acquisition_tracker.cpp',
'lock_manager.cpp',
'lock_state.cpp',
'lock_stats.cpp',
diff --git a/src/mongo/db/concurrency/d_concurrency.cpp b/src/mongo/db/concurrency/d_concurrency.cpp
index ffc24144b7f..0debd7b7ce8 100644
--- a/src/mongo/db/concurrency/d_concurrency.cpp
+++ b/src/mongo/db/concurrency/d_concurrency.cpp
@@ -37,7 +37,6 @@
#include <vector>
#include "mongo/db/concurrency/flow_control_ticketholder.h"
-#include "mongo/db/concurrency/global_lock_acquisition_tracker.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/service_context.h"
#include "mongo/stdx/memory.h"
@@ -218,7 +217,7 @@ void Lock::GlobalLock::waitForLockUntil(Date_t deadline) {
const ResourceId globalResId(RESOURCE_GLOBAL, ResourceId::SINGLETON_GLOBAL);
auto lockMode = _opCtx->lockState()->getLockMode(globalResId);
- GlobalLockAcquisitionTracker::get(_opCtx).setGlobalLockModeBit(lockMode);
+ _opCtx->lockState()->setGlobalLockTakenInMode(lockMode);
}
void Lock::GlobalLock::_unlock() {
diff --git a/src/mongo/db/concurrency/d_concurrency.h b/src/mongo/db/concurrency/d_concurrency.h
index 82823093d5f..7300824373c 100644
--- a/src/mongo/db/concurrency/d_concurrency.h
+++ b/src/mongo/db/concurrency/d_concurrency.h
@@ -211,8 +211,7 @@ public:
* Enqueues lock but does not block on lock acquisition.
* Call waitForLockUntil() to complete locking process.
*
- * Does not set that the global lock was taken on the GlobalLockAcquisitionTracker. Call
- * waitForLockUntil to do so.
+ * Does not set Locker::setGlobalLockTakenInMode(). Call waitForLockUntil to do so.
*/
GlobalLock(OperationContext* opCtx,
LockMode lockMode,
@@ -236,8 +235,7 @@ public:
}
/**
- * Waits for lock to be granted. Sets that the global lock was taken on the
- * GlobalLockAcquisitionTracker.
+ * Waits for lock to be granted. Sets Locker::setGlobalLockTakenInMode().
*/
void waitForLockUntil(Date_t deadline);
diff --git a/src/mongo/db/concurrency/d_concurrency_test.cpp b/src/mongo/db/concurrency/d_concurrency_test.cpp
index 9043e00e4b7..7e2b70472bd 100644
--- a/src/mongo/db/concurrency/d_concurrency_test.cpp
+++ b/src/mongo/db/concurrency/d_concurrency_test.cpp
@@ -36,7 +36,6 @@
#include <vector>
#include "mongo/db/concurrency/d_concurrency.h"
-#include "mongo/db/concurrency/global_lock_acquisition_tracker.h"
#include "mongo/db/concurrency/lock_manager_test_help.h"
#include "mongo/db/concurrency/replication_state_transition_lock_guard.h"
#include "mongo/db/concurrency/write_conflict_exception.h"
@@ -472,65 +471,65 @@ TEST_F(DConcurrencyTestFixture, RSTLmodeX_Timeout) {
TEST_F(DConcurrencyTestFixture, GlobalLockXSetsGlobalWriteLockedOnOperationContext) {
auto clients = makeKClientsWithLockers(1);
auto opCtx = clients[0].second.get();
- ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalWriteLocked());
+ ASSERT_FALSE(opCtx->lockState()->wasGlobalLockTakenForWrite());
{
Lock::GlobalLock globalWrite(opCtx, MODE_X, Date_t::now(), Lock::InterruptBehavior::kThrow);
ASSERT(globalWrite.isLocked());
}
- ASSERT_TRUE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalWriteLocked());
+ ASSERT_TRUE(opCtx->lockState()->wasGlobalLockTakenForWrite());
}
TEST_F(DConcurrencyTestFixture, GlobalLockIXSetsGlobalWriteLockedOnOperationContext) {
auto clients = makeKClientsWithLockers(1);
auto opCtx = clients[0].second.get();
- ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalWriteLocked());
+ ASSERT_FALSE(opCtx->lockState()->wasGlobalLockTakenForWrite());
{
Lock::GlobalLock globalWrite(
opCtx, MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow);
ASSERT(globalWrite.isLocked());
}
- ASSERT_TRUE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalWriteLocked());
+ ASSERT_TRUE(opCtx->lockState()->wasGlobalLockTakenForWrite());
}
TEST_F(DConcurrencyTestFixture, GlobalLockSDoesNotSetGlobalWriteLockedOnOperationContext) {
auto clients = makeKClientsWithLockers(1);
auto opCtx = clients[0].second.get();
- ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalWriteLocked());
+ ASSERT_FALSE(opCtx->lockState()->wasGlobalLockTakenForWrite());
{
Lock::GlobalLock globalRead(opCtx, MODE_S, Date_t::now(), Lock::InterruptBehavior::kThrow);
ASSERT(globalRead.isLocked());
}
- ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalWriteLocked());
+ ASSERT_FALSE(opCtx->lockState()->wasGlobalLockTakenForWrite());
}
TEST_F(DConcurrencyTestFixture, GlobalLockISDoesNotSetGlobalWriteLockedOnOperationContext) {
auto clients = makeKClientsWithLockers(1);
auto opCtx = clients[0].second.get();
- ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalWriteLocked());
+ ASSERT_FALSE(opCtx->lockState()->wasGlobalLockTakenForWrite());
{
Lock::GlobalLock globalRead(opCtx, MODE_IS, Date_t::now(), Lock::InterruptBehavior::kThrow);
ASSERT(globalRead.isLocked());
}
- ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalWriteLocked());
+ ASSERT_FALSE(opCtx->lockState()->wasGlobalLockTakenForWrite());
}
TEST_F(DConcurrencyTestFixture, DBLockXSetsGlobalWriteLockedOnOperationContext) {
auto clients = makeKClientsWithLockers(1);
auto opCtx = clients[0].second.get();
- ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalWriteLocked());
+ ASSERT_FALSE(opCtx->lockState()->wasGlobalLockTakenForWrite());
{ Lock::DBLock dbWrite(opCtx, "db", MODE_X); }
- ASSERT_TRUE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalWriteLocked());
+ ASSERT_TRUE(opCtx->lockState()->wasGlobalLockTakenForWrite());
}
TEST_F(DConcurrencyTestFixture, DBLockSDoesNotSetGlobalWriteLockedOnOperationContext) {
auto clients = makeKClientsWithLockers(1);
auto opCtx = clients[0].second.get();
- ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalWriteLocked());
+ ASSERT_FALSE(opCtx->lockState()->wasGlobalLockTakenForWrite());
{ Lock::DBLock dbRead(opCtx, "db", MODE_S); }
- ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalWriteLocked());
+ ASSERT_FALSE(opCtx->lockState()->wasGlobalLockTakenForWrite());
}
TEST_F(DConcurrencyTestFixture, GlobalLockXDoesNotSetGlobalWriteLockedWhenLockAcquisitionTimesOut) {
@@ -542,7 +541,7 @@ TEST_F(DConcurrencyTestFixture, GlobalLockXDoesNotSetGlobalWriteLockedWhenLockAc
ASSERT(globalWrite0.isLocked());
auto opCtx = clients[1].second.get();
- ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalWriteLocked());
+ ASSERT_FALSE(opCtx->lockState()->wasGlobalLockTakenForWrite());
{
ASSERT_THROWS_CODE(
Lock::GlobalLock(
@@ -550,92 +549,92 @@ TEST_F(DConcurrencyTestFixture, GlobalLockXDoesNotSetGlobalWriteLockedWhenLockAc
AssertionException,
ErrorCodes::LockTimeout);
}
- ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalWriteLocked());
+ ASSERT_FALSE(opCtx->lockState()->wasGlobalLockTakenForWrite());
}
-TEST_F(DConcurrencyTestFixture, GlobalLockSSetsGlobalSharedLockTakenOnOperationContext) {
+TEST_F(DConcurrencyTestFixture, GlobalLockSSetsGlobalLockTakenInModeConflictingWithWrites) {
auto clients = makeKClientsWithLockers(1);
auto opCtx = clients[0].second.get();
- ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
+ ASSERT_FALSE(opCtx->lockState()->wasGlobalLockTakenInModeConflictingWithWrites());
{
Lock::GlobalLock globalWrite(opCtx, MODE_S, Date_t::now(), Lock::InterruptBehavior::kThrow);
ASSERT(globalWrite.isLocked());
}
- ASSERT_TRUE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
+ ASSERT_TRUE(opCtx->lockState()->wasGlobalLockTakenInModeConflictingWithWrites());
}
-TEST_F(DConcurrencyTestFixture, GlobalLockISDoesNotSetGlobalSharedLockTakenOnOperationContext) {
+TEST_F(DConcurrencyTestFixture, GlobalLockISDoesNotSetGlobalLockTakenInModeConflictingWithWrites) {
auto clients = makeKClientsWithLockers(1);
auto opCtx = clients[0].second.get();
- ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
+ ASSERT_FALSE(opCtx->lockState()->wasGlobalLockTakenInModeConflictingWithWrites());
{
Lock::GlobalLock globalRead(opCtx, MODE_IS, Date_t::now(), Lock::InterruptBehavior::kThrow);
ASSERT(globalRead.isLocked());
}
- ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
+ ASSERT_FALSE(opCtx->lockState()->wasGlobalLockTakenInModeConflictingWithWrites());
}
-TEST_F(DConcurrencyTestFixture, GlobalLockIXDoesNotSetGlobalSharedLockTakenOnOperationContext) {
+TEST_F(DConcurrencyTestFixture, GlobalLockIXSetsGlobalLockTakenInModeConflictingWithWrites) {
auto clients = makeKClientsWithLockers(1);
auto opCtx = clients[0].second.get();
- ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
+ ASSERT_FALSE(opCtx->lockState()->wasGlobalLockTakenInModeConflictingWithWrites());
{
Lock::GlobalLock globalRead(opCtx, MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow);
ASSERT(globalRead.isLocked());
}
- ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
+ ASSERT_TRUE(opCtx->lockState()->wasGlobalLockTakenInModeConflictingWithWrites());
}
-TEST_F(DConcurrencyTestFixture, GlobalLockXDoesNotSetGlobalSharedLockTakenOnOperationContext) {
+TEST_F(DConcurrencyTestFixture, GlobalLockXSetsGlobalLockTakenInModeConflictingWithWrites) {
auto clients = makeKClientsWithLockers(1);
auto opCtx = clients[0].second.get();
- ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
+ ASSERT_FALSE(opCtx->lockState()->wasGlobalLockTakenInModeConflictingWithWrites());
{
Lock::GlobalLock globalRead(opCtx, MODE_X, Date_t::now(), Lock::InterruptBehavior::kThrow);
ASSERT(globalRead.isLocked());
}
- ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
+ ASSERT_TRUE(opCtx->lockState()->wasGlobalLockTakenInModeConflictingWithWrites());
}
-TEST_F(DConcurrencyTestFixture, DBLockSDoesNotSetGlobalSharedLockTakeOnOperationContext) {
+TEST_F(DConcurrencyTestFixture, DBLockSDoesNotSetGlobalLockTakenInModeConflictingWithWrites) {
auto clients = makeKClientsWithLockers(1);
auto opCtx = clients[0].second.get();
- ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
+ ASSERT_FALSE(opCtx->lockState()->wasGlobalLockTakenInModeConflictingWithWrites());
{ Lock::DBLock dbWrite(opCtx, "db", MODE_S); }
- ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
+ ASSERT_FALSE(opCtx->lockState()->wasGlobalLockTakenInModeConflictingWithWrites());
}
-TEST_F(DConcurrencyTestFixture, DBLockISDoesNotSetGlobalSharedLockTakeOnOperationContext) {
+TEST_F(DConcurrencyTestFixture, DBLockISDoesNotSetGlobalLockTakenInModeConflictingWithWrites) {
auto clients = makeKClientsWithLockers(1);
auto opCtx = clients[0].second.get();
- ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
+ ASSERT_FALSE(opCtx->lockState()->wasGlobalLockTakenInModeConflictingWithWrites());
{ Lock::DBLock dbWrite(opCtx, "db", MODE_IS); }
- ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
+ ASSERT_FALSE(opCtx->lockState()->wasGlobalLockTakenInModeConflictingWithWrites());
}
-TEST_F(DConcurrencyTestFixture, DBLockIXDoesNotSetGlobalSharedLockTakeOnOperationContext) {
+TEST_F(DConcurrencyTestFixture, DBLockIXSetsGlobalLockTakenInModeConflictingWithWrites) {
auto clients = makeKClientsWithLockers(1);
auto opCtx = clients[0].second.get();
- ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
+ ASSERT_FALSE(opCtx->lockState()->wasGlobalLockTakenInModeConflictingWithWrites());
{ Lock::DBLock dbWrite(opCtx, "db", MODE_IX); }
- ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
+ ASSERT_TRUE(opCtx->lockState()->wasGlobalLockTakenInModeConflictingWithWrites());
}
-TEST_F(DConcurrencyTestFixture, DBLockXDoesNotSetGlobalSharedLockTakeOnOperationContext) {
+TEST_F(DConcurrencyTestFixture, DBLockXSetsGlobalLockTakenInModeConflictingWithWrites) {
auto clients = makeKClientsWithLockers(1);
auto opCtx = clients[0].second.get();
- ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
+ ASSERT_FALSE(opCtx->lockState()->wasGlobalLockTakenInModeConflictingWithWrites());
{ Lock::DBLock dbRead(opCtx, "db", MODE_X); }
- ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
+ ASSERT_TRUE(opCtx->lockState()->wasGlobalLockTakenInModeConflictingWithWrites());
}
TEST_F(DConcurrencyTestFixture,
- GlobalLockSDoesNotSetGlobalSharedLockTakenWhenLockAcquisitionTimesOut) {
+ GlobalLockSDoesNotSetGlobalLockTakenInModeConflictingWithWritesWhenLockAcquisitionTimesOut) {
auto clients = makeKClientsWithLockers(2);
// Take a global lock so that the next one times out.
@@ -644,7 +643,7 @@ TEST_F(DConcurrencyTestFixture,
ASSERT(globalWrite0.isLocked());
auto opCtx = clients[1].second.get();
- ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
+ ASSERT_FALSE(opCtx->lockState()->wasGlobalLockTakenInModeConflictingWithWrites());
{
ASSERT_THROWS_CODE(
Lock::GlobalLock(
@@ -652,7 +651,7 @@ TEST_F(DConcurrencyTestFixture,
AssertionException,
ErrorCodes::LockTimeout);
}
- ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
+ ASSERT_FALSE(opCtx->lockState()->wasGlobalLockTakenInModeConflictingWithWrites());
}
TEST_F(DConcurrencyTestFixture, GlobalLockS_NoTimeoutDueToGlobalLockS) {
diff --git a/src/mongo/db/concurrency/global_lock_acquisition_tracker.cpp b/src/mongo/db/concurrency/global_lock_acquisition_tracker.cpp
deleted file mode 100644
index eeec12f3c1a..00000000000
--- a/src/mongo/db/concurrency/global_lock_acquisition_tracker.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/**
- * Copyright (C) 2018-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#include "mongo/platform/basic.h"
-
-#include "mongo/db/concurrency/global_lock_acquisition_tracker.h"
-
-namespace mongo {
-
-const OperationContext::Decoration<GlobalLockAcquisitionTracker> GlobalLockAcquisitionTracker::get =
- OperationContext::declareDecoration<GlobalLockAcquisitionTracker>();
-
-bool GlobalLockAcquisitionTracker::getGlobalWriteLocked() const {
- return _globalLockMode & ((1 << MODE_IX) | (1 << MODE_X));
-}
-
-bool GlobalLockAcquisitionTracker::getGlobalSharedLockTaken() const {
- return _globalLockMode & (1 << MODE_S);
-}
-
-bool GlobalLockAcquisitionTracker::getGlobalLockTaken() const {
- return _globalLockMode & ((1 << MODE_IX) | (1 << MODE_X) | (1 << MODE_IS) | (1 << MODE_S));
-}
-
-void GlobalLockAcquisitionTracker::setGlobalLockModeBit(LockMode mode) {
- _globalLockMode |= (1 << mode);
-}
-} // namespace mongo
diff --git a/src/mongo/db/concurrency/global_lock_acquisition_tracker.h b/src/mongo/db/concurrency/global_lock_acquisition_tracker.h
deleted file mode 100644
index dae19eb537f..00000000000
--- a/src/mongo/db/concurrency/global_lock_acquisition_tracker.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/**
- * Copyright (C) 2018-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#pragma once
-
-#include "mongo/db/operation_context.h"
-
-namespace mongo {
-
-/**
- * GlobalLockAcquisitionTracker keeps track of the global lock modes acquired during the
- * operation's lifetime. This class is used to track if we ever did a transaction with the
- * intent to do a write, so that we can enforce write concern on noop writes. Also, used
- * during step down to kill all user operations except those that acquired global lock in
- * IS mode.
- */
-class GlobalLockAcquisitionTracker {
-public:
- static const OperationContext::Decoration<GlobalLockAcquisitionTracker> get;
-
- // Decoration requires a default constructor.
- GlobalLockAcquisitionTracker() = default;
-
- /**
- * Returns whether we have ever taken a global lock in X or IX mode in this operation.
- */
- bool getGlobalWriteLocked() const;
-
- /**
- * Returns whether we have ever taken a global lock in S mode in this operation.
- */
- bool getGlobalSharedLockTaken() const;
-
- /**
- * Returns whether we have ever taken a global lock in this operation.
- */
- bool getGlobalLockTaken() const;
-
- /**
- * Sets the mode bit in _globalLockMode. Once a mode bit is set, we won't clear it.
- */
- void setGlobalLockModeBit(LockMode mode);
-
-private:
- // keeps track of the global lock modes acquired for this operation.
- unsigned char _globalLockMode = (1 << MODE_NONE);
-};
-
-} // namespace mongo
diff --git a/src/mongo/db/concurrency/lock_state.cpp b/src/mongo/db/concurrency/lock_state.cpp
index e5b095fdb66..617eb26f22f 100644
--- a/src/mongo/db/concurrency/lock_state.cpp
+++ b/src/mongo/db/concurrency/lock_state.cpp
@@ -588,6 +588,26 @@ bool LockerImpl::isCollectionLockedForMode(const NamespaceString& nss, LockMode
return false;
}
+bool LockerImpl::wasGlobalLockTakenForWrite() const {
+ return _globalLockMode & ((1 << MODE_IX) | (1 << MODE_X));
+}
+
+bool LockerImpl::wasGlobalLockTakenInModeConflictingWithWrites() const {
+ return _wasGlobalLockTakenInModeConflictingWithWrites.load();
+}
+
+bool LockerImpl::wasGlobalLockTaken() const {
+ return _globalLockMode;
+}
+
+void LockerImpl::setGlobalLockTakenInMode(LockMode mode) {
+ _globalLockMode |= (1 << mode);
+
+ if (mode == MODE_IX || mode == MODE_X || mode == MODE_S) {
+ _wasGlobalLockTakenInModeConflictingWithWrites.store(true);
+ }
+}
+
ResourceId LockerImpl::getWaitingResource() const {
scoped_spinlock scopedLock(_lock);
diff --git a/src/mongo/db/concurrency/lock_state.h b/src/mongo/db/concurrency/lock_state.h
index 4a0654cc2b8..0b9741633d5 100644
--- a/src/mongo/db/concurrency/lock_state.h
+++ b/src/mongo/db/concurrency/lock_state.h
@@ -158,6 +158,14 @@ public:
return _wuowNestingLevel > 0;
}
+ bool wasGlobalLockTakenForWrite() const override;
+
+ bool wasGlobalLockTakenInModeConflictingWithWrites() const override;
+
+ bool wasGlobalLockTaken() const override;
+
+ void setGlobalLockTakenInMode(LockMode mode) override;
+
/**
* Requests a lock for resource 'resId' with mode 'mode'. An OperationContext 'opCtx' must be
* provided to interrupt waiting on the locker condition variable that indicates status of
@@ -357,6 +365,13 @@ private:
// A structure for accumulating time spent getting flow control tickets.
FlowControlTicketholder::CurOp _flowControlStats;
+ // Tracks the global lock modes ever acquired in this Locker's life. This value should only ever
+ // be accessed from the thread that owns the Locker.
+ unsigned char _globalLockMode = (1 << MODE_NONE);
+
+ // Tracks whether this operation should be killed on step down.
+ AtomicWord<bool> _wasGlobalLockTakenInModeConflictingWithWrites{false};
+
//////////////////////////////////////////////////////////////////////////////////////////
//
// Methods merged from LockState, which should eventually be removed or changed to methods
diff --git a/src/mongo/db/concurrency/locker.h b/src/mongo/db/concurrency/locker.h
index 8585cf0bb1c..61a0a950b3c 100644
--- a/src/mongo/db/concurrency/locker.h
+++ b/src/mongo/db/concurrency/locker.h
@@ -236,6 +236,29 @@ public:
virtual bool inAWriteUnitOfWork() const = 0;
/**
+ * Returns whether we have ever taken a global lock in X or IX mode in this operation.
+ * Should only be called on the thread owning the locker.
+ */
+ virtual bool wasGlobalLockTakenForWrite() const = 0;
+
+ /**
+ * Returns whether we have ever taken a global lock in S, X, or IX mode in this operation.
+ */
+ virtual bool wasGlobalLockTakenInModeConflictingWithWrites() const = 0;
+
+ /**
+ * Returns whether we have ever taken a global lock in this operation.
+ * Should only be called on the thread owning the locker.
+ */
+ virtual bool wasGlobalLockTaken() const = 0;
+
+ /**
+ * Sets the mode bit in _globalLockMode. Once a mode bit is set, we won't clear it. Also sets
+ * _wasGlobalLockTakenInModeConflictingWithWrites to true if the mode is S, X, or IX.
+ */
+ virtual void setGlobalLockTakenInMode(LockMode mode) = 0;
+
+ /**
* Acquires lock on the specified resource in the specified mode and returns the outcome
* of the operation. See the details for LockResult for more information on what the
* different results mean.
diff --git a/src/mongo/db/concurrency/locker_noop.h b/src/mongo/db/concurrency/locker_noop.h
index 5e53bd6f6de..886510cb16c 100644
--- a/src/mongo/db/concurrency/locker_noop.h
+++ b/src/mongo/db/concurrency/locker_noop.h
@@ -118,6 +118,20 @@ public:
return false;
}
+ virtual bool wasGlobalLockTakenForWrite() const {
+ return false;
+ }
+
+ virtual bool wasGlobalLockTakenInModeConflictingWithWrites() const {
+ return false;
+ }
+
+ virtual bool wasGlobalLockTaken() const {
+ return false;
+ }
+
+ virtual void setGlobalLockTakenInMode(LockMode mode) {}
+
virtual LockResult lockRSTLBegin(OperationContext* opCtx, LockMode mode) {
MONGO_UNREACHABLE;
}
diff --git a/src/mongo/db/curop.cpp b/src/mongo/db/curop.cpp
index cf6b63648e6..de531be80e8 100644
--- a/src/mongo/db/curop.cpp
+++ b/src/mongo/db/curop.cpp
@@ -43,7 +43,6 @@
#include "mongo/db/commands.h"
#include "mongo/db/commands/server_status_metric.h"
#include "mongo/db/concurrency/d_concurrency.h"
-#include "mongo/db/concurrency/global_lock_acquisition_tracker.h"
#include "mongo/db/concurrency/locker.h"
#include "mongo/db/json.h"
#include "mongo/db/query/getmore_request.h"
@@ -406,9 +405,7 @@ bool CurOp::completeAndLogOperation(OperationContext* opCtx,
if (shouldLogOp || (shouldSample && _debug.executionTimeMicros > slowMs * 1000LL)) {
auto lockerInfo = opCtx->lockState()->getLockerInfo(_lockStatsBase);
- const GlobalLockAcquisitionTracker& globalLockTracker =
- GlobalLockAcquisitionTracker::get(opCtx);
- if (_debug.storageStats == nullptr && globalLockTracker.getGlobalLockTaken() &&
+ if (_debug.storageStats == nullptr && opCtx->lockState()->wasGlobalLockTaken() &&
opCtx->getServiceContext()->getStorageEngine()) {
// Do not fetch operation statistics again if we have already got them (for instance,
// as a part of stashing the transaction).
diff --git a/src/mongo/db/repl/replication_coordinator_impl.cpp b/src/mongo/db/repl/replication_coordinator_impl.cpp
index 6670e7c6ec6..9b0e6ac8b68 100644
--- a/src/mongo/db/repl/replication_coordinator_impl.cpp
+++ b/src/mongo/db/repl/replication_coordinator_impl.cpp
@@ -46,7 +46,6 @@
#include "mongo/db/commands.h"
#include "mongo/db/commands/test_commands_enabled.h"
#include "mongo/db/concurrency/d_concurrency.h"
-#include "mongo/db/concurrency/global_lock_acquisition_tracker.h"
#include "mongo/db/concurrency/replication_state_transition_lock_guard.h"
#include "mongo/db/index/index_descriptor.h"
#include "mongo/db/index_builds_coordinator.h"
@@ -1816,10 +1815,8 @@ void ReplicationCoordinatorImpl::_killUserOperationsOnStepDown(
// Don't kill the stepdown thread.
if (toKill && !toKill->isKillPending() && toKill->getOpID() != stepDownOpCtx->getOpID()) {
- const GlobalLockAcquisitionTracker& globalLockTracker =
- GlobalLockAcquisitionTracker::get(toKill);
- if (globalLockTracker.getGlobalWriteLocked() ||
- globalLockTracker.getGlobalSharedLockTaken()) {
+ auto locker = toKill->lockState();
+ if (locker->wasGlobalLockTakenInModeConflictingWithWrites()) {
serviceCtx->killOperation(lk, toKill, ErrorCodes::InterruptedDueToStepDown);
userOpsKilled.increment();
} else {
diff --git a/src/mongo/db/service_entry_point_common.cpp b/src/mongo/db/service_entry_point_common.cpp
index 8f2a35b0055..6fb6df55378 100644
--- a/src/mongo/db/service_entry_point_common.cpp
+++ b/src/mongo/db/service_entry_point_common.cpp
@@ -44,7 +44,6 @@
#include "mongo/db/commands.h"
#include "mongo/db/commands/test_commands_enabled.h"
#include "mongo/db/commands/txn_cmds_gen.h"
-#include "mongo/db/concurrency/global_lock_acquisition_tracker.h"
#include "mongo/db/curop.h"
#include "mongo/db/curop_failpoint_helpers.h"
#include "mongo/db/curop_metrics.h"
diff --git a/src/mongo/db/service_entry_point_mongod.cpp b/src/mongo/db/service_entry_point_mongod.cpp
index 533db3576e8..9036e55a289 100644
--- a/src/mongo/db/service_entry_point_mongod.cpp
+++ b/src/mongo/db/service_entry_point_mongod.cpp
@@ -34,7 +34,6 @@
#include "mongo/db/service_entry_point_mongod.h"
#include "mongo/db/commands/fsync_locked.h"
-#include "mongo/db/concurrency/global_lock_acquisition_tracker.h"
#include "mongo/db/curop.h"
#include "mongo/db/read_concern.h"
#include "mongo/db/repl/repl_client_info.h"
@@ -104,10 +103,21 @@ public:
const repl::OpTime& lastOpBeforeRun,
BSONObjBuilder& commandResponseBuilder) const override {
auto lastOpAfterRun = repl::ReplClientInfo::forClient(opCtx->getClient()).getLastOp();
+
// Ensures that if we tried to do a write, we wait for write concern, even if that write was
// a noop.
+ //
+ // Transactions do not stash their lockers on commit and abort, so after commit and abort,
+ // wasGlobalLockTakenForWrite will return whether any statement in the transaction as a
+ // whole acquired the global write lock.
+ //
+ // Speculative majority semantics dictate that "abortTransaction" should not wait for write
+ // concern on operations the transaction observed. As a result, "abortTransaction" only ever
+ // waits on an oplog entry it wrote (and has already set lastOp to) or previous writes on
+ // the same client.
if ((lastOpAfterRun == lastOpBeforeRun) &&
- GlobalLockAcquisitionTracker::get(opCtx).getGlobalWriteLocked()) {
+ opCtx->lockState()->wasGlobalLockTakenForWrite() &&
+ (invocation->definition()->getName() != "abortTransaction")) {
repl::ReplClientInfo::forClient(opCtx->getClient()).setLastOpToSystemLastOpTime(opCtx);
lastOpAfterRun = repl::ReplClientInfo::forClient(opCtx->getClient()).getLastOp();
}