diff options
author | Tess Avitabile <tess.avitabile@mongodb.com> | 2018-02-22 15:15:30 -0500 |
---|---|---|
committer | Tess Avitabile <tess.avitabile@mongodb.com> | 2018-02-26 10:36:28 -0500 |
commit | 87f1ccd840f55653023a4c849175061d33a444a6 (patch) | |
tree | f1cd845028a7037c3f6f76360fbe96006b6b6f0e /src/mongo/db/concurrency | |
parent | 0ef1e13db4f3d217ddc6de7ffc46842a4fc9889a (diff) | |
download | mongo-87f1ccd840f55653023a4c849175061d33a444a6.tar.gz |
SERVER-33289 Release WT tickets when stashing RecoveryUnit
Diffstat (limited to 'src/mongo/db/concurrency')
-rw-r--r-- | src/mongo/db/concurrency/d_concurrency_test.cpp | 43 | ||||
-rw-r--r-- | src/mongo/db/concurrency/lock_state.cpp | 64 | ||||
-rw-r--r-- | src/mongo/db/concurrency/lock_state.h | 15 | ||||
-rw-r--r-- | src/mongo/db/concurrency/locker.h | 12 | ||||
-rw-r--r-- | src/mongo/db/concurrency/locker_noop.h | 8 |
5 files changed, 126 insertions, 16 deletions
diff --git a/src/mongo/db/concurrency/d_concurrency_test.cpp b/src/mongo/db/concurrency/d_concurrency_test.cpp index 67b903c51af..ca6f47643e7 100644 --- a/src/mongo/db/concurrency/d_concurrency_test.cpp +++ b/src/mongo/db/concurrency/d_concurrency_test.cpp @@ -1027,6 +1027,49 @@ TEST_F(DConcurrencyTestFixture, NoThrottlingWhenNotAcquiringTickets) { ASSERT(R2.isLocked()); } +TEST_F(DConcurrencyTestFixture, ReleaseAndReacquireTicket) { + auto clientOpctxPairs = makeKClientsWithLockers<DefaultLockerImpl>(2); + auto opctx1 = clientOpctxPairs[0].second.get(); + auto opctx2 = clientOpctxPairs[1].second.get(); + // Limit the locker to 1 ticket at a time. + UseGlobalThrottling throttle(opctx1, 1); + + Lock::GlobalRead R1(opctx1, Date_t::now()); + ASSERT(R1.isLocked()); + + { + // A second Locker should not be able to acquire a ticket. + Lock::GlobalRead R2(opctx2, Date_t::now()); + ASSERT(!R2.isLocked()); + } + + opctx1->lockState()->releaseTicket(); + + { + // Now a second Locker can acquire a ticket. + Lock::GlobalRead R2(opctx2, Date_t::now()); + ASSERT(R2.isLocked()); + } + + opctx1->lockState()->reacquireTicket(); + + { + // Now a second Locker cannot acquire a ticket. + Lock::GlobalRead R2(opctx2, Date_t::now()); + ASSERT(!R2.isLocked()); + } +} + +TEST_F(DConcurrencyTestFixture, LockerWithReleasedTicketCanBeUnlocked) { + auto clientOpctxPairs = makeKClientsWithLockers<DefaultLockerImpl>(2); + auto opctx1 = clientOpctxPairs[0].second.get(); + + Lock::GlobalRead R1(opctx1, Date_t::now()); + ASSERT(R1.isLocked()); + + opctx1->lockState()->releaseTicket(); +} + TEST_F(DConcurrencyTestFixture, DBLockTimeout) { auto clientOpctxPairs = makeKClientsWithLockers<DefaultLockerImpl>(2); auto opctx1 = clientOpctxPairs[0].second.get(); diff --git a/src/mongo/db/concurrency/lock_state.cpp b/src/mongo/db/concurrency/lock_state.cpp index 4efdb1c49b6..a6dac2d5d5f 100644 --- a/src/mongo/db/concurrency/lock_state.cpp +++ b/src/mongo/db/concurrency/lock_state.cpp @@ -297,21 +297,37 @@ LockResult LockerImpl<IsForMMAPV1>::lockGlobal(LockMode mode) { } template <bool IsForMMAPV1> +void LockerImpl<IsForMMAPV1>::reacquireTicket() { + invariant(_modeForTicket != MODE_NONE); + auto acquireTicketResult = _acquireTicket(_modeForTicket, Date_t::max()); + invariant(acquireTicketResult == LOCK_OK); +} + +template <bool IsForMMAPV1> +LockResult LockerImpl<IsForMMAPV1>::_acquireTicket(LockMode mode, Date_t deadline) { + const bool reader = isSharedLockMode(mode); + auto holder = shouldAcquireTicket() ? ticketHolders[mode] : nullptr; + if (holder) { + _clientState.store(reader ? kQueuedReader : kQueuedWriter); + if (deadline == Date_t::max()) { + holder->waitForTicket(); + } else if (!holder->waitForTicketUntil(deadline)) { + _clientState.store(kInactive); + return LOCK_TIMEOUT; + } + } + _clientState.store(reader ? kActiveReader : kActiveWriter); + return LOCK_OK; +} + +template <bool IsForMMAPV1> LockResult LockerImpl<IsForMMAPV1>::_lockGlobalBegin(LockMode mode, Date_t deadline) { dassert(isLocked() == (_modeForTicket != MODE_NONE)); if (_modeForTicket == MODE_NONE) { - const bool reader = isSharedLockMode(mode); - auto holder = shouldAcquireTicket() ? ticketHolders[mode] : nullptr; - if (holder) { - _clientState.store(reader ? kQueuedReader : kQueuedWriter); - if (deadline == Date_t::max()) { - holder->waitForTicket(); - } else if (!holder->waitForTicketUntil(deadline)) { - _clientState.store(kInactive); - return LOCK_TIMEOUT; - } + auto acquireTicketResult = _acquireTicket(mode, deadline); + if (acquireTicketResult != LOCK_OK) { + return acquireTicketResult; } - _clientState.store(reader ? kActiveReader : kActiveWriter); _modeForTicket = mode; } const LockResult result = lockBegin(resourceIdGlobal, mode); @@ -806,16 +822,32 @@ LockResult LockerImpl<IsForMMAPV1>::lockComplete(ResourceId resId, } template <bool IsForMMAPV1> +void LockerImpl<IsForMMAPV1>::releaseTicket() { + invariant(_modeForTicket != MODE_NONE); + _releaseTicket(); +} + +template <bool IsForMMAPV1> +void LockerImpl<IsForMMAPV1>::_releaseTicket() { + auto holder = shouldAcquireTicket() ? ticketHolders[_modeForTicket] : nullptr; + if (holder) { + holder->release(); + } + _clientState.store(kInactive); +} + +template <bool IsForMMAPV1> bool LockerImpl<IsForMMAPV1>::_unlockImpl(LockRequestsMap::Iterator* it) { if (globalLockManager.unlock(it->objAddr())) { if (it->key() == resourceIdGlobal) { invariant(_modeForTicket != MODE_NONE); - auto holder = shouldAcquireTicket() ? ticketHolders[_modeForTicket] : nullptr; - _modeForTicket = MODE_NONE; - if (holder) { - holder->release(); + + // We may have already released our ticket through a call to releaseTicket(). + if (_clientState.load() != kInactive) { + _releaseTicket(); } - _clientState.store(kInactive); + + _modeForTicket = MODE_NONE; } scoped_spinlock scopedLock(_lock); diff --git a/src/mongo/db/concurrency/lock_state.h b/src/mongo/db/concurrency/lock_state.h index de93aa65f0c..645a9c094ae 100644 --- a/src/mongo/db/concurrency/lock_state.h +++ b/src/mongo/db/concurrency/lock_state.h @@ -144,6 +144,10 @@ public: virtual void restoreLockState(const LockSnapshot& stateToRestore); + virtual void releaseTicket(); + + virtual void reacquireTicket(); + /** * Allows for lock requests to be requested in a non-blocking way. There can be only one * outstanding pending lock request per locker object. @@ -211,6 +215,17 @@ private: */ bool _shouldDelayUnlock(ResourceId resId, LockMode mode) const; + /** + * Releases the ticket for the Locker. + */ + void _releaseTicket(); + + /** + * Acquires a ticket for the Locker under 'mode'. Returns LOCK_TIMEOUT if it cannot acquire a + * ticket within 'deadline'. + */ + LockResult _acquireTicket(LockMode mode, Date_t deadline); + // Used to disambiguate different lockers const LockerId _id; diff --git a/src/mongo/db/concurrency/locker.h b/src/mongo/db/concurrency/locker.h index ff133800115..700f2b6856b 100644 --- a/src/mongo/db/concurrency/locker.h +++ b/src/mongo/db/concurrency/locker.h @@ -300,6 +300,18 @@ public: */ virtual void restoreLockState(const LockSnapshot& stateToRestore) = 0; + /** + * Releases the ticket associated with the Locker. This allows locks to be held without + * contributing to reader/writer throttling. + */ + virtual void releaseTicket() = 0; + + /** + * Reacquires a ticket for the Locker. This must only be called after releaseTicket(). It + * restores the ticket under its previous LockMode. + */ + virtual void reacquireTicket() = 0; + // // These methods are legacy from LockerImpl and will eventually go away or be converted to // calls into the Locker methods diff --git a/src/mongo/db/concurrency/locker_noop.h b/src/mongo/db/concurrency/locker_noop.h index 208c76fd673..ac1419c0597 100644 --- a/src/mongo/db/concurrency/locker_noop.h +++ b/src/mongo/db/concurrency/locker_noop.h @@ -137,6 +137,14 @@ public: invariant(false); } + virtual void releaseTicket() { + invariant(false); + } + + virtual void reacquireTicket() { + invariant(false); + } + virtual void dump() const { invariant(false); } |