docs: private <T> T complete(CompletableFuture<T> future, int acquirePermits) {
      AtomicBoolean needRelease = new AtomicBoolean(false);
      try {
        return future.thenApply(value -> {
          if (needRelease.get()) {
            if (acquirePermits > 0) {
              asyncSemaphore.release(acquirePermits);
            }
          }
          return value;
        }).get(timeout.toMillis(), TimeUnit.MILLISECONDS);
      } catch (InterruptedException | PrimitiveException.Interrupted e) {
        needRelease.set(acquirePermits > 0);
        Thread.currentThread().interrupt();
        throw new PrimitiveException.Interrupted();
      } catch (TimeoutException | PrimitiveException.Timeout e) {
        needRelease.set(acquirePermits > 0);
        throw new PrimitiveException.Timeout();
      } catch (ExecutionException e) {
        needRelease.set(acquirePermits > 0);
        Throwable cause = Throwables.getRootCause(e);
        if (cause instanceof PrimitiveException) {
          throw (PrimitiveException) cause;
        } else {
          throw new PrimitiveException(cause);
        }
      }
    }