rt/emul/compact/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Mon, 09 Feb 2015 20:31:09 +0100
branchjdk7-b147
changeset 1776 b208ff5b7dbc
child 1779 9d757281c666
permissions -rw-r--r--
Adding field updaters into the emul library
     1 /*
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     3  *
     4  * This code is free software; you can redistribute it and/or modify it
     5  * under the terms of the GNU General Public License version 2 only, as
     6  * published by the Free Software Foundation.  Oracle designates this
     7  * particular file as subject to the "Classpath" exception as provided
     8  * by Oracle in the LICENSE file that accompanied this code.
     9  *
    10  * This code is distributed in the hope that it will be useful, but WITHOUT
    11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    13  * version 2 for more details (a copy is included in the LICENSE file that
    14  * accompanied this code).
    15  *
    16  * You should have received a copy of the GNU General Public License version
    17  * 2 along with this work; if not, write to the Free Software Foundation,
    18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    19  *
    20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    21  * or visit www.oracle.com if you need additional information or have any
    22  * questions.
    23  */
    24 
    25 /*
    26  * This file is available under and governed by the GNU General Public
    27  * License version 2 only, as published by the Free Software Foundation.
    28  * However, the following notice accompanied the original version of this
    29  * file:
    30  *
    31  * Written by Doug Lea with assistance from members of JCP JSR-166
    32  * Expert Group and released to the public domain, as explained at
    33  * http://creativecommons.org/publicdomain/zero/1.0/
    34  */
    35 
    36 package java.util.concurrent.atomic;
    37 import sun.misc.Unsafe;
    38 import java.lang.reflect.*;
    39 
    40 /**
    41  * A reflection-based utility that enables atomic updates to
    42  * designated {@code volatile} reference fields of designated
    43  * classes.  This class is designed for use in atomic data structures
    44  * in which several reference fields of the same node are
    45  * independently subject to atomic updates. For example, a tree node
    46  * might be declared as
    47  *
    48  *  <pre> {@code
    49  * class Node {
    50  *   private volatile Node left, right;
    51  *
    52  *   private static final AtomicReferenceFieldUpdater<Node, Node> leftUpdater =
    53  *     AtomicReferenceFieldUpdater.newUpdater(Node.class, Node.class, "left");
    54  *   private static AtomicReferenceFieldUpdater<Node, Node> rightUpdater =
    55  *     AtomicReferenceFieldUpdater.newUpdater(Node.class, Node.class, "right");
    56  *
    57  *   Node getLeft() { return left;  }
    58  *   boolean compareAndSetLeft(Node expect, Node update) {
    59  *     return leftUpdater.compareAndSet(this, expect, update);
    60  *   }
    61  *   // ... and so on
    62  * }}</pre>
    63  *
    64  * <p>Note that the guarantees of the {@code compareAndSet}
    65  * method in this class are weaker than in other atomic classes.
    66  * Because this class cannot ensure that all uses of the field
    67  * are appropriate for purposes of atomic access, it can
    68  * guarantee atomicity only with respect to other invocations of
    69  * {@code compareAndSet} and {@code set} on the same updater.
    70  *
    71  * @since 1.5
    72  * @author Doug Lea
    73  * @param <T> The type of the object holding the updatable field
    74  * @param <V> The type of the field
    75  */
    76 public abstract class AtomicReferenceFieldUpdater<T, V> {
    77 
    78     /**
    79      * Creates and returns an updater for objects with the given field.
    80      * The Class arguments are needed to check that reflective types and
    81      * generic types match.
    82      *
    83      * @param tclass the class of the objects holding the field.
    84      * @param vclass the class of the field
    85      * @param fieldName the name of the field to be updated.
    86      * @return the updater
    87      * @throws IllegalArgumentException if the field is not a volatile reference type.
    88      * @throws RuntimeException with a nested reflection-based
    89      * exception if the class does not hold field or is the wrong type.
    90      */
    91     public static <U, W> AtomicReferenceFieldUpdater<U,W> newUpdater(Class<U> tclass, Class<W> vclass, String fieldName) {
    92         return new AtomicReferenceFieldUpdaterImpl<U,W>(tclass,
    93                                                         vclass,
    94                                                         fieldName);
    95     }
    96 
    97     /**
    98      * Protected do-nothing constructor for use by subclasses.
    99      */
   100     protected AtomicReferenceFieldUpdater() {
   101     }
   102 
   103     /**
   104      * Atomically sets the field of the given object managed by this updater
   105      * to the given updated value if the current value {@code ==} the
   106      * expected value. This method is guaranteed to be atomic with respect to
   107      * other calls to {@code compareAndSet} and {@code set}, but not
   108      * necessarily with respect to other changes in the field.
   109      *
   110      * @param obj An object whose field to conditionally set
   111      * @param expect the expected value
   112      * @param update the new value
   113      * @return true if successful.
   114      */
   115     public abstract boolean compareAndSet(T obj, V expect, V update);
   116 
   117     /**
   118      * Atomically sets the field of the given object managed by this updater
   119      * to the given updated value if the current value {@code ==} the
   120      * expected value. This method is guaranteed to be atomic with respect to
   121      * other calls to {@code compareAndSet} and {@code set}, but not
   122      * necessarily with respect to other changes in the field.
   123      *
   124      * <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
   125      * and does not provide ordering guarantees, so is only rarely an
   126      * appropriate alternative to {@code compareAndSet}.
   127      *
   128      * @param obj An object whose field to conditionally set
   129      * @param expect the expected value
   130      * @param update the new value
   131      * @return true if successful.
   132      */
   133     public abstract boolean weakCompareAndSet(T obj, V expect, V update);
   134 
   135     /**
   136      * Sets the field of the given object managed by this updater to the
   137      * given updated value. This operation is guaranteed to act as a volatile
   138      * store with respect to subsequent invocations of {@code compareAndSet}.
   139      *
   140      * @param obj An object whose field to set
   141      * @param newValue the new value
   142      */
   143     public abstract void set(T obj, V newValue);
   144 
   145     /**
   146      * Eventually sets the field of the given object managed by this
   147      * updater to the given updated value.
   148      *
   149      * @param obj An object whose field to set
   150      * @param newValue the new value
   151      * @since 1.6
   152      */
   153     public abstract void lazySet(T obj, V newValue);
   154 
   155     /**
   156      * Gets the current value held in the field of the given object managed
   157      * by this updater.
   158      *
   159      * @param obj An object whose field to get
   160      * @return the current value
   161      */
   162     public abstract V get(T obj);
   163 
   164     /**
   165      * Atomically sets the field of the given object managed by this updater
   166      * to the given value and returns the old value.
   167      *
   168      * @param obj An object whose field to get and set
   169      * @param newValue the new value
   170      * @return the previous value
   171      */
   172     public V getAndSet(T obj, V newValue) {
   173         for (;;) {
   174             V current = get(obj);
   175             if (compareAndSet(obj, current, newValue))
   176                 return current;
   177         }
   178     }
   179 
   180     private static final class AtomicReferenceFieldUpdaterImpl<T,V>
   181         extends AtomicReferenceFieldUpdater<T,V> {
   182         private static final Unsafe unsafe = Unsafe.getUnsafe();
   183         private final long offset;
   184         private final Class<T> tclass;
   185         private final Class<V> vclass;
   186         private final Class cclass;
   187 
   188         /*
   189          * Internal type checks within all update methods contain
   190          * internal inlined optimizations checking for the common
   191          * cases where the class is final (in which case a simple
   192          * getClass comparison suffices) or is of type Object (in
   193          * which case no check is needed because all objects are
   194          * instances of Object). The Object case is handled simply by
   195          * setting vclass to null in constructor.  The targetCheck and
   196          * updateCheck methods are invoked when these faster
   197          * screenings fail.
   198          */
   199 
   200         AtomicReferenceFieldUpdaterImpl(Class<T> tclass,
   201                                         Class<V> vclass,
   202                                         String fieldName) {
   203             Field field = null;
   204             Class fieldClass = null;
   205             Class caller = null;
   206             int modifiers = 0;
   207             try {
   208                 field = tclass.getDeclaredField(fieldName);
   209                 caller = sun.reflect.Reflection.getCallerClass(3);
   210                 modifiers = field.getModifiers();
   211                 sun.reflect.misc.ReflectUtil.ensureMemberAccess(
   212                     caller, tclass, null, modifiers);
   213                 sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass);
   214                 fieldClass = field.getType();
   215             } catch (Exception ex) {
   216                 throw new RuntimeException(ex);
   217             }
   218 
   219             if (vclass != fieldClass)
   220                 throw new ClassCastException();
   221 
   222             if (!Modifier.isVolatile(modifiers))
   223                 throw new IllegalArgumentException("Must be volatile type");
   224 
   225             this.cclass = (Modifier.isProtected(modifiers) &&
   226                            caller != tclass) ? caller : null;
   227             this.tclass = tclass;
   228             if (vclass == Object.class)
   229                 this.vclass = null;
   230             else
   231                 this.vclass = vclass;
   232             offset = unsafe.objectFieldOffset(field);
   233         }
   234 
   235         void targetCheck(T obj) {
   236             if (!tclass.isInstance(obj))
   237                 throw new ClassCastException();
   238             if (cclass != null)
   239                 ensureProtectedAccess(obj);
   240         }
   241 
   242         void updateCheck(T obj, V update) {
   243             if (!tclass.isInstance(obj) ||
   244                 (update != null && vclass != null && !vclass.isInstance(update)))
   245                 throw new ClassCastException();
   246             if (cclass != null)
   247                 ensureProtectedAccess(obj);
   248         }
   249 
   250         public boolean compareAndSet(T obj, V expect, V update) {
   251             if (obj == null || obj.getClass() != tclass || cclass != null ||
   252                 (update != null && vclass != null &&
   253                  vclass != update.getClass()))
   254                 updateCheck(obj, update);
   255             return unsafe.compareAndSwapObject(obj, offset, expect, update);
   256         }
   257 
   258         public boolean weakCompareAndSet(T obj, V expect, V update) {
   259             // same implementation as strong form for now
   260             if (obj == null || obj.getClass() != tclass || cclass != null ||
   261                 (update != null && vclass != null &&
   262                  vclass != update.getClass()))
   263                 updateCheck(obj, update);
   264             return unsafe.compareAndSwapObject(obj, offset, expect, update);
   265         }
   266 
   267         public void set(T obj, V newValue) {
   268             if (obj == null || obj.getClass() != tclass || cclass != null ||
   269                 (newValue != null && vclass != null &&
   270                  vclass != newValue.getClass()))
   271                 updateCheck(obj, newValue);
   272             unsafe.putObjectVolatile(obj, offset, newValue);
   273         }
   274 
   275         public void lazySet(T obj, V newValue) {
   276             if (obj == null || obj.getClass() != tclass || cclass != null ||
   277                 (newValue != null && vclass != null &&
   278                  vclass != newValue.getClass()))
   279                 updateCheck(obj, newValue);
   280             unsafe.putOrderedObject(obj, offset, newValue);
   281         }
   282 
   283         public V get(T obj) {
   284             if (obj == null || obj.getClass() != tclass || cclass != null)
   285                 targetCheck(obj);
   286             return (V)unsafe.getObjectVolatile(obj, offset);
   287         }
   288 
   289         private void ensureProtectedAccess(T obj) {
   290             if (cclass.isInstance(obj)) {
   291                 return;
   292             }
   293             throw new RuntimeException(
   294                 new IllegalAccessException("Class " +
   295                     cclass.getName() +
   296                     " can not access a protected member of class " +
   297                     tclass.getName() +
   298                     " using an instance of " +
   299                     obj.getClass().getName()
   300                 )
   301             );
   302         }
   303     }
   304 }