Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8352433

Behavioral updates for ClassValue::remove

XMLWordPrintable

    • Icon: CSR CSR
    • Resolution: Unresolved
    • Icon: P4 P4
    • 25
    • core-libs
    • None
    • behavioral
    • low
    • The new happens-before relationship established is backward-compatible. The old `remove` in `computeValue` happens mostly in single-threaded contexts, too.
    • Java API
    • SE

      Summary

      Massive updates to the specification model of ClassValue to simplify and correct its behaviors around removals.

      Problem

      The behaviors of ClassValue is currently inscrutable from the method descriptions.

      In particular, the specification of remove is overwhelmingly complex, and it has a few behavioral problems: it either cannot prevent installation of a stale value (before JDK-8351045) or stalls in an infinite loop if it is called from computeValue (after JDK-8351045).

      We want to make the mental model of remove and its interactions with get or computeValue simpler.

      Solution

      Redefine the model of ClassValue completely.

      Now, all accesses on a ClassValue association (identified by a ClassValue-Class pair) have a total order; the accesses can be:

      1. Read access: Reads the current associated value
      2. Associate access: Try to associate the value from a computeValue
      3. Remove access: Removes the current associated value

      get call first performs a read access; if the associated value is absent, it enters a loop of calling computeValue and trying to install; the loop terminates when an associated value is present or computeValue failed with an exception, and retries if the a most recent remove access does not happen-before the finish of the computeValue.

      This happens-before relationship means that a retry can always reestablish this relationship if there is no new remove access when the next computeValue call returns, and a previous remove on the same thread does not require a retry. So a retry happens if the latest remove access happens on a different thread after the read/install access that initiated the computeValue.

      remove is simple: A remove access simply clears an association and prevents future associating of values from computeValue that cannot observe this remove.

      Specification

      The old specifications on these methods were almost completely replaced; hence not provided in this section.

      New class-level specification:

      Lazily associate a computed value with any {@code Class} object.
      For example, if a dynamic language needs to construct a message dispatch
      table for each class encountered at a message send call site,
      it can use a {@code ClassValue} to cache information needed to
      perform the message send quickly, for each class encountered.
      <p>
      The basic operation of a {@code ClassValue} is {@link #get get}, which
      returns the associated value, initially created by an invocation to {@link
      <h1>computeValue computeValue}; multiple invocations may happen under race, but</h1>
      
      exactly one value is associated to a {@code Class} and returned.
      <p>
      Another operation is {@link #remove remove}: it clears the associated value
      (if it exists), and ensures the next associated value is computed with input
      states up-to-date with the removal.
      <p>
      For a particular association, there is a total order for accesses to the
      associated value.  Accesses are atomic; they include:
      
      
      
      • A read-only access by {@code get}
      • An attempt to associate the return value of a {@code computeValue} by {@code get}
      • Clearing of an association by {@code remove}
      A {@code get} call always include at least one access; a {@code remove} call always has exactly one access; a {@code computeValue} call always happens between two accesses. This establishes the order of {@code computeValue} calls with respect to {@code remove} calls and determines whether the results of a {@code computeValue} can be successfully associated by a {@code get}. @param <T> the type of the associated value @author John Rose, JSR 292 EG @since 1.7

      New specification for get:

      {@return the value associated to the given {@code Class}}
      <p>
      This method first performs a read-only access, and returns the associated
      value if it exists.  Otherwise, this method tries to associate a value
      from a {@link #computeValue computeValue} invocation until the associated
      value exists, which may be from another thread.
      <p>
      This method may throw an exception from a {@code computeValue} invocation.
      In this case, no association happens.
      
      @param type the {@code Class} to retrieve the associated value for
      @throws NullPointerException if the argument is {@code null}
      @see #remove
      @see #computeValue

      New specification for computeValue:

      Computes the value to associate to the given {@code Class}.
      <p>
      This method is invoked when the initial read-only access by {@link #get
      get} finds no associated value.
      <p>
      If this method throws an exception, the initiating {@code get} call will
      not attempt to associate a value, and may terminate by returning the
      associated value if it exists, or by propagating that exception otherwise.
      <p>
      Otherwise, the value is computed and returned.  An attempt to associate
      the return value happens, with one of the following outcomes:
      
      
      
      • The associated value is present; it is returned and no association is done.
      • The most recent {@link #remove remove} call, if it exists, does not happen-before (JLS {@jls 17.4.5}) the finish of the {@code computeValue} that computed the value to associate. A new invocation to {@code computeValue}, which that {@code remove} call happens-before, happens to re-establish this happens-before relationship.
      • Otherwise, this value is successfully associated and returned.
      @apiNote A {@code computeValue} call may, due to class loading or other circumstances, recursively call {@code get} or {@code remove} for the same {@code type}. The recursive {@code get}, if the recursion stops, successfully finishes and this initiating {@code get} observes the associated value from recursion. The recursive {@code remove} is no-op, since being on the same thread, the {@code remove} already happens-before the finish of this {@code computeValue}; the result from this {@code computeValue} still may be associated. @param type the {@code Class} to associate a value to @return the newly computed value to associate @see #get @see #remove

      New specification for remove:

      Removes the associated value for the given {@code Class} and invalidates
      all out-of-date computations.  If this association is subsequently
      {@linkplain #get accessed}, this removal happens-before (JLS {@jls
      17.4.5}) the finish of the {@link #computeValue computeValue} call that
      returned the associated value.
      
      @param type the type whose class value must be removed
      @throws NullPointerException if the argument is {@code null}

            liach Chen Liang
            liach Chen Liang
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated: