From 355dac1a4c4f02584ea83fd4e679551c6faa6536 Mon Sep 17 00:00:00 2001 From: Chris Hegarty Date: Fri, 18 Dec 2015 16:06:24 +0000 Subject: [PATCH] 8056152: API to create Threads that do not inherit inheritable thread-local initial values Reviewed-by: alanb, dholmes, mchung, mr, rriggs --- .../java/lang/InheritableThreadLocal.java | 5 + .../share/classes/java/lang/Thread.java | 69 +++++++++++- .../classes/java/lang/ref/Finalizer.java | 7 +- .../classes/java/lang/ref/Reference.java | 5 +- .../java.base/share/classes/sun/misc/GC.java | 4 +- .../classes/sun/misc/InnocuousThread.java | 18 +++- .../share/classes/sun/misc/Signal.java | 2 +- .../share/classes/sun/net/NetworkServer.java | 5 +- .../classes/sun/net/www/MimeLauncher.java | 5 +- .../classes/sun/nio/fs/AbstractPoller.java | 3 +- .../share/classes/sun/nio/fs/Cancellable.java | 3 +- .../sun/nio/fs/PollingWatchService.java | 3 +- .../sun/security/provider/SeedGenerator.java | 11 +- .../sun/security/ssl/SSLSocketImpl.java | 8 +- .../sun/nio/ch/WindowsSelectorImpl.java | 4 +- jdk/test/java/lang/Thread/ITLConstructor.java | 100 ++++++++++++++++++ 16 files changed, 213 insertions(+), 39 deletions(-) create mode 100644 jdk/test/java/lang/Thread/ITLConstructor.java diff --git a/jdk/src/java.base/share/classes/java/lang/InheritableThreadLocal.java b/jdk/src/java.base/share/classes/java/lang/InheritableThreadLocal.java index 07ea6be5d1e..23f395c7f51 100644 --- a/jdk/src/java.base/share/classes/java/lang/InheritableThreadLocal.java +++ b/jdk/src/java.base/share/classes/java/lang/InheritableThreadLocal.java @@ -40,6 +40,11 @@ import java.lang.ref.*; * maintained in the variable (e.g., User ID, Transaction ID) must be * automatically transmitted to any child threads that are created. * + *

