/*
 * Copyright 2015 The gRPC Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.grpc;

import io.grpc.Context.CheckReturnValue;
import io.grpc.PersistentHashArrayMappedTrie.Node;
import java.io.Closeable;
import java.util.ArrayList;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * A context propagation mechanism which can carry scoped-values across API boundaries and between
 * threads. Examples of state propagated via context include:
 * <ul>
 *   <li>Security principals and credentials.</li>
 *   <li>Local and distributed tracing information.</li>
 * </ul>
 *
 * <p>A Context object can be {@link #attach attached} to the {@link Storage}, which effectively
 * forms a <b>scope</b> for the context.  The scope is bound to the current thread.  Within a scope,
 * its Context is accessible even across API boundaries, through {@link #current}.  The scope is
 * later exited by {@link #detach detaching} the Context.
 *
 * <p>Context objects are immutable and inherit state from their parent. To add or overwrite the
 * current state a new context object must be created and then attached, replacing the previously
 * bound context. For example:
 *
 * <pre>
 *   Context withCredential = Context.current().withValue(CRED_KEY, cred);
 *   withCredential.run(new Runnable() {
 *     public void run() {
 *        readUserRecords(userId, CRED_KEY.get());
 *     }
 *   });
 * </pre>
 *
 * <p>Contexts are also used to represent a scoped unit of work. When the unit of work is done the
 * context must be cancelled. This cancellation will cascade to all descendant contexts. You can
 * add a {@link CancellationListener} to a context to be notified when it or one of its ancestors
 * has been cancelled. Cancellation does not release the state stored by a context and it's
 * perfectly valid to {@link #attach()} an already cancelled context to make it current. To cancel a
 * context (and its descendants) you first create a {@link CancellableContext} and when you need to
 * signal cancellation call {@link CancellableContext#cancel} or {@link
 * CancellableContext#detachAndCancel}.
 *
 * <p>Contexts can also be created with a timeout relative to the system nano clock which will
 * cause it to automatically cancel at the desired time.
 *
 *
 * <p>Notes and cautions on use:
 * <ul>
 *    <li>Every {@code attach()} should have a {@code detach()} in the same method. And every
 * CancellableContext should be cancelled at some point. Breaking these rules may lead to memory
 * leaks.
 *    <li>While Context objects are immutable they do not place such a restriction on the state
 * they store.</li>
 *    <li>Context is not intended for passing optional parameters to an API and developers should
 * take care to avoid excessive dependence on context when designing an API.</li>
 *    <li>Do not mock this class.  Use {@link #ROOT} for a non-null instance.
 * </ul>
 */
/* @DoNotMock("Use ROOT for a non-null Context") // commented out to avoid dependencies  */
@CheckReturnValue
public class Context {

  static final Logger log = Logger.getLogger(Context.class.getName());

  // Long chains of contexts are suspicious and usually indicate a misuse of Context.
  // The threshold is arbitrarily chosen.
  // VisibleForTesting
  static final int CONTEXT_DEPTH_WARN_THRESH = 1000;

  /**
   * The logical root context which is the ultimate ancestor of all contexts. This context
   * is not cancellable and so will not cascade cancellation or retain listeners.
   *
   * <p>Never assume this is the default context for new threads, because {@link Storage} may define
   * a default context that is different from ROOT.
   */
  public static final Context ROOT = new Context();

  // Visible For testing
  static Storage storage() {
    return LazyStorage.storage;
  }

  // Lazy-loaded storage. Delaying storage initialization until after class initialization makes it
  // much easier to avoid circular loading since there can still be references to Context as long as
  // they don't depend on storage, like key() and currentContextExecutor(). It also makes it easier
  // to handle exceptions.
  private static final class LazyStorage {
    static final Storage storage;

    static {
      AtomicReference<Throwable> deferredStorageFailure = new AtomicReference<>();
      storage = createStorage(deferredStorageFailure);
      Throwable failure = deferredStorageFailure.get();
      // Logging must happen after storage has been set, as loggers may use Context.
      if (failure != null) {
        log.log(Level.FINE, "Storage override doesn't exist. Using default", failure);
      }
    }

    private static Storage createStorage(
        AtomicReference<? super ClassNotFoundException> deferredStorageFailure) {
      try {
        Class<?> clazz = Class.forName("io.grpc.override.ContextStorageOverride");
        // The override's constructor is prohibited from triggering any code that can loop back to
        // Context
        return clazz.asSubclass(Storage.class).getConstructor().newInstance();
      } catch (ClassNotFoundException e) {
        deferredStorageFailure.set(e);
        return new ThreadLocalContextStorage();
      } catch (Exception e) {
        throw new RuntimeException("Storage override failed to initialize", e);
      }
    }
  }

