summaryrefslogtreecommitdiff
path: root/src/mongo/db/service_context.cpp
diff options
context:
space:
mode:
authorAmirsaman Memaripour <amirsaman.memaripour@mongodb.com>2020-04-29 21:03:20 -0400
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-05-14 14:36:40 +0000
commitb77252a641838f43e3c09796a09be22ab8f44e1c (patch)
tree158d46c351017adb5ce393759fa90af4e02473de /src/mongo/db/service_context.cpp
parentc9a0717687ed1dc70efac8dbaff06b14551ee6f3 (diff)
downloadmongo-b77252a641838f43e3c09796a09be22ab8f44e1c.tar.gz
SERVER-47802 Destroy opCtx after responding to clients
Postpone destruction of opCtx until after responding to clients to reduce the cost of destroying opCtx on the critical execution path.
Diffstat (limited to 'src/mongo/db/service_context.cpp')
-rw-r--r--src/mongo/db/service_context.cpp57
1 files changed, 45 insertions, 12 deletions
diff --git a/src/mongo/db/service_context.cpp b/src/mongo/db/service_context.cpp
index a0df164a749..58f440af121 100644
--- a/src/mongo/db/service_context.cpp
+++ b/src/mongo/db/service_context.cpp
@@ -282,20 +282,12 @@ ServiceContext::UniqueOperationContext ServiceContext::makeOperationContext(Clie
void ServiceContext::OperationContextDeleter::operator()(OperationContext* opCtx) const {
auto client = opCtx->getClient();
- if (client->session()) {
- _numCurrentOps.subtractAndFetch(1);
- }
- auto service = client->getServiceContext();
+ invariant(client);
- {
- stdx::lock_guard lk(service->_mutex);
- service->_clientByOperationId.erase(opCtx->getOpID());
- }
+ auto service = client->getServiceContext();
+ invariant(service);
- {
- stdx::lock_guard<Client> lk(*client);
- client->resetOperationContext();
- }
+ service->_delistOperation(opCtx);
opCtx->getBaton()->detach();
onDestroy(opCtx, service->_clientObservers);
@@ -369,6 +361,47 @@ void ServiceContext::killOperation(WithLock, OperationContext* opCtx, ErrorCodes
}
}
+void ServiceContext::_delistOperation(OperationContext* opCtx) noexcept {
+ // Removing `opCtx` from `_clientByOperationId` must always precede removing the `opCtx` from
+ // its client to prevent situations that another thread could use the service context to get a
+ // hold of an `opCtx` that has been removed from its client.
+ {
+ stdx::lock_guard lk(_mutex);
+ if (_clientByOperationId.erase(opCtx->getOpID()) != 1) {
+ // Another thread has already delisted this `opCtx`.
+ return;
+ }
+ }
+
+ auto client = opCtx->getClient();
+ stdx::lock_guard clientLock(*client);
+ // Reaching here implies this call was able to remove the `opCtx` from ServiceContext.
+
+ // Assigning a new opCtx to the client must never precede the destruction of any existing opCtx
+ // that references the client.
+ invariant(client->getOperationContext() == opCtx);
+ client->resetOperationContext();
+
+ if (client->session()) {
+ _numCurrentOps.subtractAndFetch(1);
+ }
+}
+
+void ServiceContext::killAndDelistOperation(OperationContext* opCtx,
+ ErrorCodes::Error killCode) noexcept {
+
+ auto client = opCtx->getClient();
+ invariant(client);
+
+ auto service = client->getServiceContext();
+ invariant(service == this);
+
+ _delistOperation(opCtx);
+
+ stdx::lock_guard clientLock(*client);
+ killOperation(clientLock, opCtx, killCode);
+}
+
void ServiceContext::unsetKillAllOperations() {
_globalKill.store(false);
}