docs: @CanIgnoreReturnValue
    @Override
    public boolean cancel(boolean mayInterruptIfRunning) {
      Object localValue = value;
      boolean rValue = false;
      if (localValue == null | localValue instanceof AbstractFuture.SetFuture) {
        // Try to delay allocating the exception. At this point we may still lose the CAS, but it is
        // certainly less likely.
        Throwable cause =
            GENERATE_CANCELLATION_CAUSES
                ? new CancellationException("Future.cancel() was called.")
                : null;
        Object valueToSet = new Cancellation(mayInterruptIfRunning, cause);
        AbstractFuture<?> abstractFuture = this;
        while (true) {
          if (ATOMIC_HELPER.casValue(abstractFuture, localValue, valueToSet)) {
            rValue = true;
            // We call interuptTask before calling complete(), which is consistent with
            // FutureTask
            if (mayInterruptIfRunning) {
              abstractFuture.interruptTask();
            }
            complete(abstractFuture);
            if (localValue instanceof AbstractFuture.SetFuture) {
              // propagate cancellation to the future set in setfuture, this is racy, and we don't
              // care if we are successful or not.
              ListenableFuture<?> futureToPropagateTo =
                  ((AbstractFuture.SetFuture) localValue).future;
              if (futureToPropagateTo instanceof TrustedFuture) {
                // If the future is a TrustedFuture then we specifically avoid calling cancel()
                // this has 2 benefits
                // 1. for long chains of futures strung together with setFuture we consume less stack
                // 2. we avoid allocating Cancellation objects at every level of the cancellation
                //    chain
                // We can only do this for TrustedFuture, because TrustedFuture.cancel is final and
                // does nothing but delegate to this method.
                AbstractFuture<?> trusted = (AbstractFuture<?>) futureToPropagateTo;
                localValue = trusted.value;
                if (localValue == null | localValue instanceof AbstractFuture.SetFuture) {
                  abstractFuture = trusted;
                  continue;  // loop back up and try to complete the new future
                }
              } else {
                // not a TrustedFuture, call cancel directly.
                futureToPropagateTo.cancel(mayInterruptIfRunning);
              }
            }
            break;
          }
          // obj changed, reread
          localValue = abstractFuture.value;
          if (!(localValue instanceof AbstractFuture.SetFuture)) {
            // obj cannot be null at this point, because value can only change from null to non-null.
            // So if value changed (and it did since we lost the CAS), then it cannot be null and
            // since it isn't a SetFuture, then the future must be done and we should exit the loop
            break;
          }
        }
      }
      return rValue;
    }