  /**
   * Create a {@link Key} with the given debug name.
   *
   * @param debugString a name intended for debugging purposes and does not impact behavior.
   *                    Multiple different keys may have the same debugString.
   *                    The value should be not null.
   */
  public static <T> Key<T> key(String debugString) {
    return new Key<>(debugString);
  }

  /**
   * Create a {@link Key} with the given debug name and default value.
   *
   * @param debugString a name intended for debugging purposes and does not impact behavior.
   *                    Multiple different keys may have the same debugString.
   *                    The value should be not null.
   */
  public static <T> Key<T> keyWithDefault(String debugString, T defaultValue) {
    return new Key<>(debugString, defaultValue);
  }

  /**
   * Return the context associated with the current scope, will never return {@code null}.
   *
   * <p>Will never return {@link CancellableContext} even if one is attached, instead a
   * {@link Context} is returned with the same properties and lifetime. This is to avoid
   * code stealing the ability to cancel arbitrarily.
   */
  public static Context current() {
    Context current = storage().current();
    if (current == null) {
      return ROOT;
    }
    return current;
  }

  final CancellableContext cancellableAncestor;
  final Node<Key<?>, Object> keyValueEntries;
  // The number parents between this context and the root context.
  final int generation;

  /**
   * Construct a context that cannot be cancelled and will not cascade cancellation from its parent.
   */
  private Context(Node<Key<?>, Object> keyValueEntries, int generation) {
    this.cancellableAncestor = null;
    this.keyValueEntries = keyValueEntries;
    this.generation = generation;
    validateGeneration(generation);
  }

  /**
   * Construct a context that cannot be cancelled but will cascade cancellation from its parent if
   * it is cancellable.
   */
  private Context(Context parent, Node<Key<?>, Object> keyValueEntries) {
    this.cancellableAncestor = cancellableAncestor(parent);
    this.keyValueEntries = keyValueEntries;
    this.generation = parent.generation + 1;
    validateGeneration(generation);
  }

  /**
   * Construct for {@link #ROOT}.
   */
  private Context() {
    this.cancellableAncestor = null;
    this.keyValueEntries = null;
    this.generation = 0;
    validateGeneration(generation);
  }

  /**
   * Create a new context which is independently cancellable and also cascades cancellation from
   * its parent. Callers <em>must</em> ensure that either {@link
   * CancellableContext#cancel(Throwable)} or {@link CancellableContext#detachAndCancel(Context,
   * Throwable)} are called at a later point, in order to allow this context to be garbage
   * collected.
   *
   * <p>Sample usage:
   * <pre>
   *   Context.CancellableContext withCancellation = Context.current().withCancellation();
   *   try {
   *     withCancellation.run(new Runnable() {
   *       public void run() {
   *         Context current = Context.current();
   *         while (!current.isCancelled()) {
   *           keepWorking();
   *         }
   *       }
   *     });
   *   } finally {
   *     withCancellation.cancel(null);
   *   }
   * </pre>
   */
  public CancellableContext withCancellation() {
    return new CancellableContext(this);
  }

  /**
   * Create a new context which will cancel itself after the given {@code duration} from now.
   * The returned context will cascade cancellation of its parent. Callers may explicitly cancel
   * the returned context prior to the deadline just as for {@link #withCancellation()}. If the unit
   * of work completes before the deadline, the context should be explicitly cancelled to allow
   * it to be garbage collected.
   *
   * <p>Sample usage:
   * <pre>
   *   Context.CancellableContext withDeadline = Context.current()
   *       .withDeadlineAfter(5, TimeUnit.SECONDS, scheduler);
   *   try {
   *     withDeadline.run(new Runnable() {
   *       public void run() {
   *         Context current = Context.current();
   *         while (!current.isCancelled()) {
   *           keepWorking();
   *         }
   *       }
   *     });
   *   } finally {
   *     withDeadline.cancel(null);
   *   }
   * </pre>
   */
  public CancellableContext withDeadlineAfter(long duration, TimeUnit unit,
                                              ScheduledExecutorService scheduler) {
    return withDeadline(Deadline.after(duration, unit), scheduler);
  }