Note: During the creation of a new {@link + * Thread#Thread(ThreadGroup,Runnable,String,long,boolean) thread}, it is + * possible to opt out of receiving initial values for inheritable + * thread-local variables. + * * @author Josh Bloch and Doug Lea * @see ThreadLocal * @since 1.2 diff --git a/jdk/src/java.base/share/classes/java/lang/Thread.java b/jdk/src/java.base/share/classes/java/lang/Thread.java index 57f90384243..e40aa6f0d51 100644 --- a/jdk/src/java.base/share/classes/java/lang/Thread.java +++ b/jdk/src/java.base/share/classes/java/lang/Thread.java @@ -344,11 +344,11 @@ class Thread implements Runnable { /** * Initializes a Thread with the current AccessControlContext. - * @see #init(ThreadGroup,Runnable,String,long,AccessControlContext) + * @see #init(ThreadGroup,Runnable,String,long,AccessControlContext,boolean) */ private void init(ThreadGroup g, Runnable target, String name, long stackSize) { - init(g, target, name, stackSize, null); + init(g, target, name, stackSize, null, true); } /** @@ -361,9 +361,12 @@ class Thread implements Runnable { * zero to indicate that this parameter is to be ignored. * @param acc the AccessControlContext to inherit, or * AccessController.getContext() if null + * @param inheritThreadLocals if {@code true}, inherit initial values for + * inheritable thread-locals from the constructing thread */ private void init(ThreadGroup g, Runnable target, String name, - long stackSize, AccessControlContext acc) { + long stackSize, AccessControlContext acc, + boolean inheritThreadLocals) { if (name == null) { throw new NullPointerException("name cannot be null"); } @@ -414,7 +417,7 @@ class Thread implements Runnable { acc != null ? acc : AccessController.getContext(); this.target = target; setPriority(priority); - if (parent.inheritableThreadLocals != null) + if (inheritThreadLocals && parent.inheritableThreadLocals != null) this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); /* Stash the specified stack size in case the VM cares */ @@ -468,7 +471,7 @@ class Thread implements Runnable { * This is not a public constructor. */ Thread(Runnable target, AccessControlContext acc) { - init(null, target, "Thread-" + nextThreadNum(), 0, acc); + init(null, target, "Thread-" + nextThreadNum(), 0, acc, true); } /** @@ -677,6 +680,62 @@ class Thread implements Runnable { init(group, target, name, stackSize); } + /** + * Allocates a new {@code Thread} object so that it has {@code target} + * as its run object, has the specified {@code name} as its name, + * belongs to the thread group referred to by {@code group}, has + * the specified {@code stackSize}, and inherits initial values for + * {@linkplain InheritableThreadLocal inheritable thread-local} variables + * if {@code inheritThreadLocals} is {@code true}. + * + *

This constructor is identical to {@link + * #Thread(ThreadGroup,Runnable,String,long)} with the added ability to + * suppress, or not, the inheriting of initial values for inheritable + * thread-local variables from the constructing thread. This allows for + * finer grain control over inheritable thread-locals. Care must be taken + * when passing a value of {@code false} for {@code inheritThreadLocals}, + * as it may lead to unexpected behavior if the new thread executes code + * that expects a specific thread-local value to be inherited. + * + *

Specifying a value of {@code true} for the {@code inheritThreadLocals} + * parameter will cause this constructor to behave exactly like the + * {@code Thread(ThreadGroup, Runnable, String, long)} constructor. + * + * @param group + * the thread group. If {@code null} and there is a security + * manager, the group is determined by {@linkplain + * SecurityManager#getThreadGroup SecurityManager.getThreadGroup()}. + * If there is not a security manager or {@code + * SecurityManager.getThreadGroup()} returns {@code null}, the group + * is set to the current thread's thread group. + * + * @param target + * the object whose {@code run} method is invoked when this thread + * is started. If {@code null}, this thread's run method is invoked. + * + * @param name + * the name of the new thread + * + * @param stackSize + * the desired stack size for the new thread, or zero to indicate + * that this parameter is to be ignored + * + * @param inheritThreadLocals + * if {@code true}, inherit initial values for inheritable + * thread-locals from the constructing thread, otherwise no initial + * values are inherited + * + * @throws SecurityException + * if the current thread cannot create a thread in the specified + * thread group + * + * @since 9 + */ + public Thread(ThreadGroup group, Runnable target, String name, + long stackSize, boolean inheritThreadLocals) { + init(group, target, name, stackSize, null, inheritThreadLocals); + } + /** * Causes this thread to begin execution; the Java Virtual Machine * calls the run method of this thread. diff --git a/jdk/src/java.base/share/classes/java/lang/ref/Finalizer.java b/jdk/src/java.base/share/classes/java/lang/ref/Finalizer.java index 4c6653f815a..221d07e207b 100644 --- a/jdk/src/java.base/share/classes/java/lang/ref/Finalizer.java +++ b/jdk/src/java.base/share/classes/java/lang/ref/Finalizer.java @@ -29,7 +29,6 @@ import java.security.PrivilegedAction; import java.security.AccessController; import jdk.internal.misc.JavaLangAccess; import jdk.internal.misc.SharedSecrets; -import sun.misc.ManagedLocalsThread; import sun.misc.VM; final class Finalizer extends FinalReference { /* Package-private; must be in @@ -131,7 +130,7 @@ final class Finalizer extends FinalReference { /* Package-private; must for (ThreadGroup tgn = tg; tgn != null; tg = tgn, tgn = tg.getParent()); - Thread sft = new ManagedLocalsThread(tg, proc, "Secondary finalizer"); + Thread sft = new Thread(tg, proc, "Secondary finalizer", 0, false); sft.start(); try { sft.join(); @@ -190,10 +189,10 @@ final class Finalizer extends FinalReference { /* Package-private; must }}}); } - private static class FinalizerThread extends ManagedLocalsThread { + private static class FinalizerThread extends Thread { private volatile boolean running; FinalizerThread(ThreadGroup g) { - super(g, "Finalizer"); + super(g, null, "Finalizer", 0, false); } public void run() { // in case of recursive call to run() diff --git a/jdk/src/java.base/share/classes/java/lang/ref/Reference.java b/jdk/src/java.base/share/classes/java/lang/ref/Reference.java index d18f059308f..b8b9c432848 100644 --- a/jdk/src/java.base/share/classes/java/lang/ref/Reference.java +++ b/jdk/src/java.base/share/classes/java/lang/ref/Reference.java @@ -29,7 +29,6 @@ import sun.misc.Cleaner; import jdk.internal.HotSpotIntrinsicCandidate; import jdk.internal.misc.JavaLangRefAccess; import jdk.internal.misc.SharedSecrets; -import sun.misc.ManagedLocalsThread; /** * Abstract base class for reference objects. This class defines the @@ -128,7 +127,7 @@ public abstract class Reference { /* High-priority thread to enqueue pending References */ - private static class ReferenceHandler extends ManagedLocalsThread { + private static class ReferenceHandler extends Thread { private static void ensureClassInitialized(Class clazz) { try { @@ -147,7 +146,7 @@ public abstract class Reference { } ReferenceHandler(ThreadGroup g, String name) { - super(g, name); + super(g, null, name, 0, false); } public void run() { diff --git a/jdk/src/java.base/share/classes/sun/misc/GC.java b/jdk/src/java.base/share/classes/sun/misc/GC.java index 4cfd9781274..084f41b506a 100644 --- a/jdk/src/java.base/share/classes/sun/misc/GC.java +++ b/jdk/src/java.base/share/classes/sun/misc/GC.java @@ -82,7 +82,7 @@ public class GC { */ public static native long maxObjectInspectionAge(); - private static class Daemon extends ManagedLocalsThread { + private static class Daemon extends Thread { public void run() { for (;;) { @@ -122,7 +122,7 @@ public class GC { } private Daemon(ThreadGroup tg) { - super(tg, "GC Daemon"); + super(tg, null, "GC Daemon", 0L, false); } /* Create a new daemon thread in the root thread group */ diff --git a/jdk/src/java.base/share/classes/sun/misc/InnocuousThread.java b/jdk/src/java.base/share/classes/sun/misc/InnocuousThread.java index a9c129cd79e..838a5da4cc6 100644 --- a/jdk/src/java.base/share/classes/sun/misc/InnocuousThread.java +++ b/jdk/src/java.base/share/classes/sun/misc/InnocuousThread.java @@ -35,8 +35,10 @@ import java.util.concurrent.atomic.AtomicInteger; * A thread that has no permissions, is not a member of any user-defined * ThreadGroup and supports the ability to erase ThreadLocals. */ -public final class InnocuousThread extends ManagedLocalsThread { +public final class InnocuousThread extends Thread { private static final jdk.internal.misc.Unsafe UNSAFE; + private static final long THREAD_LOCALS; + private static final long INHERITABLE_THREAD_LOCALS; private static final ThreadGroup INNOCUOUSTHREADGROUP; private static final AccessControlContext ACC; private static final long INHERITEDACCESSCONTROLCONTEXT; @@ -54,7 +56,7 @@ public final class InnocuousThread extends ManagedLocalsThread { } public InnocuousThread(ThreadGroup group, Runnable target, String name) { - super(group, target, name); + super(group, target, name, 0L, false); UNSAFE.putOrderedObject(this, INHERITEDACCESSCONTROLCONTEXT, ACC); UNSAFE.putOrderedObject(this, CONTEXTCLASSLOADER, ClassLoader.getSystemClassLoader()); } @@ -73,6 +75,14 @@ public final class InnocuousThread extends ManagedLocalsThread { throw new SecurityException("setContextClassLoader"); } + /** + * Drops all thread locals (and inherited thread locals). + */ + public final void eraseThreadLocals() { + UNSAFE.putObject(this, THREAD_LOCALS, null); + UNSAFE.putObject(this, INHERITABLE_THREAD_LOCALS, null); + } + // ensure run method is run only once private volatile boolean hasRun; @@ -96,6 +106,10 @@ public final class InnocuousThread extends ManagedLocalsThread { Class tk = Thread.class; Class gk = ThreadGroup.class; + THREAD_LOCALS = UNSAFE.objectFieldOffset + (tk.getDeclaredField("threadLocals")); + INHERITABLE_THREAD_LOCALS = UNSAFE.objectFieldOffset + (tk.getDeclaredField("inheritableThreadLocals")); INHERITEDACCESSCONTROLCONTEXT = UNSAFE.objectFieldOffset (tk.getDeclaredField("inheritedAccessControlContext")); CONTEXTCLASSLOADER = UNSAFE.objectFieldOffset diff --git a/jdk/src/java.base/share/classes/sun/misc/Signal.java b/jdk/src/java.base/share/classes/sun/misc/Signal.java index 92438833737..6094de35509 100644 --- a/jdk/src/java.base/share/classes/sun/misc/Signal.java +++ b/jdk/src/java.base/share/classes/sun/misc/Signal.java @@ -213,7 +213,7 @@ public final class Signal { } }; if (handler != null) { - new ManagedLocalsThread(runnable, sig + " handler").start(); + new Thread(null, runnable, sig + " handler", 0, false).start(); } } diff --git a/jdk/src/java.base/share/classes/sun/net/NetworkServer.java b/jdk/src/java.base/share/classes/sun/net/NetworkServer.java index f0cd8349c25..37881e765c0 100644 --- a/jdk/src/java.base/share/classes/sun/net/NetworkServer.java +++ b/jdk/src/java.base/share/classes/sun/net/NetworkServer.java @@ -27,7 +27,6 @@ package sun.net; import java.io.*; import java.net.Socket; import java.net.ServerSocket; -import sun.misc.ManagedLocalsThread; /** * This is the base class for network servers. To define a new type @@ -73,7 +72,7 @@ public class NetworkServer implements Runnable, Cloneable { NetworkServer n = (NetworkServer)clone(); n.serverSocket = null; n.clientSocket = ns; - new ManagedLocalsThread(n).start(); + new Thread(null, n, "NetworkServer", 0, false).start(); } catch(Exception e) { System.out.print("Server failure\n"); e.printStackTrace(); @@ -108,7 +107,7 @@ public class NetworkServer implements Runnable, Cloneable { for each new connection. */ public final void startServer(int port) throws IOException { serverSocket = new ServerSocket(port, 50); - serverInstance = new ManagedLocalsThread(this); + serverInstance = new Thread(null, this, "NetworkServer", 0, false); serverInstance.start(); } diff --git a/jdk/src/java.base/share/classes/sun/net/www/MimeLauncher.java b/jdk/src/java.base/share/classes/sun/net/www/MimeLauncher.java index 32b3603de82..d95ca3774ba 100644 --- a/jdk/src/java.base/share/classes/sun/net/www/MimeLauncher.java +++ b/jdk/src/java.base/share/classes/sun/net/www/MimeLauncher.java @@ -27,9 +27,8 @@ package sun.net.www; import java.net.URL; import java.io.*; import java.util.StringTokenizer; -import sun.misc.ManagedLocalsThread; -class MimeLauncher extends ManagedLocalsThread { +class MimeLauncher extends Thread { java.net.URLConnection uc; MimeEntry m; String genericTempFileTemplate; @@ -38,7 +37,7 @@ class MimeLauncher extends ManagedLocalsThread { MimeLauncher (MimeEntry M, java.net.URLConnection uc, InputStream is, String tempFileTemplate, String threadName) throws ApplicationLaunchException { - super(threadName); + super(null, null, threadName, 0, false); m = M; this.uc = uc; this.is = is; diff --git a/jdk/src/java.base/share/classes/sun/nio/fs/AbstractPoller.java b/jdk/src/java.base/share/classes/sun/nio/fs/AbstractPoller.java index 039f5b74c2d..84fbae5dddd 100644 --- a/jdk/src/java.base/share/classes/sun/nio/fs/AbstractPoller.java +++ b/jdk/src/java.base/share/classes/sun/nio/fs/AbstractPoller.java @@ -30,7 +30,6 @@ import java.security.AccessController; import java.security.PrivilegedAction; import java.io.IOException; import java.util.*; -import sun.misc.ManagedLocalsThread; /** * Base implementation of background poller thread used in watch service @@ -60,7 +59,7 @@ abstract class AbstractPoller implements Runnable { AccessController.doPrivileged(new PrivilegedAction<>() { @Override public Object run() { - Thread thr = new ManagedLocalsThread(thisRunnable); + Thread thr = new Thread(null, thisRunnable, "FileSystemWatchService", 0, false); thr.setDaemon(true); thr.start(); return null; diff --git a/jdk/src/java.base/share/classes/sun/nio/fs/Cancellable.java b/jdk/src/java.base/share/classes/sun/nio/fs/Cancellable.java index 4e283a5f157..5e3ff43a7b0 100644 --- a/jdk/src/java.base/share/classes/sun/nio/fs/Cancellable.java +++ b/jdk/src/java.base/share/classes/sun/nio/fs/Cancellable.java @@ -25,7 +25,6 @@ package sun.nio.fs; -import sun.misc.ManagedLocalsThread; import jdk.internal.misc.Unsafe; import java.util.concurrent.ExecutionException; @@ -118,7 +117,7 @@ abstract class Cancellable implements Runnable { * thread by writing into the memory location that it polls cooperatively. */ static void runInterruptibly(Cancellable task) throws ExecutionException { - Thread t = new ManagedLocalsThread(task); + Thread t = new Thread(null, task, "NIO-Task", 0, false); t.start(); boolean cancelledByInterrupt = false; while (t.isAlive()) { diff --git a/jdk/src/java.base/share/classes/sun/nio/fs/PollingWatchService.java b/jdk/src/java.base/share/classes/sun/nio/fs/PollingWatchService.java index bc4af73e762..536bfa7accf 100644 --- a/jdk/src/java.base/share/classes/sun/nio/fs/PollingWatchService.java +++ b/jdk/src/java.base/share/classes/sun/nio/fs/PollingWatchService.java @@ -35,7 +35,6 @@ import java.io.IOException; import java.util.*; import java.util.concurrent.*; import com.sun.nio.file.SensitivityWatchEventModifier; -import sun.misc.ManagedLocalsThread; /** * Simple WatchService implementation that uses periodic tasks to poll @@ -59,7 +58,7 @@ class PollingWatchService .newSingleThreadScheduledExecutor(new ThreadFactory() { @Override public Thread newThread(Runnable r) { - Thread t = new ManagedLocalsThread(r); + Thread t = new Thread(null, r, "FileSystemWatchService", 0, false); t.setDaemon(true); return t; }}); diff --git a/jdk/src/java.base/share/classes/sun/security/provider/SeedGenerator.java b/jdk/src/java.base/share/classes/sun/security/provider/SeedGenerator.java index 87604b5637f..dd55044d820 100644 --- a/jdk/src/java.base/share/classes/sun/security/provider/SeedGenerator.java +++ b/jdk/src/java.base/share/classes/sun/security/provider/SeedGenerator.java @@ -75,7 +75,6 @@ import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.Random; -import sun.misc.ManagedLocalsThread; import sun.security.util.Debug; abstract class SeedGenerator { @@ -305,9 +304,11 @@ abstract class SeedGenerator { } finalsg[0] = new ThreadGroup (group, "SeedGenerator ThreadGroup"); - Thread newT = new ManagedLocalsThread(finalsg[0], + Thread newT = new Thread(finalsg[0], ThreadedSeedGenerator.this, - "SeedGenerator Thread"); + "SeedGenerator Thread", + 0, + false); newT.setPriority(Thread.MIN_PRIORITY); newT.setDaemon(true); return newT; @@ -342,8 +343,8 @@ abstract class SeedGenerator { // Start some noisy threads try { BogusThread bt = new BogusThread(); - Thread t = new ManagedLocalsThread - (seedGroup, bt, "SeedGenerator Thread"); + Thread t = new Thread + (seedGroup, bt, "SeedGenerator Thread", 0, false); t.start(); } catch (Exception e) { throw new InternalError("internal error: " + diff --git a/jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java b/jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java index 0162dc3b477..9b357d3b42f 100644 --- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java +++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java @@ -40,7 +40,6 @@ import java.util.concurrent.locks.ReentrantLock; import javax.crypto.BadPaddingException; import javax.net.ssl.*; -import sun.misc.ManagedLocalsThread; import jdk.internal.misc.JavaNetInetAddressAccess; import jdk.internal.misc.SharedSecrets; @@ -1153,10 +1152,13 @@ public final class SSLSocketImpl extends BaseSSLSocketImpl { HandshakeCompletedEvent event = new HandshakeCompletedEvent(this, sess); - Thread thread = new ManagedLocalsThread( + Thread thread = new Thread( + null, new NotifyHandshake( handshakeListeners.entrySet(), event), - "HandshakeCompletedNotify-Thread"); + "HandshakeCompletedNotify-Thread", + 0, + false); thread.start(); } } diff --git a/jdk/src/java.base/windows/classes/sun/nio/ch/WindowsSelectorImpl.java b/jdk/src/java.base/windows/classes/sun/nio/ch/WindowsSelectorImpl.java index efeba7f9ec9..6fa5efb1cbd 100644 --- a/jdk/src/java.base/windows/classes/sun/nio/ch/WindowsSelectorImpl.java +++ b/jdk/src/java.base/windows/classes/sun/nio/ch/WindowsSelectorImpl.java @@ -40,7 +40,6 @@ import java.util.List; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; -import sun.misc.ManagedLocalsThread; /** * A multi-threaded implementation of Selector for Windows. @@ -404,13 +403,14 @@ final class WindowsSelectorImpl extends SelectorImpl { } // Represents a helper thread used for select. - private final class SelectThread extends ManagedLocalsThread { + private final class SelectThread extends Thread { private final int index; // index of this thread final SubSelector subSelector; private long lastRun = 0; // last run number private volatile boolean zombie; // Creates a new thread private SelectThread(int i) { + super(null, null, "SelectorHelper", 0, false); this.index = i; this.subSelector = new SubSelector(i); //make sure we wait for next round of poll diff --git a/jdk/test/java/lang/Thread/ITLConstructor.java b/jdk/test/java/lang/Thread/ITLConstructor.java new file mode 100644 index 00000000000..d4858cc893a --- /dev/null +++ b/jdk/test/java/lang/Thread/ITLConstructor.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2015, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary Basic test for Thread(ThreadGroup,Runnable,String,long,boolean) + */ + +public class ITLConstructor { + static InheritableThreadLocal n = new InheritableThreadLocal<>() { + protected Integer initialValue() { + return 0; + } + + protected Integer childValue(Integer parentValue) { + return parentValue + 1; + } + }; + + static final int CHILD_THREAD_COUNT = 10; + + public static void main(String args[]) throws Exception { + test(true); + test(false); + } + + static void test(boolean inherit) throws Exception { + // concurrent access to separate indexes is ok + int[] x = new int[CHILD_THREAD_COUNT]; + Thread child = new Thread(Thread.currentThread().getThreadGroup(), + new AnotherRunnable(0, x, inherit), + "ITLConstructor-thread-"+(0), + 0, + inherit); + child.start(); + child.join(); // waits for *all* threads to complete + + // Check results + for(int i=0; i