summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jstests/noPassthrough/abort_awaits_correct_optime.js59
-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.cpp65
-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.cpp31
-rw-r--r--src/mongo/db/concurrency/lock_state.h11
-rw-r--r--src/mongo/db/concurrency/locker.h20
-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.cpp4
15 files changed, 182 insertions, 174 deletions
diff --git a/jstests/noPassthrough/abort_awaits_correct_optime.js b/jstests/noPassthrough/abort_awaits_correct_optime.js
deleted file mode 100644
index 6301bd5676d..00000000000
--- a/jstests/noPassthrough/abort_awaits_correct_optime.js
+++ /dev/null
@@ -1,59 +0,0 @@
-// Test that a transaction which has done only a no-op write (an update with no effect), and aborts,
-// awaits the system lastOpTime with the transaction's writeConcern.
-// @tags: [uses_transactions]
-(function() {
- "use strict";
-
- load("jstests/libs/write_concern_util.js"); // For stopReplicationOnSecondaries.
-
- const dbName = "test";
- const collName = "coll";
-
- const rst = new ReplSetTest({nodes: 2});
- rst.startSet();
- rst.initiate();
-
- const primary = rst.getPrimary();
- const primaryDB = primary.getDB(dbName);
- const session = primary.getDB("admin").getMongo().startSession();
- const sessionDB = session.getDatabase(dbName);
-
- assert.commandWorked(primaryDB[collName].insert({x: 1}, {writeConcern: {w: "majority"}}));
-
- jsTestLog("Stop replication");
-
- stopReplicationOnSecondaries(rst);
-
- jsTestLog("Start snapshot transaction, initial command is a write with no effect");
-
- session.startTransaction(
- {writeConcern: {w: "majority", wtimeout: 1000}, readConcern: {level: "snapshot"}});
-
- const fruitlessUpdate = {update: collName, updates: [{q: {x: 1}, u: {$set: {x: 1}}}]};
- printjson(assert.commandWorked(sessionDB.runCommand(fruitlessUpdate)));
-
- jsTestLog("Advance opTime on primary, with replication stopped");
-
- printjson(assert.commandWorked(primaryDB.runCommand({insert: collName, documents: [{}]})));
-
- jsTestLog("Abort the transaction, expect wtimeout after 1 second");
-
- assert.commandFailedWithCode(
- session.abortTransaction_forTesting(), ErrorCodes.WriteConcernFailed, "abort transaction");
-
- jsTestLog("Restart replication");
-
- restartReplicationOnSecondaries(rst);
-
- jsTestLog("Try transaction with replication enabled");
-
- session.startTransaction({
- writeConcern: {w: "majority", wtimeout: ReplSetTest.kDefaultTimeoutMS},
- readConcern: {level: "snapshot"}
- });
-
- assert.commandWorked(sessionDB.runCommand(fruitlessUpdate));
- assert.commandWorked(session.abortTransaction_forTesting());
-
- rst.stopSet();
-}());
diff --git a/src/mongo/db/concurrency/SConscript b/src/mongo/db/concurrency/SConscript
index f74bf25ba89..986bb83c616 100644
--- a/src/mongo/db/concurrency/SConscript
+++ b/src/mongo/db/concurrency/SConscript
@@ -40,6 +40,7 @@ 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 dce90b1dcfb..c1f858a75a0 100644
--- a/src/mongo/db/concurrency/d_concurrency.cpp
+++ b/src/mongo/db/concurrency/d_concurrency.cpp
@@ -37,6 +37,7 @@
#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"
@@ -222,7 +223,7 @@ void Lock::GlobalLock::waitForLockUntil(Date_t deadline) {
const ResourceId globalResId(RESOURCE_GLOBAL, ResourceId::SINGLETON_GLOBAL);
auto lockMode = _opCtx->lockState()->getLockMode(globalResId);
- _opCtx->lockState()->setGlobalLockModeBit(lockMode);
+ GlobalLockAcquisitionTracker::get(_opCtx).setGlobalLockModeBit(lockMode);
}
void Lock::GlobalLock::_unlock() {
diff --git a/src/mongo/db/concurrency/d_concurrency.h b/src/mongo/db/concurrency/d_concurrency.h
index 7efb40ffd7d..e6259e85ca4 100644
--- a/src/mongo/db/concurrency/d_concurrency.h
+++ b/src/mongo/db/concurrency/d_concurrency.h
@@ -211,7 +211,8 @@ public:
* Enqueues lock but does not block on lock acquisition.
* Call waitForLockUntil() to complete locking process.
*
- * Does not set Locker::wasGlobalWriteLockTaken(). Call waitForLockUntil to do so.
+ * Does not set that the global lock was taken on the GlobalLockAcquisitionTracker. Call
+ * waitForLockUntil to do so.
*/
GlobalLock(OperationContext* opCtx,
LockMode lockMode,
@@ -235,7 +236,8 @@ public:
}
/**
- * Waits for lock to be granted. Sets Locker::wasGlobalWriteLockTaken().
+ * Waits for lock to be granted. Sets that the global lock was taken on the
+ * GlobalLockAcquisitionTracker.
*/
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 17987599def..4c919292b80 100644
--- a/src/mongo/db/concurrency/d_concurrency_test.cpp
+++ b/src/mongo/db/concurrency/d_concurrency_test.cpp
@@ -36,6 +36,7 @@
#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"
@@ -471,65 +472,65 @@ TEST_F(DConcurrencyTestFixture, RSTLmodeX_Timeout) {
TEST_F(DConcurrencyTestFixture, GlobalLockXSetsGlobalWriteLockedOnOperationContext) {
auto clients = makeKClientsWithLockers(1);
auto opCtx = clients[0].second.get();
- ASSERT_FALSE(opCtx->lockState()->wasGlobalWriteLockTaken());
+ ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalWriteLocked());
{
Lock::GlobalLock globalWrite(opCtx, MODE_X, Date_t::now(), Lock::InterruptBehavior::kThrow);
ASSERT(globalWrite.isLocked());
}
- ASSERT_TRUE(opCtx->lockState()->wasGlobalWriteLockTaken());
+ ASSERT_TRUE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalWriteLocked());
}
TEST_F(DConcurrencyTestFixture, GlobalLockIXSetsGlobalWriteLockedOnOperationContext) {
auto clients = makeKClientsWithLockers(1);
auto opCtx = clients[0].second.get();
- ASSERT_FALSE(opCtx->lockState()->wasGlobalWriteLockTaken());
+ ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalWriteLocked());
{
Lock::GlobalLock globalWrite(
opCtx, MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow);
ASSERT(globalWrite.isLocked());
}
- ASSERT_TRUE(opCtx->lockState()->wasGlobalWriteLockTaken());
+ ASSERT_TRUE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalWriteLocked());
}
TEST_F(DConcurrencyTestFixture, GlobalLockSDoesNotSetGlobalWriteLockedOnOperationContext) {
auto clients = makeKClientsWithLockers(1);
auto opCtx = clients[0].second.get();
- ASSERT_FALSE(opCtx->lockState()->wasGlobalWriteLockTaken());
+ ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalWriteLocked());
{
Lock::GlobalLock globalRead(opCtx, MODE_S, Date_t::now(), Lock::InterruptBehavior::kThrow);
ASSERT(globalRead.isLocked());
}
- ASSERT_FALSE(opCtx->lockState()->wasGlobalWriteLockTaken());
+ ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalWriteLocked());
}
TEST_F(DConcurrencyTestFixture, GlobalLockISDoesNotSetGlobalWriteLockedOnOperationContext) {
auto clients = makeKClientsWithLockers(1);
auto opCtx = clients[0].second.get();
- ASSERT_FALSE(opCtx->lockState()->wasGlobalWriteLockTaken());
+ ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalWriteLocked());
{
Lock::GlobalLock globalRead(opCtx, MODE_IS, Date_t::now(), Lock::InterruptBehavior::kThrow);
ASSERT(globalRead.isLocked());
}
- ASSERT_FALSE(opCtx->lockState()->wasGlobalWriteLockTaken());
+ ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalWriteLocked());
}
TEST_F(DConcurrencyTestFixture, DBLockXSetsGlobalWriteLockedOnOperationContext) {
auto clients = makeKClientsWithLockers(1);
auto opCtx = clients[0].second.get();
- ASSERT_FALSE(opCtx->lockState()->wasGlobalWriteLockTaken());
+ ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalWriteLocked());
{ Lock::DBLock dbWrite(opCtx, "db", MODE_X); }
- ASSERT_TRUE(opCtx->lockState()->wasGlobalWriteLockTaken());
+ ASSERT_TRUE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalWriteLocked());
}
TEST_F(DConcurrencyTestFixture, DBLockSDoesNotSetGlobalWriteLockedOnOperationContext) {
auto clients = makeKClientsWithLockers(1);
auto opCtx = clients[0].second.get();
- ASSERT_FALSE(opCtx->lockState()->wasGlobalWriteLockTaken());
+ ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalWriteLocked());
{ Lock::DBLock dbRead(opCtx, "db", MODE_S); }
- ASSERT_FALSE(opCtx->lockState()->wasGlobalWriteLockTaken());
+ ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalWriteLocked());
}
TEST_F(DConcurrencyTestFixture, GlobalLockXDoesNotSetGlobalWriteLockedWhenLockAcquisitionTimesOut) {
@@ -541,7 +542,7 @@ TEST_F(DConcurrencyTestFixture, GlobalLockXDoesNotSetGlobalWriteLockedWhenLockAc
ASSERT(globalWrite0.isLocked());
auto opCtx = clients[1].second.get();
- ASSERT_FALSE(opCtx->lockState()->wasGlobalWriteLockTaken());
+ ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalWriteLocked());
{
ASSERT_THROWS_CODE(
Lock::GlobalLock(
@@ -549,88 +550,88 @@ TEST_F(DConcurrencyTestFixture, GlobalLockXDoesNotSetGlobalWriteLockedWhenLockAc
AssertionException,
ErrorCodes::LockTimeout);
}
- ASSERT_FALSE(opCtx->lockState()->wasGlobalWriteLockTaken());
+ ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalWriteLocked());
}
TEST_F(DConcurrencyTestFixture, GlobalLockSSetsGlobalSharedLockTakenOnOperationContext) {
auto clients = makeKClientsWithLockers(1);
auto opCtx = clients[0].second.get();
- ASSERT_FALSE(opCtx->lockState()->wasGlobalSharedLockTaken());
+ ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
{
Lock::GlobalLock globalWrite(opCtx, MODE_S, Date_t::now(), Lock::InterruptBehavior::kThrow);
ASSERT(globalWrite.isLocked());
}
- ASSERT_TRUE(opCtx->lockState()->wasGlobalSharedLockTaken());
+ ASSERT_TRUE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
}
TEST_F(DConcurrencyTestFixture, GlobalLockISDoesNotSetGlobalSharedLockTakenOnOperationContext) {
auto clients = makeKClientsWithLockers(1);
auto opCtx = clients[0].second.get();
- ASSERT_FALSE(opCtx->lockState()->wasGlobalSharedLockTaken());
+ ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
{
Lock::GlobalLock globalRead(opCtx, MODE_IS, Date_t::now(), Lock::InterruptBehavior::kThrow);
ASSERT(globalRead.isLocked());
}
- ASSERT_FALSE(opCtx->lockState()->wasGlobalSharedLockTaken());
+ ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
}
TEST_F(DConcurrencyTestFixture, GlobalLockIXDoesNotSetGlobalSharedLockTakenOnOperationContext) {
auto clients = makeKClientsWithLockers(1);
auto opCtx = clients[0].second.get();
- ASSERT_FALSE(opCtx->lockState()->wasGlobalSharedLockTaken());
+ ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
{
Lock::GlobalLock globalRead(opCtx, MODE_IX, Date_t::now(), Lock::InterruptBehavior::kThrow);
ASSERT(globalRead.isLocked());
}
- ASSERT_FALSE(opCtx->lockState()->wasGlobalSharedLockTaken());
+ ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
}
TEST_F(DConcurrencyTestFixture, GlobalLockXDoesNotSetGlobalSharedLockTakenOnOperationContext) {
auto clients = makeKClientsWithLockers(1);
auto opCtx = clients[0].second.get();
- ASSERT_FALSE(opCtx->lockState()->wasGlobalSharedLockTaken());
+ ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
{
Lock::GlobalLock globalRead(opCtx, MODE_X, Date_t::now(), Lock::InterruptBehavior::kThrow);
ASSERT(globalRead.isLocked());
}
- ASSERT_FALSE(opCtx->lockState()->wasGlobalSharedLockTaken());
+ ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
}
TEST_F(DConcurrencyTestFixture, DBLockSDoesNotSetGlobalSharedLockTakeOnOperationContext) {
auto clients = makeKClientsWithLockers(1);
auto opCtx = clients[0].second.get();
- ASSERT_FALSE(opCtx->lockState()->wasGlobalSharedLockTaken());
+ ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
{ Lock::DBLock dbWrite(opCtx, "db", MODE_S); }
- ASSERT_FALSE(opCtx->lockState()->wasGlobalSharedLockTaken());
+ ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
}
TEST_F(DConcurrencyTestFixture, DBLockISDoesNotSetGlobalSharedLockTakeOnOperationContext) {
auto clients = makeKClientsWithLockers(1);
auto opCtx = clients[0].second.get();
- ASSERT_FALSE(opCtx->lockState()->wasGlobalSharedLockTaken());
+ ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
{ Lock::DBLock dbWrite(opCtx, "db", MODE_IS); }
- ASSERT_FALSE(opCtx->lockState()->wasGlobalSharedLockTaken());
+ ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
}
TEST_F(DConcurrencyTestFixture, DBLockIXDoesNotSetGlobalSharedLockTakeOnOperationContext) {
auto clients = makeKClientsWithLockers(1);
auto opCtx = clients[0].second.get();
- ASSERT_FALSE(opCtx->lockState()->wasGlobalSharedLockTaken());
+ ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
{ Lock::DBLock dbWrite(opCtx, "db", MODE_IX); }
- ASSERT_FALSE(opCtx->lockState()->wasGlobalSharedLockTaken());
+ ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
}
TEST_F(DConcurrencyTestFixture, DBLockXDoesNotSetGlobalSharedLockTakeOnOperationContext) {
auto clients = makeKClientsWithLockers(1);
auto opCtx = clients[0].second.get();
- ASSERT_FALSE(opCtx->lockState()->wasGlobalSharedLockTaken());
+ ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
{ Lock::DBLock dbRead(opCtx, "db", MODE_X); }
- ASSERT_FALSE(opCtx->lockState()->wasGlobalSharedLockTaken());
+ ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
}
TEST_F(DConcurrencyTestFixture,
@@ -643,7 +644,7 @@ TEST_F(DConcurrencyTestFixture,
ASSERT(globalWrite0.isLocked());
auto opCtx = clients[1].second.get();
- ASSERT_FALSE(opCtx->lockState()->wasGlobalSharedLockTaken());
+ ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
{
ASSERT_THROWS_CODE(
Lock::GlobalLock(
@@ -651,7 +652,7 @@ TEST_F(DConcurrencyTestFixture,
AssertionException,
ErrorCodes::LockTimeout);
}
- ASSERT_FALSE(opCtx->lockState()->wasGlobalSharedLockTaken());
+ ASSERT_FALSE(GlobalLockAcquisitionTracker::get(opCtx).getGlobalSharedLockTaken());
}
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
new file mode 100644
index 00000000000..eeec12f3c1a
--- /dev/null
+++ b/src/mongo/db/concurrency/global_lock_acquisition_tracker.cpp
@@ -0,0 +1,54 @@
+/**
+ * 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
new file mode 100644
index 00000000000..dae19eb537f
--- /dev/null
+++ b/src/mongo/db/concurrency/global_lock_acquisition_tracker.h
@@ -0,0 +1,75 @@
+/**
+ * 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 1450ba98397..225e563fe6e 100644
--- a/src/mongo/db/concurrency/lock_state.cpp
+++ b/src/mongo/db/concurrency/lock_state.cpp
@@ -615,37 +615,6 @@ bool LockerImpl::isCollectionLockedForMode(const NamespaceString& nss, LockMode
return false;
}
-bool LockerImpl::wasGlobalWriteLockTaken() const {
- return _globalLockMode.load() & ((1 << MODE_IX) | (1 << MODE_X));
-}
-
-bool LockerImpl::wasGlobalSharedLockTaken() const {
- return _globalLockMode.load() & (1 << MODE_S);
-}
-
-bool LockerImpl::wasGlobalLockTaken() const {
- return _globalLockMode.load() &
- ((1 << MODE_IX) | (1 << MODE_X) | (1 << MODE_IS) | (1 << MODE_S));
-}
-
-void LockerImpl::setGlobalLockModeBit(LockMode mode) {
- const unsigned char bit = 1 << mode;
- unsigned char actual = _globalLockMode.load();
- unsigned char expected;
-
- // This is monotonic (once "bit" is set it is never unset) so we can optimistically try to set
- // it, retrying if another thread modifies _globalLockMode between the load and the swap. If
- // another thread sets "bit" while we are trying to, then we're done.
- do {
- if (actual & bit) {
- return;
- }
-
- expected = actual;
- actual = _globalLockMode.compareAndSwap(expected, actual | bit);
- } while (actual != expected);
-}
-
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 936e5585c64..f7659dc9b4b 100644
--- a/src/mongo/db/concurrency/lock_state.h
+++ b/src/mongo/db/concurrency/lock_state.h
@@ -158,14 +158,6 @@ public:
return _wuowNestingLevel > 0;
}
- bool wasGlobalWriteLockTaken() const override;
-
- bool wasGlobalSharedLockTaken() const override;
-
- bool wasGlobalLockTaken() const override;
-
- void setGlobalLockModeBit(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
@@ -356,9 +348,6 @@ private:
// available. Note this will be ineffective if uninterruptible lock guard is set.
boost::optional<Milliseconds> _maxLockTimeout;
- // Tracks the global lock modes ever acquired in this Locker's life.
- AtomicWord<unsigned char> _globalLockMode;
-
//////////////////////////////////////////////////////////////////////////////////////////
//
// 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 69a048e123f..24f9dfbd532 100644
--- a/src/mongo/db/concurrency/locker.h
+++ b/src/mongo/db/concurrency/locker.h
@@ -235,26 +235,6 @@ public:
virtual bool inAWriteUnitOfWork() const = 0;
/**
- * Returns whether we have ever taken a global lock in X or IX mode in this operation.
- */
- virtual bool wasGlobalWriteLockTaken() const = 0;
-
- /**
- * Returns whether we have ever taken a global lock in S mode in this operation.
- */
- virtual bool wasGlobalSharedLockTaken() const = 0;
-
- /**
- * Returns whether we have ever taken a global lock in this operation.
- */
- virtual bool wasGlobalLockTaken() const = 0;
-
- /**
- * Sets the mode bit in _globalLockMode. Once a mode bit is set, we won't clear it.
- */
- virtual void setGlobalLockModeBit(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 b06917043ed..5e53bd6f6de 100644
--- a/src/mongo/db/concurrency/locker_noop.h
+++ b/src/mongo/db/concurrency/locker_noop.h
@@ -118,20 +118,6 @@ public:
return false;
}
- virtual bool wasGlobalWriteLockTaken() const {
- return false;
- }
-
- virtual bool wasGlobalSharedLockTaken() const {
- return false;
- }
-
- virtual bool wasGlobalLockTaken() const {
- return false;
- }
-
- virtual void setGlobalLockModeBit(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 bd5a1687bf6..cb18faa1337 100644
--- a/src/mongo/db/curop.cpp
+++ b/src/mongo/db/curop.cpp
@@ -43,6 +43,7 @@
#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"
@@ -405,7 +406,9 @@ bool CurOp::completeAndLogOperation(OperationContext* opCtx,
if (shouldLogOp || (shouldSample && _debug.executionTimeMicros > slowMs * 1000LL)) {
auto lockerInfo = opCtx->lockState()->getLockerInfo(_lockStatsBase);
- if (_debug.storageStats == nullptr && opCtx->lockState()->wasGlobalLockTaken() &&
+ const GlobalLockAcquisitionTracker& globalLockTracker =
+ GlobalLockAcquisitionTracker::get(opCtx);
+ if (_debug.storageStats == nullptr && globalLockTracker.getGlobalLockTaken() &&
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 e5bd72235ba..674e244acd8 100644
--- a/src/mongo/db/repl/replication_coordinator_impl.cpp
+++ b/src/mongo/db/repl/replication_coordinator_impl.cpp
@@ -46,6 +46,7 @@
#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"
@@ -1807,8 +1808,10 @@ void ReplicationCoordinatorImpl::_killUserOperationsOnStepDown(
// Don't kill the stepdown thread.
if (toKill && !toKill->isKillPending() && toKill->getOpID() != stepDownOpCtx->getOpID()) {
- auto locker = toKill->lockState();
- if (locker->wasGlobalWriteLockTaken() || locker->wasGlobalSharedLockTaken()) {
+ const GlobalLockAcquisitionTracker& globalLockTracker =
+ GlobalLockAcquisitionTracker::get(toKill);
+ if (globalLockTracker.getGlobalWriteLocked() ||
+ globalLockTracker.getGlobalSharedLockTaken()) {
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 ee195f4ad49..a3077e08a38 100644
--- a/src/mongo/db/service_entry_point_common.cpp
+++ b/src/mongo/db/service_entry_point_common.cpp
@@ -44,6 +44,7 @@
#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 d67b6fee50a..533db3576e8 100644
--- a/src/mongo/db/service_entry_point_mongod.cpp
+++ b/src/mongo/db/service_entry_point_mongod.cpp
@@ -34,6 +34,7 @@
#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"
@@ -105,7 +106,8 @@ public:
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.
- if ((lastOpAfterRun == lastOpBeforeRun) && opCtx->lockState()->wasGlobalWriteLockTaken()) {
+ if ((lastOpAfterRun == lastOpBeforeRun) &&
+ GlobalLockAcquisitionTracker::get(opCtx).getGlobalWriteLocked()) {
repl::ReplClientInfo::forClient(opCtx->getClient()).setLastOpToSystemLastOpTime(opCtx);
lastOpAfterRun = repl::ReplClientInfo::forClient(opCtx->getClient()).getLastOp();
}