  /**
   * Create a new context which will cancel itself at the given {@link Deadline}.
   * The returned context will cascade cancellation of its parent. Callers may explicitly cancel
   * the returned context prior to the deadline just as for {@link #withCancellation()}. If the unit
   * of work completes before the deadline, the context should be explicitly cancelled to allow
   * it to be garbage collected.
   *
   * <p>Sample usage:
   * <pre>
   *   Context.CancellableContext withDeadline = Context.current()
   *      .withDeadline(someReceivedDeadline, scheduler);
   *   try {
   *     withDeadline.run(new Runnable() {
   *       public void run() {
   *         Context current = Context.current();
   *         while (!current.isCancelled() &amp;&amp; moreWorkToDo()) {
   *           keepWorking();
   *         }
   *       }
   *     });
   *   } finally {
   *     withDeadline.cancel(null);
   *   }
   * </pre>
   */
  public CancellableContext withDeadline(Deadline newDeadline, ScheduledExecutorService scheduler) {
    checkNotNull(newDeadline, "deadline");
    checkNotNull(scheduler, "scheduler");
    Deadline existingDeadline = getDeadline();
    boolean scheduleDeadlineCancellation = true;
    if (existingDeadline != null && existingDeadline.compareTo(newDeadline) <= 0) {
      // The new deadline won't have an effect, so ignore it
      newDeadline = existingDeadline;
      scheduleDeadlineCancellation = false;
    }
    CancellableContext newCtx = new CancellableContext(this, newDeadline);
    if (scheduleDeadlineCancellation) {
      newCtx.setUpDeadlineCancellation(newDeadline, scheduler);
    }
    return newCtx;
  }

  /**
   * Create a new context with the given key value set. The new context will cascade cancellation
   * from its parent.
   *
   <pre>
   *   Context withCredential = Context.current().withValue(CRED_KEY, cred);
   *   withCredential.run(new Runnable() {
   *     public void run() {
   *        readUserRecords(userId, CRED_KEY.get());
   *     }
   *   });
   * </pre>
   *
   * <p>Note that multiple calls to {@link #withValue} can be chained together.
   * That is,
   *
   * <pre>
   * context.withValues(K1, V1, K2, V2);
   * // is the same as
   * context.withValue(K1, V1).withValue(K2, V2);
   * </pre>
   *
   * <p>Nonetheless, {@link Context} should not be treated like a general purpose
   * map with a large number of keys and values — combine multiple related items
   * together into a single key instead of separating them. But if the items
   * are unrelated, have separate keys for them.
   */
  public <V> Context withValue(Key<V> k1, V v1) {
    Node<Key<?>, Object> newKeyValueEntries =
        PersistentHashArrayMappedTrie.put(keyValueEntries, k1, v1);
    return new Context(this, newKeyValueEntries);
  }

  /**
   * Create a new context with the given key value set. The new context will cascade cancellation
   * from its parent.
   */
  public <V1, V2> Context withValues(Key<V1> k1, V1 v1, Key<V2> k2, V2 v2) {
    Node<Key<?>, Object> newKeyValueEntries =
        PersistentHashArrayMappedTrie.put(keyValueEntries, k1, v1);
    newKeyValueEntries = PersistentHashArrayMappedTrie.put(newKeyValueEntries, k2, v2);
    return new Context(this, newKeyValueEntries);
  }

  /**
   * Create a new context with the given key value set. The new context will cascade cancellation
   * from its parent.
   */
  public <V1, V2, V3> Context withValues(Key<V1> k1, V1 v1, Key<V2> k2, V2 v2, Key<V3> k3, V3 v3) {
    Node<Key<?>, Object> newKeyValueEntries =
        PersistentHashArrayMappedTrie.put(keyValueEntries, k1, v1);
    newKeyValueEntries = PersistentHashArrayMappedTrie.put(newKeyValueEntries, k2, v2);
    newKeyValueEntries = PersistentHashArrayMappedTrie.put(newKeyValueEntries, k3, v3);
    return new Context(this, newKeyValueEntries);
  }

