summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLouis Williams <louis.williams@mongodb.com>2018-08-28 16:42:39 -0400
committerLouis Williams <louis.williams@mongodb.com>2018-08-29 16:49:25 -0400
commit57b4b2216ffc7986440b87f9659e970e061b9f33 (patch)
tree90f12bb9d2a3416f32e99f1c89d6c0aa36217acd
parent4cb0742947dabee476c9979cae39c728a21568d5 (diff)
downloadmongo-57b4b2216ffc7986440b87f9659e970e061b9f33.tar.gz
SERVER-36531 Lock acquisition may throw despite use of UninterruptibleLockGuard when WiredTiger tickets are exhausted
-rw-r--r--src/mongo/db/concurrency/d_concurrency_test.cpp47
-rw-r--r--src/mongo/db/concurrency/lock_state.cpp6
2 files changed, 51 insertions, 2 deletions
diff --git a/src/mongo/db/concurrency/d_concurrency_test.cpp b/src/mongo/db/concurrency/d_concurrency_test.cpp
index df07a688104..585a06eba4b 100644
--- a/src/mongo/db/concurrency/d_concurrency_test.cpp
+++ b/src/mongo/db/concurrency/d_concurrency_test.cpp
@@ -1231,6 +1231,53 @@ TEST_F(DConcurrencyTestFixture, TicketAcquireCanBeInterrupted) {
ASSERT_THROWS_CODE(result.get(), AssertionException, ErrorCodes::Interrupted);
}
+TEST_F(DConcurrencyTestFixture, TicketAcquireRespectsUninterruptibleLockGuard) {
+ auto clientOpctxPairs = makeKClientsWithLockers(1);
+ auto opCtx = clientOpctxPairs[0].second.get();
+ // Limit the locker to 0 tickets at a time.
+ UseGlobalThrottling throttle(opCtx, 0);
+
+ // This thread should block and return because it cannot acquire a ticket within the deadline.
+ auto result = runTaskAndKill(opCtx, [&] {
+ UninterruptibleLockGuard noInterrupt(opCtx->lockState());
+ Lock::GlobalRead R(
+ opCtx, Date_t::now() + Milliseconds(1500), Lock::InterruptBehavior::kThrow);
+ ASSERT(!R.isLocked());
+ });
+
+ result.get(); // This should not throw.
+}
+
+TEST_F(DConcurrencyTestFixture, TicketAcquireWithMaxDeadlineRespectsUninterruptibleLockGuard) {
+ auto clientOpctxPairs = makeKClientsWithLockers(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);
+
+ // Take the only ticket available.
+ boost::optional<Lock::GlobalRead> R1;
+ R1.emplace(opCtx1, Date_t::now(), Lock::InterruptBehavior::kThrow);
+ ASSERT(R1->isLocked());
+
+ boost::optional<Lock::GlobalRead> R2;
+
+ // Block until a ticket is available.
+ auto result =
+ runTaskAndKill(opCtx2,
+ [&] {
+ UninterruptibleLockGuard noInterrupt(opCtx2->lockState());
+ R2.emplace(opCtx2, Date_t::max(), Lock::InterruptBehavior::kThrow);
+ },
+ [&] {
+ // Relase the only ticket available to unblock the other thread.
+ R1.reset();
+ });
+
+ result.get(); // This should not throw.
+ ASSERT(R2->isLocked());
+}
+
TEST_F(DConcurrencyTestFixture, TicketReacquireCanBeInterrupted) {
auto clientOpctxPairs = makeKClientsWithLockers(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 6198276f0de..47fd8a408b8 100644
--- a/src/mongo/db/concurrency/lock_state.cpp
+++ b/src/mongo/db/concurrency/lock_state.cpp
@@ -325,9 +325,11 @@ LockResult LockerImpl::_acquireTicket(OperationContext* opCtx, LockMode mode, Da
// If the ticket wait is interrupted, restore the state of the client.
auto restoreStateOnErrorGuard = MakeGuard([&] { _clientState.store(kInactive); });
+
+ OperationContext* interruptible = _uninterruptibleLocksRequested ? nullptr : opCtx;
if (deadline == Date_t::max()) {
- holder->waitForTicket(opCtx);
- } else if (!holder->waitForTicketUntil(opCtx, deadline)) {
+ holder->waitForTicket(interruptible);
+ } else if (!holder->waitForTicketUntil(interruptible, deadline)) {
return LOCK_TIMEOUT;
}
restoreStateOnErrorGuard.Dismiss();