8151571: InnocuousThread cannot be created during early startup
authormchung
Fri, 25 Mar 2016 12:30:13 -0700
changeset 36680 c9fe8f8e7e6f
parent 36679 0e6911cee995
child 36681 c27e554bbe5c
8151571: InnocuousThread cannot be created during early startup Reviewed-by: alanb, plevart, chegar
jdk/src/java.base/share/classes/jdk/internal/logger/BootstrapLogger.java
jdk/src/java.base/share/classes/jdk/internal/misc/InnocuousThread.java
jdk/src/java.base/share/classes/jdk/internal/ref/CleanerFactory.java
jdk/src/java.base/share/classes/jdk/internal/ref/CleanerImpl.java
jdk/src/java.base/share/classes/sun/net/www/http/KeepAliveCache.java
jdk/src/java.base/share/classes/sun/net/www/http/KeepAliveStream.java
jdk/src/java.base/share/classes/sun/nio/ch/ThreadPool.java
jdk/src/java.base/share/classes/sun/nio/fs/NativeBuffer.java
--- 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