public void asyncClose(@NotNull final Socket socket, @NotNull final String address,
      @NotNull final Runnable runBeforeClose,
      @NotNull final Runnable runAfterClose) {
    if (socket.isClosed()) {
      return;
    }
    boolean doItInline = false;
    try {
      Future submittedTask = CompletableFuture.completedFuture(this);
      closedLock.lock();
      try {
        if (closed) {
          // this SocketCloser has been closed so do a synchronous, inline, close
          doItInline = true;
        } else {
          submittedTask = asyncExecute(address, () -> {
            Thread.currentThread().setName("AsyncSocketCloser for " + address);
            try {
              runBeforeClose.run();
              inlineClose(socket);
              runAfterClose.run();
            } finally {
              Thread.currentThread().setName("unused AsyncSocketCloser");
            }
          });
        }
      } finally {
        closedLock.unlock();
      }
      if (!doItInline) {
        waitForFutureTaskWithTimeout(submittedTask);
        return;
      }
    } catch (RejectedExecutionException | OutOfMemoryError ignore) {
      // If we can't start a thread to close the socket just do it inline.
      // See bug 50573.
    }
    runBeforeClose.run();
    inlineClose(socket);
    runAfterClose.run();
  }
Closes the specified socket in a background thread. In some cases we see close hang.
Depending on how the SocketCloser is configured (see ASYNC_CLOSE_WAIT_MILLISECONDS)
this method may block for a certain amount of time. If it is called after the SocketCloser is
closed then a normal synchronous close is done.

@param socket the socket to close
@param address key used to determine which executor to use. Usually the address of a peer or
       client
@param runBeforeClose a Runnable with stuff to execute before the socket is closed
@param runAfterClose a Runnable with stuff to execute after the socket is closed
geode-core/src/main/java/org/apache/geode/internal/net/SocketCloser.java