### Elasticsearch Version

8.7.0-SNAPSHOT

### Installed Plugins

_No response_

### Java Version

_bundled_

### OS Version

N/A

### Problem Description

During benchmarking I noticed the following stack trace in the ES logs:

```
[2023-02-10T09:12:28,400][WARN ][r.suppressed             ] [elasticsearch-0] path: /_profiling/stacktraces, params: {}
java.lang.IllegalStateException: Channel is already closed
        at org.elasticsearch.rest.RestController$ResourceHandlingHttpChannel.close(RestController.java:772) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.rest.RestController$ResourceHandlingHttpChannel.sendResponse(RestController.java:759) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.rest.action.RestResponseListener.processResponse(RestResponseListener.java:26) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.rest.action.RestActionListener.onResponse(RestActionListener.java:38) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.rest.action.RestCancellableNodeClient$1.onResponse(RestCancellableNodeClient.java:87) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.rest.action.RestCancellableNodeClient$1.onResponse(RestCancellableNodeClient.java:81) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.client.internal.node.NodeClient$SafelyWrappedActionListener.onResponse(NodeClient.java:160) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.tasks.TaskManager$1.onResponse(TaskManager.java:209) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.tasks.TaskManager$1.onResponse(TaskManager.java:203) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.action.support.ContextPreservingActionListener.onResponse(ContextPreservingActionListener.java:31) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.xpack.security.action.filter.SecurityActionFilter.lambda$applyInternal$2(SecurityActionFilter.java:165) ~[?:?]
        at org.elasticsearch.action.ActionListener$DelegatingFailureActionListener.onResponse(ActionListener.java:250) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.xpack.profiler.TransportGetProfilingAction$DetailsHandler.mayFinish(TransportGetProfilingAction.java:427) ~[?:?]
        at org.elasticsearch.xpack.profiler.TransportGetProfilingAction$DetailsHandler.onExecutableDetailsResponse(TransportGetProfilingAction.java:419) ~[?:?]
        at org.elasticsearch.xpack.profiler.TransportGetProfilingAction$5.onResponse(TransportGetProfilingAction.java:315) ~[?:?]
        at org.elasticsearch.xpack.profiler.TransportGetProfilingAction$5.onResponse(TransportGetProfilingAction.java:312) ~[?:?]
        at org.elasticsearch.client.internal.node.NodeClient$SafelyWrappedActionListener.onResponse(NodeClient.java:160) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.tasks.TaskManager$1.onResponse(TaskManager.java:209) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.tasks.TaskManager$1.onResponse(TaskManager.java:203) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.action.support.ContextPreservingActionListener.onResponse(ContextPreservingActionListener.java:31) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.xpack.security.action.filter.SecurityActionFilter.lambda$applyInternal$2(SecurityActionFilter.java:165) ~[?:?]
        at org.elasticsearch.action.ActionListener$DelegatingFailureActionListener.onResponse(ActionListener.java:250) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.action.get.TransportMultiGetAction$1.finishHim(TransportMultiGetAction.java:155) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.action.get.TransportMultiGetAction$1.onResponse(TransportMultiGetAction.java:138) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.action.get.TransportMultiGetAction$1.onResponse(TransportMultiGetAction.java:130) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.client.internal.node.NodeClient$SafelyWrappedActionListener.onResponse(NodeClient.java:160) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.tasks.TaskManager$1.onResponse(TaskManager.java:209) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.tasks.TaskManager$1.onResponse(TaskManager.java:203) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.action.support.ContextPreservingActionListener.onResponse(ContextPreservingActionListener.java:31) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.xpack.security.action.filter.SecurityActionFilter.lambda$applyInternal$2(SecurityActionFilter.java:165) ~[?:?]
        at org.elasticsearch.action.ActionListener$DelegatingFailureActionListener.onResponse(ActionListener.java:250) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.action.ActionListenerResponseHandler.handleResponse(ActionListenerResponseHandler.java:43) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.transport.TransportService$ContextRestoreResponseHandler.handleResponse(TransportService.java:1367) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.transport.TransportService$DirectResponseChannel.processResponse(TransportService.java:1466) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.transport.TransportService$DirectResponseChannel.sendResponse(TransportService.java:1437) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.transport.TaskTransportChannel.sendResponse(TaskTransportChannel.java:44) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.action.support.ChannelActionListener.lambda$onResponse$0(ChannelActionListener.java:38) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.action.ActionListener.run(ActionListener.java:567) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.action.support.ChannelActionListener.onResponse(ChannelActionListener.java:38) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.action.support.ChannelActionListener.onResponse(ChannelActionListener.java:20) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.action.ActionRunnable$2.accept(ActionRunnable.java:50) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.action.ActionRunnable$2.accept(ActionRunnable.java:47) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.action.ActionRunnable$3.doRun(ActionRunnable.java:72) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.common.util.concurrent.ThreadContext$ContextPreservingAbstractRunnable.doRun(ThreadContext.java:958) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at org.elasticsearch.common.util.concurrent.AbstractRunnable.run(AbstractRunnable.java:26) ~[elasticsearch-8.8.0-SNAPSHOT.jar:?]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) ~[?:?]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[?:?]
        at java.lang.Thread.run(Thread.java:1589) ~[?:?]
```

This indicates that we attempt to send the response twice, see:

https://github.com/elastic/elasticsearch/blob/8ec73d6a559d069e18eadc07db493694df196bd4/server/src/main/java/org/elasticsearch/rest/RestController.java#L759-L761

and 

https://github.com/elastic/elasticsearch/blob/8ec73d6a559d069e18eadc07db493694df196bd4/server/src/main/java/org/elasticsearch/rest/RestController.java#L770-L773

This is caused by a race condition in the profiling plugin. It guards with two atomics against duplicate responses but if the method is called after both atomics have just been decremented to zero, we will send the response twice:

https://github.com/elastic/elasticsearch/blob/8ec73d6a559d069e18eadc07db493694df196bd4/x-pack/plugin/profiler/src/main/java/org/elasticsearch/xpack/profiler/TransportGetProfilingAction.java#L422-L429

### Steps to Reproduce

Issue repeated calls against `/_profiling/stacktrace`. As this is a concurrency issue, it may take a few iterations to manifest.

### Logs (if relevant)

_No response_
x-pack/plugin/profiler/src/main/java/org/elasticsearch/xpack/profiler/TransportGetProfilingAction.java
private final AtomicInteger remainingSlices;
        private final Client client;
        private final GetProfilingResponseBuilder responseBuilder;
        private final ActionListener<GetProfilingResponse> submitListener;
        private final Map<String, StackTrace> stackTracePerId;
        
        
        
        private final Set<String> stackFrameIds = new ConcurrentSkipListSet<>();
        private final Set<String> executableIds = new ConcurrentSkipListSet<>();
        private final AtomicInteger totalFrames = new AtomicInteger();
        private final long start = System.nanoTime();
        private StackTraceHandler(
            Client client,
            GetProfilingResponseBuilder responseBuilder,
            ActionListener<GetProfilingResponse> submitListener,
            int stackTraceCount,
            int slices
        ) {
            this.stackTracePerId = new ConcurrentHashMap<>(stackTraceCount);
            this.remainingSlices = new AtomicInteger(slices);
            this.client = client;
            this.responseBuilder = responseBuilder;
            this.submitListener = submitListener;
        }
        public void onResponse(MultiGetResponse multiGetItemResponses) {