summaryrefslogtreecommitdiff
path: root/src/mongo/db/concurrency
diff options
context:
space:
mode:
authorTess Avitabile <tess.avitabile@mongodb.com>2018-02-22 15:15:30 -0500
committerTess Avitabile <tess.avitabile@mongodb.com>2018-02-26 10:36:28 -0500
commit87f1ccd840f55653023a4c849175061d33a444a6 (patch)
treef1cd845028a7037c3f6f76360fbe96006b6b6f0e /src/mongo/db/concurrency
parent0ef1e13db4f3d217ddc6de7ffc46842a4fc9889a (diff)
downloadmongo-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.cpp43
-rw-r--r--src/mongo/db/concurrency/lock_state.cpp64
-rw-r--r--src/mongo/db/concurrency/lock_state.h15
-rw-r--r--src/mongo/db/concurrency/locker.h12
-rw-r--r--src/mongo/db/concurrency/locker_noop.h8
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);
}