diff options
-rw-r--r-- | jstests/noPassthrough/abort_awaits_correct_optime.js | 59 | ||||
-rw-r--r-- | src/mongo/db/concurrency/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/db/concurrency/d_concurrency.cpp | 3 | ||||
-rw-r--r-- | src/mongo/db/concurrency/d_concurrency.h | 6 | ||||
-rw-r--r-- | src/mongo/db/concurrency/d_concurrency_test.cpp | 65 | ||||
-rw-r--r-- | src/mongo/db/concurrency/global_lock_acquisition_tracker.cpp | 54 | ||||
-rw-r--r-- | src/mongo/db/concurrency/global_lock_acquisition_tracker.h | 75 | ||||
-rw-r--r-- | src/mongo/db/concurrency/lock_state.cpp | 31 | ||||
-rw-r--r-- | src/mongo/db/concurrency/lock_state.h | 11 | ||||
-rw-r--r-- | src/mongo/db/concurrency/locker.h | 20 | ||||
-rw-r--r-- | src/mongo/db/concurrency/locker_noop.h | 14 | ||||
-rw-r--r-- | src/mongo/db/curop.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/repl/replication_coordinator_impl.cpp | 7 | ||||
-rw-r--r-- | src/mongo/db/service_entry_point_common.cpp | 1 | ||||
-rw-r--r-- | src/mongo/db/service_entry_point_mongod.cpp | 4 |
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(); } |