8160710: Enable Thread to grant VarHandle field access to ThreadLocalRandom/Striped64
Reviewed-by: martin, dl, chegar
--- 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);
}