  /**
   * Create a new context with the given key value set. The new context will cascade cancellation
   * from its parent.
   *
   * <p>For more than 4 key-value pairs, note that multiple calls to
   * {@link #withValue} can be chained together. That is,
   *
   * <pre>
   * context.withValues(K1, V1, K2, V2);
   * // is the same as
   * context.withValue(K1, V1).withValue(K2, V2);
   * </pre>
   *
   * <p>Nonetheless, {@link Context} should not be treated like a general purpose
   * map with a large number of keys and values — combine multiple related items
   * together into a single key instead of separating them. But if the items
   * are unrelated, have separate keys for them.
   */
  public <V1, V2, V3, V4> Context withValues(Key<V1> k1, V1 v1, Key<V2> k2, V2 v2,
      Key<V3> k3, V3 v3, Key<V4> k4, V4 v4) {
    Node<Key<?>, Object> newKeyValueEntries =
        PersistentHashArrayMappedTrie.put(keyValueEntries, k1, v1);
    newKeyValueEntries = PersistentHashArrayMappedTrie.put(newKeyValueEntries, k2, v2);
    newKeyValueEntries = PersistentHashArrayMappedTrie.put(newKeyValueEntries, k3, v3);
    newKeyValueEntries = PersistentHashArrayMappedTrie.put(newKeyValueEntries, k4, v4);
    return new Context(this, newKeyValueEntries);
  }

  /**
   * Create a new context which propagates the values of this context but does not cascade its
   * cancellation.
   */
  public Context fork() {
    return new Context(keyValueEntries, generation + 1);
  }

  /**
   * Attach this context, thus enter a new scope within which this context is {@link #current}.  The
   * previously current context is returned. It is allowed to attach contexts where {@link
   * #isCancelled()} is {@code true}.
   *
   * <p>Instead of using {@code attach()} and {@link #detach(Context)} most use-cases are better
   * served by using the {@link #run(Runnable)} or {@link #call(java.util.concurrent.Callable)} to
   * execute work immediately within a context's scope. If work needs to be done in other threads it
   * is recommended to use the 'wrap' methods or to use a propagating executor.
   *
   * <p>All calls to {@code attach()} should have a corresponding {@link #detach(Context)} within
   * the same method:
   * <pre>{@code Context previous = someContext.attach();
   * try {
   *   // Do work
   * } finally {
   *   someContext.detach(previous);
   * }}</pre>
   */
  public Context attach() {
    Context prev = storage().doAttach(this);
    if (prev == null) {
      return ROOT;
    }
    return prev;
  }

  /**
   * Reverse an {@code attach()}, restoring the previous context and exiting the current scope.
   *
   * <p>This context should be the same context that was previously {@link #attach attached}.  The
   * provided replacement should be what was returned by the same {@link #attach attach()} call.  If
   * an {@code attach()} and a {@code detach()} meet above requirements, they match.
   *
   * <p>It is expected that between any pair of matching {@code attach()} and {@code detach()}, all
   * {@code attach()}es and {@code detach()}es are called in matching pairs.  If this method finds
   * that this context is not {@link #current current}, either you or some code in-between are not
   * detaching correctly, and a SEVERE message will be logged but the context to attach will still
   * be bound.  <strong>Never</strong> use {@code Context.current().detach()}, as this will
   * compromise this error-detecting mechanism.
   */
  public void detach(Context toAttach) {
    checkNotNull(toAttach, "toAttach");
    storage().detach(this, toAttach);
  }

  // Visible for testing
  boolean isCurrent() {
    return current() == this;
  }

  /**
   * Is this context cancelled.
   */
  public boolean isCancelled() {
    if (cancellableAncestor == null) {
      return false;
    } else {
      return cancellableAncestor.isCancelled();
    }
  }

  /**
   * If a context {@link #isCancelled()} then return the cause of the cancellation or
   * {@code null} if context was cancelled without a cause. If the context is not yet cancelled
   * will always return {@code null}.
   *
   * <p>The cancellation cause is provided for informational purposes only and implementations
   * should generally assume that it has already been handled and logged properly.
   */
  public Throwable cancellationCause() {
    if (cancellableAncestor == null) {
      return null;
    } else {
      return cancellableAncestor.cancellationCause();
    }
  }

  /**
   * A context may have an associated {@link Deadline} at which it will be automatically cancelled.
   * @return A {@link io.grpc.Deadline} or {@code null} if no deadline is set.
   */
  public Deadline getDeadline() {
    if (cancellableAncestor == null) {
      return null;
    }
    return cancellableAncestor.getDeadline();
  }

  /**
   * Add a listener that will be notified when the context becomes cancelled.
   */
  public void addListener(final CancellationListener cancellationListener,
                          final Executor executor) {
    checkNotNull(cancellationListener, "cancellationListener");
    checkNotNull(executor, "executor");
    if (cancellableAncestor == null) {
      return;
    }
    cancellableAncestor.addListenerInternal(
        new ExecutableListener(executor, cancellationListener, this));
  }

