diff options
author | Louis Williams <louis.williams@mongodb.com> | 2018-08-28 16:42:39 -0400 |
---|---|---|
committer | Louis Williams <louis.williams@mongodb.com> | 2018-08-29 16:49:25 -0400 |
commit | 57b4b2216ffc7986440b87f9659e970e061b9f33 (patch) | |
tree | 90f12bb9d2a3416f32e99f1c89d6c0aa36217acd | |
parent | 4cb0742947dabee476c9979cae39c728a21568d5 (diff) | |
download | mongo-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.cpp | 47 | ||||
-rw-r--r-- | src/mongo/db/concurrency/lock_state.cpp | 6 |
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(); |