/** {@inheritDoc} */
    @Override
    public void addCancelRequestListener(final CancelRequestListener listener) {
      Reject.ifNull(listener);

      boolean invokeImmediately = false;
      synchronized (stateLock) {
        switch (state) {
          case PENDING:
            if (cancelRequestListeners == null) {
              cancelRequestListeners = new LinkedList<>();
            }
            cancelRequestListeners.add(listener);
            break;
          case CANCEL_REQUESTED:
            // Signal immediately outside lock.
            invokeImmediately = true;
            break;
          case TOO_LATE:
          case RESULT_SENT:
          case CANCELLED:
            /*
             * No point in registering the callback since the request
             * can never be cancelled now.
             */
            break;
        }
      }

      if (invokeImmediately) {
        listener.handleCancelRequest(cancelRequestReason);
      }
    }
    private <R extends ExtendedResult> void cancel(
        final LocalizableMessage reason,
        final ExtendedRequest<R> cancelRequest,
        final LdapResultHandler<R> cancelResultHandler,
        final boolean sendResult) {
      Reject.ifNull(reason);

      if (!isCancelSupported) {
        if (cancelResultHandler != null) {
          final Result result = Responses.newGenericExtendedResult(ResultCode.CANNOT_CANCEL);
          cancelResultHandler.handleException(newLdapException(result));
        }
        return;
      }

      List<CancelRequestListener> tmpListeners = null;
      boolean invokeResultHandler = false;
      boolean resultHandlerIsSuccess = false;

      synchronized (stateLock) {
        switch (state) {
          case PENDING:
            /* Switch to CANCEL_REQUESTED state. */
            cancelRequestReason = reason;
            if (cancelResultHandler != null) {
              cancelResultHandlers = new LinkedList<>();
              cancelResultHandlers.add(
                  new ExtendedResultHandlerHolder<R>(cancelRequest, cancelResultHandler));
            }
            tmpListeners = cancelRequestListeners;
            cancelRequestListeners = null;
            state = RequestState.CANCEL_REQUESTED;
            this.sendResult &= sendResult;
            break;
          case CANCEL_REQUESTED:
            /*
             * Cancel already request so listeners already invoked.
             */
            if (cancelResultHandler != null) {
              if (cancelResultHandlers == null) {
                cancelResultHandlers = new LinkedList<>();
              }
              cancelResultHandlers.add(
                  new ExtendedResultHandlerHolder<R>(cancelRequest, cancelResultHandler));
            }
            break;
          case TOO_LATE:
          case RESULT_SENT:
            /*
             * Cannot cancel, so invoke result handler immediately
             * outside of lock.
             */
            if (cancelResultHandler != null) {
              invokeResultHandler = true;
              resultHandlerIsSuccess = false;
            }
            break;
          case CANCELLED:
            /*
             * Multiple cancellation attempts. Clients should not do
             * this, but the cancel will effectively succeed
             * immediately, so invoke result handler immediately outside
             * of lock.
             */
            if (cancelResultHandler != null) {
              invokeResultHandler = true;
              resultHandlerIsSuccess = true;
            }
            break;
        }
      }

      /* Invoke listeners outside of lock. */
      if (tmpListeners != null) {
        for (final CancelRequestListener listener : tmpListeners) {
          listener.handleCancelRequest(reason);
        }
      }

      if (invokeResultHandler) {
        if (resultHandlerIsSuccess) {
          final R result =
              cancelRequest.getResultDecoder().newExtendedErrorResult(ResultCode.SUCCESS, "", "");
          cancelResultHandler.handleResult(result);
        } else {
          final Result result = Responses.newGenericExtendedResult(ResultCode.TOO_LATE);
          cancelResultHandler.handleException(newLdapException(result));
        }
      }
    }