8151571: InnocuousThread cannot be created during early startup
Reviewed-by: alanb, plevart, chegar
--- a/jdk/src/java.base/share/classes/jdk/internal/logger/BootstrapLogger.java Fri Mar 25 17:12:18 2016 +0100
+++ b/jdk/src/java.base/share/classes/jdk/internal/logger/BootstrapLogger.java Fri Mar 25 12:30:13 2016 -0700
@@ -135,7 +135,7 @@
Thread thread = AccessController.doPrivileged(new PrivilegedAction<Thread>() {
@Override
public Thread run() {
- Thread t = new InnocuousThread(new BootstrapMessageLoggerTask(owner, r));
+ Thread t = InnocuousThread.newThread(new BootstrapMessageLoggerTask(owner, r));
t.setName("BootstrapMessageLoggerTask-"+t.getName());
return t;
}
--- a/jdk/src/java.base/share/classes/jdk/internal/misc/InnocuousThread.java Fri Mar 25 17:12:18 2016 +0100
+++ b/jdk/src/java.base/share/classes/jdk/internal/misc/InnocuousThread.java Fri Mar 25 12:30:13 2016 -0700
@@ -45,20 +45,49 @@
private static final long CONTEXTCLASSLOADER;
private static final AtomicInteger threadNumber = new AtomicInteger(1);
+ private static String newName() {
+ return "InnocuousThread-" + threadNumber.getAndIncrement();
+ }
- public InnocuousThread(Runnable target) {
- this(INNOCUOUSTHREADGROUP, target,
- "InnocuousThread-" + threadNumber.getAndIncrement());
+ /**
+ * Returns a new InnocuousThread with an auto-generated thread name
+ * and its context class loader is set to the system class loader.
+ */
+ public static Thread newThread(Runnable target) {
+ return newThread(newName(), target);
}
- public InnocuousThread(Runnable target, String name) {
- this(INNOCUOUSTHREADGROUP, target, name);
+ /**
+ * Returns a new InnocuousThread with its context class loader
+ * set to the system class loader.
+ */
+ public static Thread newThread(String name, Runnable target) {
+ return new InnocuousThread(INNOCUOUSTHREADGROUP,
+ target,
+ name,
+ ClassLoader.getSystemClassLoader());
}
- public InnocuousThread(ThreadGroup group, Runnable target, String name) {
+ /**
+ * Returns a new InnocuousThread with an auto-generated thread name.
+ * Its context class loader is set to null.
+ */
+ public static Thread newSystemThread(Runnable target) {
+ return newSystemThread(newName(), target);
+ }
+
+ /**
+ * Returns a new InnocuousThread with null context class loader.
+ */
+ public static Thread newSystemThread(String name, Runnable target) {
+ return new InnocuousThread(INNOCUOUSTHREADGROUP,
+ target, name, null);
+ }
+
+ private InnocuousThread(ThreadGroup group, Runnable target, String name, ClassLoader tccl) {
super(group, target, name, 0L, false);
UNSAFE.putOrderedObject(this, INHERITEDACCESSCONTROLCONTEXT, ACC);
- UNSAFE.putOrderedObject(this, CONTEXTCLASSLOADER, ClassLoader.getSystemClassLoader());
+ UNSAFE.putOrderedObject(this, CONTEXTCLASSLOADER, tccl);
}
@Override
--- a/jdk/src/java.base/share/classes/jdk/internal/ref/CleanerFactory.java Fri Mar 25 17:12:18 2016 +0100
+++ b/jdk/src/java.base/share/classes/jdk/internal/ref/CleanerFactory.java Fri Mar 25 12:30:13 2016 -0700
@@ -25,24 +25,44 @@
package jdk.internal.ref;
+import jdk.internal.misc.InnocuousThread;
+
import java.lang.ref.Cleaner;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.concurrent.ThreadFactory;
/**
- * CleanerFactory provides a Cleaner for use within OpenJDK modules.
+ * CleanerFactory provides a Cleaner for use within system modules.
* The cleaner is created on the first reference to the CleanerFactory.
*/
public final class CleanerFactory {
/* The common Cleaner. */
- private final static Cleaner commonCleaner = Cleaner.create();
+ private final static Cleaner commonCleaner = Cleaner.create(new ThreadFactory() {
+ @Override
+ public Thread newThread(Runnable r) {
+ return AccessController.doPrivileged(new PrivilegedAction<>() {
+ @Override
+ public Thread run() {
+ Thread t = InnocuousThread.newSystemThread("Common-Cleaner", r);
+ t.setPriority(Thread.MAX_PRIORITY - 2);
+ return t;
+ }
+ });
+ }
+ });
/**
- * Cleaner for use within OpenJDK modules.
+ * Cleaner for use within system modules.
*
- * @return a Cleaner for use within OpenJDK modules
+ * This Cleaner will run on a thread whose context class loader
+ * is {@code null}. The system cleaning action to perform in
+ * this Cleaner should handle a {@code null} context class loader.
+ *
+ * @return a Cleaner for use within system modules
*/
public static Cleaner cleaner() {
return commonCleaner;
}
-
}
--- a/jdk/src/java.base/share/classes/jdk/internal/ref/CleanerImpl.java Fri Mar 25 17:12:18 2016 +0100
+++ b/jdk/src/java.base/share/classes/jdk/internal/ref/CleanerImpl.java Fri Mar 25 12:30:13 2016 -0700
@@ -324,10 +324,10 @@
final AtomicInteger cleanerThreadNumber = new AtomicInteger();
public Thread newThread(Runnable r) {
- return AccessController.doPrivileged(new PrivilegedAction<Thread>() {
+ return AccessController.doPrivileged(new PrivilegedAction<>() {
@Override
public Thread run() {
- Thread t = new InnocuousThread(r);
+ Thread t = InnocuousThread.newThread(r);
t.setPriority(Thread.MAX_PRIORITY - 2);
t.setName("Cleaner-" + cleanerThreadNumber.getAndIncrement());
return t;
--- a/jdk/src/java.base/share/classes/sun/net/www/http/KeepAliveCache.java Fri Mar 25 17:12:18 2016 +0100
+++ b/jdk/src/java.base/share/classes/sun/net/www/http/KeepAliveCache.java Fri Mar 25 12:30:13 2016 -0700
@@ -96,12 +96,9 @@
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<>() {
public Void run() {
- keepAliveTimer = new InnocuousThread(cache, "Keep-Alive-Timer");
+ keepAliveTimer = InnocuousThread.newSystemThread("Keep-Alive-Timer", cache);
keepAliveTimer.setDaemon(true);
keepAliveTimer.setPriority(Thread.MAX_PRIORITY - 2);
- // Set the context class loader to null in order to avoid
- // keeping a strong reference to an application classloader.
- keepAliveTimer.setContextClassLoader(null);
keepAliveTimer.start();
return null;
}
--- a/jdk/src/java.base/share/classes/sun/net/www/http/KeepAliveStream.java Fri Mar 25 17:12:18 2016 +0100
+++ b/jdk/src/java.base/share/classes/sun/net/www/http/KeepAliveStream.java Fri Mar 25 12:30:13 2016 -0700
@@ -173,12 +173,9 @@
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
- cleanerThread = new InnocuousThread(queue, "Keep-Alive-SocketCleaner");
+ cleanerThread = InnocuousThread.newSystemThread("Keep-Alive-SocketCleaner", queue);
cleanerThread.setDaemon(true);
cleanerThread.setPriority(Thread.MAX_PRIORITY - 2);
- // Set the context class loader to null in order to avoid
- // keeping a strong reference to an application classloader.
- cleanerThread.setContextClassLoader(null);
cleanerThread.start();
return null;
}
--- a/jdk/src/java.base/share/classes/sun/nio/ch/ThreadPool.java Fri Mar 25 17:12:18 2016 +0100
+++ b/jdk/src/java.base/share/classes/sun/nio/ch/ThreadPool.java Fri Mar 25 12:30:13 2016 -0700
@@ -82,7 +82,7 @@
} else {
return (Runnable r) -> {
PrivilegedAction<Thread> action = () -> {
- Thread t = new InnocuousThread(r);
+ Thread t = InnocuousThread.newThread(r);
t.setDaemon(true);
return t;
};
--- a/jdk/src/java.base/share/classes/sun/nio/fs/NativeBuffer.java Fri Mar 25 17:12:18 2016 +0100
+++ b/jdk/src/java.base/share/classes/sun/nio/fs/NativeBuffer.java Fri Mar 25 12:30:13 2016 -0700
@@ -25,8 +25,9 @@
package sun.nio.fs;
+import java.lang.ref.Cleaner.Cleanable;
import jdk.internal.misc.Unsafe;
-import jdk.internal.ref.Cleaner;
+import jdk.internal.ref.CleanerFactory;
/**
* A light-weight buffer in native memory.
@@ -37,7 +38,7 @@
private final long address;
private final int size;
- private final Cleaner cleaner;
+ private final Cleanable cleanable;
// optional "owner" to avoid copying
// (only safe for use by thread-local caches)
@@ -56,7 +57,8 @@
NativeBuffer(int size) {
this.address = unsafe.allocateMemory(size);
this.size = size;
- this.cleaner = Cleaner.create(this, new Deallocator(address));
+ this.cleanable = CleanerFactory.cleaner()
+ .register(this, new Deallocator(address));
}
void release() {
@@ -72,7 +74,7 @@
}
void free() {
- cleaner.clean();
+ cleanable.clean();
}
// not synchronized; only safe for use by thread-local caches