  /**
   * Remove a {@link CancellationListener}.
   */
  public void removeListener(CancellationListener cancellationListener) {
    if (cancellableAncestor == null) {
      return;
    }
    cancellableAncestor.removeListenerInternal(cancellationListener, this);
  }

  // Used in tests to ensure that listeners are defined and released when cancellation cascades.
  // It's very important to ensure that we do not accidentally retain listeners.
  int listenerCount() {
    if (cancellableAncestor == null) {
      return 0;
    }
    return cancellableAncestor.listenerCount();
  }

  /**
   * Immediately run a {@link Runnable} with this context as the {@link #current} context.
   * @param r {@link Runnable} to run.
   */
  public void run(Runnable r) {
    Context previous = attach();
    try {
      r.run();
    } finally {
      detach(previous);
    }
  }

  /**
   * Immediately call a {@link Callable} with this context as the {@link #current} context.
   * @param c {@link Callable} to call.
   * @return result of call.
   */
  @CanIgnoreReturnValue
  public <V> V call(Callable<V> c) throws Exception {
    Context previous = attach();
    try {
      return c.call();
    } finally {
      detach(previous);
    }
  }

  /**
   * Wrap a {@link Runnable} so that it executes with this context as the {@link #current} context.
   */
  public Runnable wrap(final Runnable r) {
    return new Runnable() {
      @Override
      public void run() {
        Context previous = attach();
        try {
          r.run();
        } finally {
          detach(previous);
        }
      }
    };
  }

  /**
   * Wrap a {@link Callable} so that it executes with this context as the {@link #current} context.
   */
  public <C> Callable<C> wrap(final Callable<C> c) {
    return new Callable<C>() {
      @Override
      public C call() throws Exception {
        Context previous = attach();
        try {
          return c.call();
        } finally {
          detach(previous);
        }
      }
    };
  }

  /**
   * Wrap an {@link Executor} so that it always executes with this context as the {@link #current}
   * context. It is generally expected that {@link #currentContextExecutor(Executor)} would be
   * used more commonly than this method.
   *
   * <p>One scenario in which this executor may be useful is when a single thread is sharding work
   * to multiple threads.
   *
   * @see #currentContextExecutor(Executor)
   */
  public Executor fixedContextExecutor(final Executor e) {
    final class FixedContextExecutor implements Executor {
      @Override
      public void execute(Runnable r) {
        e.execute(wrap(r));
      }
    }

    return new FixedContextExecutor();
  }

  /**
   * Create an executor that propagates the {@link #current} context when {@link Executor#execute}
   * is called as the {@link #current} context of the {@code Runnable} scheduled. <em>Note that this
   * is a static method.</em>
   *
   * @see #fixedContextExecutor(Executor)
   */
  public static Executor currentContextExecutor(final Executor e) {
    final class CurrentContextExecutor implements Executor {
      @Override
      public void execute(Runnable r) {
        e.execute(Context.current().wrap(r));
      }
    }

    return new CurrentContextExecutor();
  }

  /**
   * A context which inherits cancellation from its parent but which can also be independently
   * cancelled and which will propagate cancellation to its descendants. To avoid leaking memory,
   * every CancellableContext must have a defined lifetime, after which it is guaranteed to be
   * cancelled.
   *
   * <p>This class must be cancelled by either calling {@link #close} or {@link #cancel}.
   * {@link #close} is equivalent to calling {@code cancel(null)}. It is safe to call the methods
   * more than once, but only the first call will have any effect. Because it's safe to call the
   * methods multiple times, users are encouraged to always call {@link #close} at the end of
   * the operation, and disregard whether {@link #cancel} was already called somewhere else.
   *
   * <p>Blocking code can use the try-with-resources idiom:
   * <pre>
   * try (CancellableContext c = Context.current()
   *     .withDeadlineAfter(100, TimeUnit.MILLISECONDS, executor)) {
   *   Context toRestore = c.attach();
   *   try {
   *     // do some blocking work
   *   } finally {
   *     c.detach(toRestore);
   *   }
   * }</pre>
   *
   * <p>Asynchronous code will have to manually track the end of the CancellableContext's lifetime,
   * and cancel the context at the appropriate time.
   */
  public static final class CancellableContext extends Context implements Closeable {

