8160710: Enable Thread to grant VarHandle field access to ThreadLocalRandom/Striped64
authorpsandoz
Thu, 19 Jan 2017 09:27:24 -0800
changeset 43219 566b551b7820
parent 43218 8be65fea76fc
child 43220 937cb78b2016
8160710: Enable Thread to grant VarHandle field access to ThreadLocalRandom/Striped64 Reviewed-by: martin, dl, chegar
jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java
jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinWorkerThread.java
jdk/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java
jdk/src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java
jdk/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Thu Jan 19 12:06:58 2017 -0500
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Thu Jan 19 09:27:24 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -846,8 +846,11 @@
             // that does not bluntly restrict classes under packages within
             // java.base from looking up MethodHandles or VarHandles.
             if (allowedModes == ALL_MODES && lookupClass.getClassLoader() == null) {
-                if ((name.startsWith("java.") && !name.startsWith("java.util.concurrent.")) ||
-                        (name.startsWith("sun.") && !name.startsWith("sun.invoke."))) {
+                if ((name.startsWith("java.") &&
+                     !name.equals("java.lang.Thread") &&
+                     !name.startsWith("java.util.concurrent.")) ||
+                    (name.startsWith("sun.") &&
+                     !name.startsWith("sun.invoke."))) {
                     throw newIllegalArgumentException("illegal lookupClass: " + lookupClass);
                 }
             }
--- a/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinWorkerThread.java	Thu Jan 19 12:06:58 2017 -0500
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinWorkerThread.java	Thu Jan 19 09:27:24 2017 -0800
@@ -185,7 +185,14 @@
     static final class InnocuousForkJoinWorkerThread extends ForkJoinWorkerThread {
         /** The ThreadGroup for all InnocuousForkJoinWorkerThreads */
         private static final ThreadGroup innocuousThreadGroup =
-            ThreadLocalRandom.createThreadGroup("InnocuousForkJoinWorkerThreadGroup");
+                java.security.AccessController.doPrivileged(
+                    new java.security.PrivilegedAction<>() {
+                        public ThreadGroup run() {
+                            ThreadGroup group = Thread.currentThread().getThreadGroup();
+                            for (ThreadGroup p; (p = group.getParent()) != null; )
+                                group = p;
+                            return new ThreadGroup(group, "InnocuousForkJoinWorkerThreadGroup");
+                        }});
 
         /** An AccessControlContext supporting no privileges */
         private static final AccessControlContext INNOCUOUS_ACC =
@@ -215,6 +222,5 @@
         public void setContextClassLoader(ClassLoader cl) {
             throw new SecurityException("setContextClassLoader");
         }
-
     }
 }
--- a/jdk/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java	Thu Jan 19 12:06:58 2017 -0500
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java	Thu Jan 19 09:27:24 2017 -0800
@@ -36,6 +36,8 @@
 package java.util.concurrent;
 
 import java.io.ObjectStreamField;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
 import java.security.AccessControlContext;
 import java.util.Random;
 import java.util.Spliterator;
@@ -48,7 +50,6 @@
 import java.util.stream.IntStream;
 import java.util.stream.LongStream;
 import java.util.stream.StreamSupport;
-import jdk.internal.misc.Unsafe;
 
 /**
  * A random number generator isolated to the current thread.  Like the
@@ -108,7 +109,7 @@
      * though, we use only a single 64bit gamma.
      *
      * Because this class is in a different package than class Thread,
-     * field access methods use Unsafe to bypass access control rules.
+     * field access methods use VarHandles to bypass access control rules.
      * To conform to the requirements of the Random superclass
      * constructor, the common static ThreadLocalRandom maintains an
      * "initialized" field for the sake of rejecting user calls to
@@ -163,8 +164,8 @@
         int probe = (p == 0) ? 1 : p; // skip 0
         long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT));
         Thread t = Thread.currentThread();
-        U.putLong(t, SEED, seed);
-        U.putInt(t, PROBE, probe);
+        SEED.set(t, seed);
+        PROBE.set(t, probe);
     }
 
     /**
@@ -173,7 +174,7 @@
      * @return the current thread's {@code ThreadLocalRandom}
      */
     public static ThreadLocalRandom current() {
-        if (U.getInt(Thread.currentThread(), PROBE) == 0)
+        if ((int) PROBE.get(Thread.currentThread()) == 0)
             localInit();
         return instance;
     }
