diff options
-rw-r--r-- | src/mongo/db/concurrency/lock_state.cpp | 19 | ||||
-rw-r--r-- | src/mongo/db/concurrency/lock_state.h | 2 | ||||
-rw-r--r-- | src/mongo/db/concurrency/locker.h | 7 | ||||
-rw-r--r-- | src/mongo/db/concurrency/locker_noop.h | 4 | ||||
-rw-r--r-- | src/mongo/db/operation_context.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/operation_context.h | 6 | ||||
-rw-r--r-- | src/mongo/db/operation_context_impl.cpp | 23 | ||||
-rw-r--r-- | src/mongo/db/operation_context_impl.h | 2 | ||||
-rw-r--r-- | src/mongo/db/repl/service_context_repl_mock.cpp | 1 |
9 files changed, 61 insertions, 8 deletions
diff --git a/src/mongo/db/concurrency/lock_state.cpp b/src/mongo/db/concurrency/lock_state.cpp index 9235f56bbb4..898f9cd462e 100644 --- a/src/mongo/db/concurrency/lock_state.cpp +++ b/src/mongo/db/concurrency/lock_state.cpp @@ -191,6 +191,17 @@ bool LockerImpl<IsForMMAPV1>::isReadLocked() const { } template <bool IsForMMAPV1> +void LockerImpl<IsForMMAPV1>::assertEmptyAndReset() { + invariant(!inAWriteUnitOfWork()); + invariant(_resourcesToUnlockAtEndOfUnitOfWork.empty()); + invariant(_requests.empty()); + invariant(_modeForTicket == MODE_NONE); + + // Reset the locking statistics so the object can be reused + _stats.reset(); +} + +template <bool IsForMMAPV1> void LockerImpl<IsForMMAPV1>::dump() const { StringBuilder ss; ss << "Locker id " << _id << " status: "; @@ -272,13 +283,7 @@ LockerImpl<IsForMMAPV1>::~LockerImpl() { // Cannot delete the Locker while there are still outstanding requests, because the // LockManager may attempt to access deleted memory. Besides it is probably incorrect // to delete with unaccounted locks anyways. - invariant(!inAWriteUnitOfWork()); - invariant(_resourcesToUnlockAtEndOfUnitOfWork.empty()); - invariant(_requests.empty()); - invariant(_modeForTicket == MODE_NONE); - - // Reset the locking statistics so the object can be reused - _stats.reset(); + assertEmptyAndReset(); } template <bool IsForMMAPV1> diff --git a/src/mongo/db/concurrency/lock_state.h b/src/mongo/db/concurrency/lock_state.h index e4bdded0bdf..79f348d4ac6 100644 --- a/src/mongo/db/concurrency/lock_state.h +++ b/src/mongo/db/concurrency/lock_state.h @@ -244,6 +244,8 @@ public: virtual bool isWriteLocked() const; virtual bool isReadLocked() const; + virtual void assertEmptyAndReset(); + virtual bool hasLockPending() const { return getWaitingResource().isValid(); } diff --git a/src/mongo/db/concurrency/locker.h b/src/mongo/db/concurrency/locker.h index 6dacd299e65..e00882a498d 100644 --- a/src/mongo/db/concurrency/locker.h +++ b/src/mongo/db/concurrency/locker.h @@ -309,6 +309,13 @@ public: virtual bool isReadLocked() const = 0; /** + * Asserts that the Locker is effectively not in use and resets the locking statistics. + * This means, there should be no locks on it, no WUOW, etc, so it would be safe to call + * the destructor or reuse the Locker. + */ + virtual void assertEmptyAndReset() = 0; + + /** * Pending means we are currently trying to get a lock (could be the parallel batch writer * lock). */ diff --git a/src/mongo/db/concurrency/locker_noop.h b/src/mongo/db/concurrency/locker_noop.h index ff252243964..24450ceeecc 100644 --- a/src/mongo/db/concurrency/locker_noop.h +++ b/src/mongo/db/concurrency/locker_noop.h @@ -160,6 +160,10 @@ public: invariant(false); } + virtual void assertEmptyAndReset() { + invariant(false); + } + virtual bool hasLockPending() const { invariant(false); } diff --git a/src/mongo/db/operation_context.cpp b/src/mongo/db/operation_context.cpp index f96c644b431..8151fe97dfa 100644 --- a/src/mongo/db/operation_context.cpp +++ b/src/mongo/db/operation_context.cpp @@ -397,6 +397,11 @@ OperationContext::RecoveryUnitState OperationContext::setRecoveryUnit(RecoveryUn return oldState; } +std::unique_ptr<Locker> OperationContext::releaseLockState() { + dassert(_locker); + return std::move(_locker); +} + void OperationContext::setLockState(std::unique_ptr<Locker> locker) { dassert(!_locker); dassert(locker); diff --git a/src/mongo/db/operation_context.h b/src/mongo/db/operation_context.h index b9d57951ec1..2c8be360a77 100644 --- a/src/mongo/db/operation_context.h +++ b/src/mongo/db/operation_context.h @@ -123,6 +123,12 @@ public: void setLockState(std::unique_ptr<Locker> locker); /** + * Releases the locker to the caller. Call during OperationContext cleanup or initialization, + * only. + */ + std::unique_ptr<Locker> releaseLockState(); + + /** * Raises a UserException if this operation is in a killed state. */ void checkForInterrupt(); diff --git a/src/mongo/db/operation_context_impl.cpp b/src/mongo/db/operation_context_impl.cpp index 0ccdbbbfbef..7fae6cfa4a7 100644 --- a/src/mongo/db/operation_context_impl.cpp +++ b/src/mongo/db/operation_context_impl.cpp @@ -47,17 +47,38 @@ std::unique_ptr<Locker> newLocker() { return stdx::make_unique<MMAPV1LockerImpl>(); return stdx::make_unique<DefaultLockerImpl>(); } + +class ClientOperationInfo { +public: + std::unique_ptr<Locker>& locker() { + if (!_locker) { + _locker = newLocker(); + } + return _locker; + } + +private: + std::unique_ptr<Locker> _locker; +}; + +const auto clientOperationInfoDecoration = Client::declareDecoration<ClientOperationInfo>(); + } // namespace using std::string; OperationContextImpl::OperationContextImpl(Client* client, unsigned opId) : OperationContext(client, opId) { - setLockState(newLocker()); + setLockState(std::move(clientOperationInfoDecoration(client).locker())); StorageEngine* storageEngine = getServiceContext()->getGlobalStorageEngine(); setRecoveryUnit(storageEngine->newRecoveryUnit(), kNotInUnitOfWork); } +OperationContextImpl::~OperationContextImpl() { + lockState()->assertEmptyAndReset(); + clientOperationInfoDecoration(getClient()).locker() = releaseLockState(); +} + ProgressMeter* OperationContextImpl::setMessage_inlock(const char* msg, const std::string& name, unsigned long long progressMeterTotal, diff --git a/src/mongo/db/operation_context_impl.h b/src/mongo/db/operation_context_impl.h index bc23dcbb089..c6069f01e8b 100644 --- a/src/mongo/db/operation_context_impl.h +++ b/src/mongo/db/operation_context_impl.h @@ -35,6 +35,8 @@ namespace mongo { class OperationContextImpl final : public OperationContext { public: + virtual ~OperationContextImpl(); + virtual ProgressMeter* setMessage_inlock(const char* msg, const std::string& name, unsigned long long progressMeterTotal, diff --git a/src/mongo/db/repl/service_context_repl_mock.cpp b/src/mongo/db/repl/service_context_repl_mock.cpp index ac416a62156..ba88cf149bd 100644 --- a/src/mongo/db/repl/service_context_repl_mock.cpp +++ b/src/mongo/db/repl/service_context_repl_mock.cpp @@ -42,6 +42,7 @@ namespace repl { std::unique_ptr<OperationContext> ServiceContextReplMock::_newOpCtx(Client* client, unsigned opId) { auto opCtx = stdx::make_unique<OperationContextNoop>(client, opId); + opCtx->releaseLockState(); opCtx->setLockState(stdx::make_unique<MMAPV1LockerImpl>()); return std::move(opCtx); } |