    private final Deadline deadline;
    private final Context uncancellableSurrogate;

    private ArrayList<ExecutableListener> listeners;
    // parentListener is initialized when listeners is initialized (only if there is a
    // cancellable ancestor), and uninitialized when listeners is uninitialized.
    private CancellationListener parentListener;
    private Throwable cancellationCause;
    private ScheduledFuture<?> pendingDeadline;
    private boolean cancelled;

    /**
     * Create a cancellable context that does not have a deadline.
     */
    private CancellableContext(Context parent) {
      super(parent, parent.keyValueEntries);
      deadline = parent.getDeadline();
      // Create a surrogate that inherits from this to attach so that you cannot retrieve a
      // cancellable context from Context.current()
      uncancellableSurrogate = new Context(this, keyValueEntries);
    }

    /**
     * Create a cancellable context that has a deadline.
     */
    private CancellableContext(Context parent, Deadline deadline) {
      super(parent, parent.keyValueEntries);
      this.deadline = deadline;
      this.uncancellableSurrogate = new Context(this, keyValueEntries);
    }

    private void setUpDeadlineCancellation(Deadline deadline, ScheduledExecutorService scheduler) {
      if (!deadline.isExpired()) {
        final class CancelOnExpiration implements Runnable {
          @Override
          public void run() {
            try {
              cancel(new TimeoutException("context timed out"));
            } catch (Throwable t) {
              log.log(Level.SEVERE, "Cancel threw an exception, which should not happen", t);
            }
          }
        }

        synchronized (this) {
          pendingDeadline = deadline.runOnExpiration(new CancelOnExpiration(), scheduler);
        }
      } else {
        // Cancel immediately if the deadline is already expired.
        cancel(new TimeoutException("context timed out"));
      }
    }

    @Override
    public Context attach() {
      return uncancellableSurrogate.attach();
    }

    @Override
    public void detach(Context toAttach) {
      uncancellableSurrogate.detach(toAttach);
    }

    @Override
    public void addListener(
        final CancellationListener cancellationListener, final Executor executor) {
      checkNotNull(cancellationListener, "cancellationListener");
      checkNotNull(executor, "executor");
      addListenerInternal(new ExecutableListener(executor, cancellationListener, this));
    }

    private void addListenerInternal(ExecutableListener executableListener) {
      synchronized (this) {
        if (isCancelled()) {
          executableListener.deliver();
        } else {
          if (listeners == null) {
            // Now that we have a listener we need to listen to our parent so
            // we can cascade listener notification.
            listeners = new ArrayList<>();
            listeners.add(executableListener);
            if (cancellableAncestor != null) {
              parentListener =
                  new CancellationListener() {
                    @Override
                    public void cancelled(Context context) {
                      CancellableContext.this.cancel(context.cancellationCause());
                    }
                  };
              cancellableAncestor.addListenerInternal(
                  new ExecutableListener(DirectExecutor.INSTANCE, parentListener, this));
            }
          } else {
            listeners.add(executableListener);
          }
        }
      }
    }

    @Override
    public void removeListener(CancellationListener cancellationListener) {
      removeListenerInternal(cancellationListener, this);
    }

    private void removeListenerInternal(CancellationListener cancellationListener,
        Context context) {
      synchronized (this) {
        if (listeners != null) {
          for (int i = listeners.size() - 1; i >= 0; i--) {
            ExecutableListener executableListener = listeners.get(i);
            if (executableListener.listener == cancellationListener
                && executableListener.context == context) {
              listeners.remove(i);
              // Just remove the first matching listener, given that we allow duplicate
              // adds we should allow for duplicates after remove.
              break;
            }
          }
          // We have no listeners so no need to listen to our parent
          if (listeners.isEmpty()) {
            if (cancellableAncestor != null) {
              cancellableAncestor.removeListener(parentListener);
            }
            parentListener = null;
            listeners = null;
          }
        }
      }
    }

    /**
     * Returns true if the Context is the current context.
     *
     * @deprecated This method violates some GRPC class encapsulation and should not be used.
     *     If you must know whether a Context is the current context, check whether it is the same
     *     object returned by {@link Context#current()}.
     */
    //TODO(spencerfang): The superclass's method is package-private, so this should really match.
    @Override
    @Deprecated
    public boolean isCurrent() {
      return uncancellableSurrogate.isCurrent();
    }

