docs: ListenableFuture<Void> closeAsync() {
      ListenableFuture<Void> retFuture = null;
      synchronized (lock) {
        if (closureFuture != null) {
          throw new IllegalStateException("Close has already been invoked");
        }
        // Fail all pending waiters.
        Waiter waiter = readWaiters.poll();
        while (waiter != null) {
          waiter.put(newSpannerException(ErrorCode.INTERNAL, "Client has been closed"));
          waiter = readWaiters.poll();
        }
        waiter = readWriteWaiters.poll();
        while (waiter != null) {
          waiter.put(newSpannerException(ErrorCode.INTERNAL, "Client has been closed"));
          waiter = readWriteWaiters.poll();
        }
        closureFuture = SettableFuture.create();
        retFuture = closureFuture;
        pendingClosure =
            totalSessions() + numSessionsBeingCreated + 1 /* For pool maintenance thread */;
  
        poolMaintainer.close();
        readSessions.clear();
        writePreparedSessions.clear();
        for (final PooledSession session : ImmutableList.copyOf(allSessions)) {
          if (session.leakedException != null) {
            logger.log(Level.WARNING, "Leaked session", session.leakedException);
          }
          if (session.state != SessionState.CLOSING) {
            closeSessionAsync(session);
          }
        }
      }
      retFuture.addListener(
          new Runnable() {
            @Override
            public void run() {
              executorFactory.release(executor);
            }
          },
          MoreExecutors.directExecutor());
  
      return retFuture;
    }