@@ -192,8 +193,8 @@
 
     final long nextSeed() {
         Thread t; long r; // read and update per-thread seed
-        U.putLong(t = Thread.currentThread(), SEED,
-                  r = U.getLong(t, SEED) + GAMMA);
+        SEED.set(t = Thread.currentThread(),
+                 (r = (long) SEED.get(t)) + GAMMA);
         return r;
     }
 
@@ -938,7 +939,7 @@
      * can be used to force initialization on zero return.
      */
     static final int getProbe() {
-        return U.getInt(Thread.currentThread(), PROBE);
+        return (int) PROBE.get(Thread.currentThread());
     }
 
     /**
@@ -949,7 +950,7 @@
         probe ^= probe << 13;   // xorshift
         probe ^= probe >>> 17;
         probe ^= probe << 5;
-        U.putInt(Thread.currentThread(), PROBE, probe);
+        PROBE.set(Thread.currentThread(), probe);
         return probe;
     }
 
@@ -959,14 +960,14 @@
     static final int nextSecondarySeed() {
         int r;
         Thread t = Thread.currentThread();
-        if ((r = U.getInt(t, SECONDARY)) != 0) {
+        if ((r = (int) SECONDARY.get(t)) != 0) {
             r ^= r << 13;   // xorshift
             r ^= r >>> 17;
             r ^= r << 5;
         }
         else if ((r = mix32(seeder.getAndAdd(SEEDER_INCREMENT))) == 0)
             r = 1; // avoid zero
-        U.putInt(t, SECONDARY, r);
+        SECONDARY.set(t, r);
         return r;
     }
 
@@ -976,41 +977,13 @@
      * Erases ThreadLocals by nulling out Thread maps.
      */
     static final void eraseThreadLocals(Thread thread) {
-        U.putObject(thread, THREADLOCALS, null);
-        U.putObject(thread, INHERITABLETHREADLOCALS, null);
+        THREADLOCALS.set(thread, null);
+        INHERITABLETHREADLOCALS.set(thread, null);
     }
 
     static final void setInheritedAccessControlContext(Thread thread,
                                                        AccessControlContext acc) {
-        U.putObjectRelease(thread, INHERITEDACCESSCONTROLCONTEXT, acc);
-    }
-
-    /**
-     * Returns a new group with the system ThreadGroup (the
-     * topmost, parent-less group) as parent.  Uses Unsafe to
-     * traverse Thread.group and ThreadGroup.parent fields.
-     */
-    static final ThreadGroup createThreadGroup(String name) {
-        if (name == null)
-            throw new NullPointerException();
-        try {
-            long tg = U.objectFieldOffset
-                (Thread.class.getDeclaredField("group"));
-            long gp = U.objectFieldOffset
-                (ThreadGroup.class.getDeclaredField("parent"));
-            ThreadGroup group = (ThreadGroup)
-                U.getObject(Thread.currentThread(), tg);
-            while (group != null) {
-                ThreadGroup parent = (ThreadGroup)U.getObject(group, gp);
-                if (parent == null)
-                    return new ThreadGroup(group, name);
-                group = parent;
-            }
-        } catch (ReflectiveOperationException e) {
-            throw new Error(e);
-        }
-        // fall through if null as cannot-happen safeguard
-        throw new Error("Cannot create ThreadGroup");
+        INHERITEDACCESSCONTROLCONTEXT.setRelease(thread, acc);
     }
 
     // Serialization support
@@ -1037,7 +1010,7 @@
         throws java.io.IOException {
 
         java.io.ObjectOutputStream.PutField fields = s.putFields();
-        fields.put("rnd", U.getLong(Thread.currentThread(), SEED));
+        fields.put("rnd", (long) SEED.get(Thread.currentThread()));
         fields.put("initialized", true);
         s.writeFields();
     }
@@ -1076,28 +1049,38 @@
     static final String BAD_RANGE = "bound must be greater than origin";
     static final String BAD_SIZE  = "size must be non-negative";
 