    /**
     * Cancel this context and optionally provide a cause (can be {@code null}) for the
     * cancellation. This will trigger notification of listeners. It is safe to call this method
     * multiple times. Only the first call will have any effect.
     *
     * <p>Calling {@code cancel(null)} is the same as calling {@link #close}.
     *
     * @return {@code true} if this context cancelled the context and notified listeners,
     *         {@code false} if the context was already cancelled.
     */
    @CanIgnoreReturnValue
    public boolean cancel(Throwable cause) {
      boolean triggeredCancel = false;
      ScheduledFuture<?> localPendingDeadline = null;
      synchronized (this) {
        if (!cancelled) {
          cancelled = true;
          if (pendingDeadline != null) {
            // If we have a scheduled cancellation pending attempt to cancel it.
            localPendingDeadline = pendingDeadline;
            pendingDeadline = null;
          }
          this.cancellationCause = cause;
          triggeredCancel = true;
        }
      }
      if (localPendingDeadline != null) {
        localPendingDeadline.cancel(false);
      }
      if (triggeredCancel) {
        notifyAndClearListeners();
      }
      return triggeredCancel;
    }

    /**
     * Notify all listeners that this context has been cancelled and immediately release
     * any reference to them so that they may be garbage collected.
     */
    private void notifyAndClearListeners() {
      ArrayList<ExecutableListener> tmpListeners;
      CancellationListener tmpParentListener;
      synchronized (this) {
        if (listeners == null) {
          return;
        }
        tmpParentListener = parentListener;
        parentListener = null;
        tmpListeners = listeners;
        listeners = null;
      }
      // Deliver events to this context listeners before we notify child contexts. We do this
      // to cancel higher level units of work before child units. This allows for a better error
      // handling paradigm where the higher level unit of work knows it is cancelled and so can
      // ignore errors that bubble up as a result of cancellation of lower level units.
      for (ExecutableListener tmpListener : tmpListeners) {
        if (tmpListener.context == this) {
          tmpListener.deliver();
        }
      }
      for (ExecutableListener tmpListener : tmpListeners) {
        if (!(tmpListener.context == this)) {
          tmpListener.deliver();
        }
      }
      if (cancellableAncestor != null) {
        cancellableAncestor.removeListener(tmpParentListener);
      }
    }

    @Override
    int listenerCount() {
      synchronized (this) {
        return listeners == null ? 0 : listeners.size();
      }
    }

    /**
     * Cancel this context and detach it as the current context.
     *
     * @param toAttach context to make current.
     * @param cause of cancellation, can be {@code null}.
     */
    public void detachAndCancel(Context toAttach, Throwable cause) {
      try {
        detach(toAttach);
      } finally {
        cancel(cause);
      }
    }

    @Override
    public boolean isCancelled() {
      synchronized (this) {
        if (cancelled) {
          return true;
        }
      }
      // Detect cancellation of parent in the case where we have no listeners and
      // record it.
      if (super.isCancelled()) {
        cancel(super.cancellationCause());
        return true;
      }
      return false;
    }

    @Override
    public Throwable cancellationCause() {
      if (isCancelled()) {
        return cancellationCause;
      }
      return null;
    }

    @Override
    public Deadline getDeadline() {
      return deadline;
    }

    /**
     * Cleans up this object by calling {@code cancel(null)}.
     */
    @Override
    public void close() {
      cancel(null);
    }
  }

  /**
   * A listener notified on context cancellation.
   */
  public interface CancellationListener {
    /**
     * Notifies that a context was cancelled.
     *
     * @param context the newly cancelled context.
     */
    void cancelled(Context context);
  }

  /**
   * Key for indexing values stored in a context. Keys use reference equality and Context does not
   * provide a mechanism to loop over Keys. This means there is no way to access a Key's value from
   * a Context without having access to the Key instance itself. This allows strong control over
   * what code can get/set a key in the Context. For example, you might manage access to Key similar
   * to a ThreadLocal using Java visibility (private/protected). Generally Keys are stored in static
   * fields.
   */
  public static final class Key<T> {
    private final String name;
    private final T defaultValue;

    Key(String name) {
      this(name, null);
    }

    Key(String name, T defaultValue) {
      this.name = checkNotNull(name, "name");
      this.defaultValue = defaultValue;
    }

    /**
     * Get the value from the {@link #current()} context for this key.
     */
    public T get() {
      return get(Context.current());
    }

    /**
     * Get the value from the specified context for this key.
     */
    @SuppressWarnings("unchecked")
    public T get(Context context) {
      T value = (T) PersistentHashArrayMappedTrie.get(context.keyValueEntries, this);
      return value == null ? defaultValue : value;
    }

