code: public boolean block(final long identifier, final Runnable asyncProcessor)
          throws InterruptedException, InvalidIdentifierException {
  
      final ComplexCondition call = allocate();
      if (call.isHeldByCurrentThread()) {
        throw new RuntimeException("release() must not be called on same thread as block() to prevent deadlock");
      }
  
      try {
        call.lock();
        // Add the call identifier to the sleeper map so release() can identify this instantiation.
        addSleeper(identifier, call);
        // Invoke the caller's asynchronous processing while holding the lock
        // so a wakeup cannot occur before the caller sleeps.
        if (executor == null) {
          asyncProcessor.run();
        } else {
          executor.execute(asyncProcessor);
        }
        // Put the caller to sleep until the ack comes back. Note: we atomically
        // give up the look as the caller sleeps and atomically reacquire the
        // the lock as we wake up.
        LOG.log(Level.FINER, "Putting caller to sleep on identifier [{0}]", identifier);
        final boolean timeoutOccurred = !call.await();
        if (timeoutOccurred) {
          LOG.log(Level.WARNING, "Call timed out on identifier [{0}]", identifier);
        }
        return timeoutOccurred;
      } finally {
        // Whether or not the call completed successfully, always remove
        // the call from the sleeper map, release the lock and cleanup.
        try {
          removeSleeper(identifier);
          recycle(call);
        } finally {
          call.unlock();
        }
      }
    }
docs: Put the caller to sleep on a specific release identifier.
  @param identifier The identifier required to awake the caller via the {@code release()} method.
  @param asyncProcessor A {@code Runnable} object that initiates the asynchronous
  processing associated with the call. This will occur inside the condition lock
  to prevent the processing from generating the signal before the calling thread blocks.
  Error conditions should be handled by throwing an exception which the caller
  will catch. The caller can retrieve the results of the processing by calling
  {@code asyndProcessor.get()}.
  @return A boolean value that indicates whether or not a timeout or error occurred.
  @throws InterruptedException The thread was interrupted while waiting on a condition.
  @throws InvalidIdentifierException The identifier parameter is invalid.