From cc954e9e1d88b30d1ab89ee3bbbd9db0bb15263d Mon Sep 17 00:00:00 2001 From: Charlie Swanson Date: Wed, 5 Apr 2017 11:35:23 -0400 Subject: SERVER-25694 Eliminate race in PlanExecutor cleanup. Ensures that a collection lock is held in at least MODE_IS while deregistering a PlanExecutor from the cursor manager. Introduces new PlanExecutor::dispose() and ClientCursor::dispose() methods that must be called before destruction of those classes, and ensures they are called before destruction. These calls will thread an OperationContext all the way through to DocumentSource::dispose() for each stage in a Pipeline, which will give DocumentSourceCursor a chance to acquire locks and deregister its PlanExecutor if necessary. --- src/mongo/db/exec/plan_stage.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'src/mongo/db/exec/plan_stage.h') diff --git a/src/mongo/db/exec/plan_stage.h b/src/mongo/db/exec/plan_stage.h index 6b3a67a2c04..33204c84839 100644 --- a/src/mongo/db/exec/plan_stage.h +++ b/src/mongo/db/exec/plan_stage.h @@ -261,6 +261,29 @@ public: */ void invalidate(OperationContext* opCtx, const RecordId& dl, InvalidationType type); + /* + * Releases any resources held by this stage. It is an error to use a PlanStage in any way after + * calling dispose(). Does not throw exceptions. + * + * Propagates to all children, then calls doDispose(). + */ + void dispose(OperationContext* opCtx) { + try { + // We may or may not be attached during disposal. We can't call + // reattachToOperationContext() + // directly, since that will assert that '_opCtx' is not set. + _opCtx = opCtx; + invariant(!_opCtx || opCtx == opCtx); + + for (auto&& child : _children) { + child->dispose(opCtx); + } + doDispose(); + } catch (...) { + std::terminate(); + } + } + /** * Retrieve a list of this stage's children. This stage keeps ownership of * its children. @@ -353,6 +376,11 @@ protected: */ virtual void doReattachToOperationContext() {} + /** + * Does stage-specific destruction. Must not throw exceptions. + */ + virtual void doDispose() {} + /** * Does the stage-specific invalidation work. */ -- cgit v1.2.1