    @Override
    public String toString() {
      return name;
    }
  }

  /**
   * Defines the mechanisms for attaching and detaching the "current" context. The constructor for
   * extending classes <em>must not</em> trigger any activity that can use Context, which includes
   * logging, otherwise it can trigger an infinite initialization loop. Extending classes must not
   * assume that only one instance will be created; Context guarantees it will only use one
   * instance, but it may create multiple and then throw away all but one.
   *
   * <p>The default implementation will put the current context in a {@link ThreadLocal}.  If an
   * alternative implementation named {@code io.grpc.override.ContextStorageOverride} exists in the
   * classpath, it will be used instead of the default implementation.
   *
   * <p>This API is <a href="https://github.com/grpc/grpc-java/issues/2462">experimental</a> and
   * subject to change.
   */
  public abstract static class Storage {
    /**
     * Implements {@link io.grpc.Context#attach}.
     *
     * <p>Caution: {@link Context#attach()} interprets a return value of {@code null} to mean
     * the same thing as {@link Context#ROOT}.
     *
     * <p>See also: {@link #current()}.

     * @param toAttach the context to be attached
     * @return A {@link Context} that should be passed back into {@link #detach(Context, Context)}
     *        as the {@code toRestore} parameter. {@code null} is a valid return value, but see
     *        caution note.
     */
    public abstract Context doAttach(Context toAttach);

    /**
     * Implements {@link io.grpc.Context#detach}.
     *
     * @param toDetach the context to be detached. Should be, or be equivalent to, the current
     *        context of the current scope
     * @param toRestore the context to be the current.  Should be, or be equivalent to, the context
     *        of the outer scope
     */
    public abstract void detach(Context toDetach, Context toRestore);

    /**
     * Implements {@link io.grpc.Context#current}.
     *
     * <p>Caution: {@link Context} interprets a return value of {@code null} to mean the same
     * thing as {@link Context#ROOT}.
     *
     * <p>See also {@link #doAttach(Context)}.
     *
     * @return The context of the current scope. {@code null} is a valid return value, but see
     *        caution note.
     */
    public abstract Context current();
  }

  /**
   * Stores listener and executor pair.
   */
  private static final class ExecutableListener implements Runnable {
    private final Executor executor;
    final CancellationListener listener;
    private final Context context;

    ExecutableListener(Executor executor, CancellationListener listener, Context context) {
      this.executor = executor;
      this.listener = listener;
      this.context = context;
    }

    void deliver() {
      try {
        executor.execute(this);
      } catch (Throwable t) {
        log.log(Level.INFO, "Exception notifying context listener", t);
      }
    }

    @Override
    public void run() {
      listener.cancelled(context);
    }
  }

  @CanIgnoreReturnValue
  static <T> T checkNotNull(T reference, Object errorMessage) {
    if (reference == null) {
      throw new NullPointerException(String.valueOf(errorMessage));
    }
    return reference;
  }

  private enum DirectExecutor implements Executor {
    INSTANCE;

    @Override
    public void execute(Runnable command) {
      command.run();
    }

    @Override
    public String toString() {
      return "Context.DirectExecutor";
    }
  }

  /**
   * Returns {@code parent} if it is a {@link CancellableContext}, otherwise returns the parent's
   * {@link #cancellableAncestor}.
   */
  static CancellableContext cancellableAncestor(Context parent) {
    if (parent instanceof CancellableContext) {
      return (CancellableContext) parent;
    }
    // The parent simply cascades cancellations.
    // Bypass the parent and reference the ancestor directly (may be null).
    return parent.cancellableAncestor;
  }

  /**
   * If the ancestry chain length is unreasonably long, then print an error to the log and record
   * the stack trace.
   */
  private static void validateGeneration(int generation) {
    if (generation == CONTEXT_DEPTH_WARN_THRESH) {
      log.log(
          Level.SEVERE,
          "Context ancestry chain length is abnormally long. "
              + "This suggests an error in application code. "
              + "Length exceeded: " + CONTEXT_DEPTH_WARN_THRESH,
          new Exception());
    }
  }

  // Not using the standard com.google.errorprone.annotations.CheckReturnValue because that will
  // introduce dependencies that some io.grpc.Context API consumers may not want.
  @interface CheckReturnValue {}

  @interface CanIgnoreReturnValue {}
}
