From 32a067938731d184b271ba3d7f43ca6727e3109c Mon Sep 17 00:00:00 2001 From: Jason Carey Date: Wed, 27 Feb 2019 13:40:18 -0500 Subject: SERVER-39427 Modify interrupt semantics for opCtx * rename opCtx->runWithoutInterruption to runWithoutInterruptionExceptAtGlobalShutdown * add a opCtx->setIsExecutingShutdown method which makes the op immune to all forms of interruption, including global shutdown This clarifies what opCtx->runWithoutInterruption actually did and offers an escape hatch that turns off interruption at process exit for the thread doing cleanup. --- src/mongo/db/operation_context_test.cpp | 75 +++++++++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 4 deletions(-) (limited to 'src/mongo/db/operation_context_test.cpp') diff --git a/src/mongo/db/operation_context_test.cpp b/src/mongo/db/operation_context_test.cpp index 9c8c40ec63e..8195a57cf26 100644 --- a/src/mongo/db/operation_context_test.cpp +++ b/src/mongo/db/operation_context_test.cpp @@ -210,7 +210,7 @@ TEST(OperationContextTest, IgnoreInterruptsWorks) { ASSERT_THROWS_CODE(opCtx->checkForInterrupt(), DBException, ErrorCodes::BadValue); ASSERT_EQUALS(opCtx->getKillStatus(), ErrorCodes::BadValue); - opCtx->runWithoutInterruption([&] { + opCtx->runWithoutInterruptionExceptAtGlobalShutdown([&] { ASSERT_OK(opCtx->checkForInterruptNoAssert()); ASSERT_OK(opCtx->getKillStatus()); }); @@ -218,6 +218,33 @@ TEST(OperationContextTest, IgnoreInterruptsWorks) { ASSERT_THROWS_CODE(opCtx->checkForInterrupt(), DBException, ErrorCodes::BadValue); ASSERT_EQUALS(opCtx->getKillStatus(), ErrorCodes::BadValue); + + serviceCtx->setKillAllOperations(); + + opCtx->runWithoutInterruptionExceptAtGlobalShutdown([&] { + ASSERT_THROWS_CODE( + opCtx->checkForInterrupt(), DBException, ErrorCodes::InterruptedAtShutdown); + }); +} + +TEST(OperationContextTest, setIsExecutingShutdownWorks) { + auto serviceCtx = ServiceContext::make(); + auto client = serviceCtx->makeClient("OperationContextTest"); + auto opCtx = client->makeOperationContext(); + + opCtx->markKilled(ErrorCodes::BadValue); + ASSERT_THROWS_CODE(opCtx->checkForInterrupt(), DBException, ErrorCodes::BadValue); + ASSERT_EQUALS(opCtx->getKillStatus(), ErrorCodes::BadValue); + + opCtx->setIsExecutingShutdown(); + + ASSERT_OK(opCtx->checkForInterruptNoAssert()); + ASSERT_OK(opCtx->getKillStatus()); + + serviceCtx->setKillAllOperations(); + + ASSERT_OK(opCtx->checkForInterruptNoAssert()); + ASSERT_OK(opCtx->getKillStatus()); } class OperationDeadlineTests : public unittest::Test { @@ -471,7 +498,7 @@ TEST_F(OperationDeadlineTests, DeadlineAfterIgnoreInterruptsReopens) { mockClock->now() + Milliseconds(500), ErrorCodes::ExceededTimeLimit, [&] { ASSERT_OK(opCtx->checkForInterruptNoAssert()); - opCtx->runWithoutInterruption([&] { + opCtx->runWithoutInterruptionExceptAtGlobalShutdown([&] { try { opCtx->runWithDeadline( mockClock->now() + Seconds(1), ErrorCodes::ExceededTimeLimit, [&] { @@ -500,12 +527,52 @@ TEST_F(OperationDeadlineTests, DeadlineAfterIgnoreInterruptsReopens) { ASSERT(reachedC); } +TEST_F(OperationDeadlineTests, DeadlineAfterSetIsExecutingShutdownReopens) { + auto opCtx = client->makeOperationContext(); + + bool reachedA = false; + bool reachedB = false; + bool reachedC = false; + + try { + opCtx->runWithDeadline( + mockClock->now() + Milliseconds(500), ErrorCodes::ExceededTimeLimit, [&] { + ASSERT_OK(opCtx->checkForInterruptNoAssert()); + + opCtx->setIsExecutingShutdown(); + try { + opCtx->runWithDeadline( + mockClock->now() + Seconds(1), ErrorCodes::ExceededTimeLimit, [&] { + ASSERT_OK(opCtx->checkForInterruptNoAssert()); + ASSERT_OK(opCtx->getKillStatus()); + mockClock->advance(Milliseconds(750)); + ASSERT_OK(opCtx->checkForInterruptNoAssert()); + mockClock->advance(Milliseconds(500)); + reachedA = true; + opCtx->checkForInterrupt(); + }); + } catch (const ExceptionFor&) { + opCtx->checkForInterrupt(); + reachedB = true; + } + + opCtx->checkForInterrupt(); + }); + } catch (const ExceptionFor&) { + reachedC = true; + } + + ASSERT(reachedA); + ASSERT(reachedB); + ASSERT_FALSE(reachedC); +} + TEST_F(OperationDeadlineTests, DeadlineAfterRunWithoutInterruptSeesViolatedMaxMS) { auto opCtx = client->makeOperationContext(); opCtx->setDeadlineByDate(mockClock->now() + Milliseconds(100), ErrorCodes::MaxTimeMSExpired); - ASSERT_THROWS_CODE(opCtx->runWithoutInterruption([&] { + ASSERT_THROWS_CODE(opCtx->runWithoutInterruptionExceptAtGlobalShutdown([&] { opCtx->runWithDeadline( mockClock->now() + Milliseconds(200), ErrorCodes::ExceededTimeLimit, [&] { mockClock->advance(Milliseconds(300)); @@ -521,7 +588,7 @@ TEST_F(OperationDeadlineTests, DeadlineAfterRunWithoutInterruptDoesntSeeUnviolat opCtx->setDeadlineByDate(mockClock->now() + Milliseconds(200), ErrorCodes::MaxTimeMSExpired); - ASSERT_THROWS_CODE(opCtx->runWithoutInterruption([&] { + ASSERT_THROWS_CODE(opCtx->runWithoutInterruptionExceptAtGlobalShutdown([&] { opCtx->runWithDeadline( mockClock->now() + Milliseconds(100), ErrorCodes::ExceededTimeLimit, [&] { mockClock->advance(Milliseconds(150)); -- cgit v1.2.1