-    // Unsafe mechanics
-    private static final Unsafe U = Unsafe.getUnsafe();
-    private static final long SEED;
-    private static final long PROBE;
-    private static final long SECONDARY;
-    private static final long THREADLOCALS;
-    private static final long INHERITABLETHREADLOCALS;
-    private static final long INHERITEDACCESSCONTROLCONTEXT;
+    // VarHandle mechanics
+    private static final VarHandle SEED;
+    private static final VarHandle PROBE;
+    private static final VarHandle SECONDARY;
+    private static final VarHandle THREADLOCALS;
+    private static final VarHandle INHERITABLETHREADLOCALS;
+    private static final VarHandle INHERITEDACCESSCONTROLCONTEXT;
     static {
         try {
-            SEED = U.objectFieldOffset
-                (Thread.class.getDeclaredField("threadLocalRandomSeed"));
-            PROBE = U.objectFieldOffset
-                (Thread.class.getDeclaredField("threadLocalRandomProbe"));
-            SECONDARY = U.objectFieldOffset
-                (Thread.class.getDeclaredField("threadLocalRandomSecondarySeed"));
-            THREADLOCALS = U.objectFieldOffset
-                (Thread.class.getDeclaredField("threadLocals"));
-            INHERITABLETHREADLOCALS = U.objectFieldOffset
-                (Thread.class.getDeclaredField("inheritableThreadLocals"));
-            INHERITEDACCESSCONTROLCONTEXT = U.objectFieldOffset
-                (Thread.class.getDeclaredField("inheritedAccessControlContext"));
+            MethodHandles.Lookup l = java.security.AccessController.doPrivileged(
+                    new java.security.PrivilegedAction<>() {
+                        public MethodHandles.Lookup run() {
+                            try {
+                                return MethodHandles.privateLookupIn(Thread.class,
+                                        MethodHandles.lookup());
+                            } catch (ReflectiveOperationException e) {
+                                throw new Error(e);
+                            }
+                        }});
+            SEED = l.findVarHandle(Thread.class,
+                    "threadLocalRandomSeed", long.class);
+            PROBE = l.findVarHandle(Thread.class,
+                    "threadLocalRandomProbe", int.class);
+            SECONDARY = l.findVarHandle(Thread.class,
+                    "threadLocalRandomSecondarySeed", int.class);
+            Class<?> tlm = Class.forName("java.lang.ThreadLocal$ThreadLocalMap");
+            THREADLOCALS = l.findVarHandle(Thread.class,
+                    "threadLocals", tlm);
+            INHERITABLETHREADLOCALS = l.findVarHandle(Thread.class,
+                    "inheritableThreadLocals", tlm);
+            INHERITEDACCESSCONTROLCONTEXT = l.findVarHandle(Thread.class,
+                    "inheritedAccessControlContext", AccessControlContext.class);
         } catch (ReflectiveOperationException e) {
             throw new Error(e);
         }
@@ -1123,7 +1106,7 @@
     // at end of <clinit> to survive static initialization circularity
     static {
         if (java.security.AccessController.doPrivileged(
-            new java.security.PrivilegedAction<Boolean>() {
+            new java.security.PrivilegedAction<>() {
                 public Boolean run() {
                     return Boolean.getBoolean("java.util.secureRandomSeed");
                 }})) {
--- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java	Thu Jan 19 12:06:58 2017 -0500
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java	Thu Jan 19 09:27:24 2017 -0800
@@ -41,7 +41,6 @@
 import java.util.concurrent.ThreadLocalRandom;
 import java.util.function.DoubleBinaryOperator;
 import java.util.function.LongBinaryOperator;
-import jdk.internal.misc.Unsafe;
 
 /**
  * A package-local class holding common representation and mechanics
@@ -191,7 +190,7 @@
      * Duplicated from ThreadLocalRandom because of packaging restrictions.
      */
     static final int getProbe() {
-        return U.getInt(Thread.currentThread(), PROBE);
+        return (int) THREAD_PROBE.get(Thread.currentThread());
     }
 
     /**
@@ -203,7 +202,7 @@
         probe ^= probe << 13;   // xorshift
         probe ^= probe >>> 17;
         probe ^= probe << 5;
-        U.putInt(Thread.currentThread(), PROBE, probe);
+        THREAD_PROBE.set(Thread.currentThread(), probe);
         return probe;
     }
 
@@ -373,18 +372,28 @@
         }
     }
 
-    // Unsafe and VarHandle mechanics
-    private static final Unsafe U = Unsafe.getUnsafe();
+    // VarHandle mechanics
     private static final VarHandle BASE;
     private static final VarHandle CELLSBUSY;
-    private static final long PROBE;
+    private static final VarHandle THREAD_PROBE;
     static {
         try {
             MethodHandles.Lookup l = MethodHandles.lookup();
-            BASE = l.findVarHandle(Striped64.class, "base", long.class);
-            CELLSBUSY = l.findVarHandle(Striped64.class, "cellsBusy", int.class);
-            PROBE = U.objectFieldOffset
-                (Thread.class.getDeclaredField("threadLocalRandomProbe"));
+            BASE = l.findVarHandle(Striped64.class,
+                    "base", long.class);
+            CELLSBUSY = l.findVarHandle(Striped64.class,
+                    "cellsBusy", int.class);
+            l = java.security.AccessController.doPrivileged(
+                    new java.security.PrivilegedAction<>() {
+                        public MethodHandles.Lookup run() {
+                            try {
+                                return MethodHandles.privateLookupIn(Thread.class, MethodHandles.lookup());
+                            } catch (ReflectiveOperationException e) {
+                                throw new Error(e);
+                            }
+                        }});
+            THREAD_PROBE = l.findVarHandle(Thread.class,
+                    "threadLocalRandomProbe", int.class);
         } catch (ReflectiveOperationException e) {
             throw new Error(e);
         }
--- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java	Thu Jan 19 12:06:58 2017 -0500
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java	Thu Jan 19 09:27:24 2017 -0800
@@ -37,6 +37,9 @@
 
 import jdk.internal.misc.Unsafe;
 
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+
 /**
  * Basic thread blocking primitives for creating locks and other
  * synchronization classes.
@@ -139,7 +142,7 @@
 
     private static void setBlocker(Thread t, Object arg) {
         // Even though volatile, hotspot doesn't need a write barrier here.
-        U.putObject(t, PARKBLOCKER, arg);
+        THREAD_PARKBLOCKER.set(t, arg);
     }
 
     /**
@@ -289,7 +292,7 @@
     public static Object getBlocker(Thread t) {
         if (t == null)
             throw new NullPointerException();
-        return U.getObjectVolatile(t, PARKBLOCKER);
+        return THREAD_PARKBLOCKER.getVolatile(t);
     }
 
     /**
@@ -396,14 +399,14 @@
     static final int nextSecondarySeed() {
         int r;
         Thread t = Thread.currentThread();
-        if ((r = U.getInt(t, SECONDARY)) != 0) {
+        if ((r = (int) THREAD_SECONDARY.get(t)) != 0) {
             r ^= r << 13;   // xorshift
             r ^= r >>> 17;
             r ^= r << 5;
         }
         else if ((r = java.util.concurrent.ThreadLocalRandom.current().nextInt()) == 0)
             r = 1; // avoid zero
-        U.putInt(t, SECONDARY, r);
+        THREAD_SECONDARY.set(t, r);
         return r;
     }
 
@@ -414,23 +417,32 @@
      * ways that do not preserve unique mappings.
      */
     static final long getThreadId(Thread thread) {
-        return U.getLongVolatile(thread, TID);
+        return (long) THREAD_TID.getVolatile(thread);
     }
 
     // Hotspot implementation via intrinsics API
     private static final Unsafe U = Unsafe.getUnsafe();
-    private static final long PARKBLOCKER;
-    private static final long SECONDARY;
-    private static final long TID;
+    // VarHandle mechanics
+    private static final VarHandle THREAD_PARKBLOCKER;
+    private static final VarHandle THREAD_SECONDARY;
+    private static final VarHandle THREAD_TID;
     static {
         try {
-            PARKBLOCKER = U.objectFieldOffset
-                (Thread.class.getDeclaredField("parkBlocker"));
-            SECONDARY = U.objectFieldOffset
-                (Thread.class.getDeclaredField("threadLocalRandomSecondarySeed"));
-            TID = U.objectFieldOffset
-                (Thread.class.getDeclaredField("tid"));
-
+            MethodHandles.Lookup l = java.security.AccessController.doPrivileged(
+                    new java.security.PrivilegedAction<>() {
+                        public MethodHandles.Lookup run() {
+                            try {
+                                return MethodHandles.privateLookupIn(Thread.class, MethodHandles.lookup());
+                            } catch (ReflectiveOperationException e) {
+                                throw new Error(e);
+                            }
+                        }});
+            THREAD_PARKBLOCKER = l.findVarHandle(Thread.class,
+                    "parkBlocker", Object.class);
+            THREAD_SECONDARY = l.findVarHandle(Thread.class,
+                    "threadLocalRandomSecondarySeed", int.class);
+            THREAD_TID = l.findVarHandle(Thread.class,
+                    "tid", long.class);
         } catch (ReflectiveOperationException e) {
             throw new Error(e);
         }