8056152: API to create Threads that do not inherit inheritable thread-local initial values

Reviewed-by: alanb, dholmes, mchung, mr, rriggs
This commit is contained in:
Chris Hegarty 2015-12-18 16:06:24 +00:00
parent d36b231c45
commit 355dac1a4c
16 changed files with 213 additions and 39 deletions

View File

@ -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.
*
* <p>Note: During the creation of a new {@link
* Thread#Thread(ThreadGroup,Runnable,String,long,boolean) thread}, it is
* possible to <i>opt out</i> of receiving initial values for inheritable
* thread-local variables.
*
* @author Josh Bloch and Doug Lea
* @see ThreadLocal
* @since 1.2

View File

@ -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}.
*
* <p> 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.
*
* <p> 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 <code>run</code> method of this thread.

View File

@ -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<Object> { /* Package-private; must be in
@ -131,7 +130,7 @@ final class Finalizer extends FinalReference<Object> { /* 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<Object> { /* 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()

View File

@ -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<T> {
/* 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<T> {
}
ReferenceHandler(ThreadGroup g, String name) {
super(g, name);
super(g, null, name, 0, false);
}
public void run() {

View File

@ -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 */

View File

@ -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

View File

@ -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();
}
}

View File

@ -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();
}

View File

@ -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;

View File

@ -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;

View File

@ -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()) {

View File

@ -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;
}});

View File

@ -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: " +

View File

@ -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();
}
}

View File

@ -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

View File

@ -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<Integer> 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<CHILD_THREAD_COUNT; i++) {
int expectedValue = 1;
if (inherit)
expectedValue = i+1;
if (x[i] != expectedValue)
throw (new Exception("Got x[" + i + "] = " + x[i]
+ ", expected: " + expectedValue));
}
}
static class AnotherRunnable implements Runnable {
final int threadId;
final int[] x;
final boolean inherit;
AnotherRunnable(int threadId, int[] x, boolean inherit) {
this.threadId = threadId;
this.x = x;
this.inherit = inherit;
}
public void run() {
int itlValue = n.get();
if (threadId < CHILD_THREAD_COUNT-1) {
Thread child = new Thread(Thread.currentThread().getThreadGroup(),
new AnotherRunnable(threadId+1, x, inherit),
"ITLConstructor-thread-" + (threadId+1),
0,
inherit);
child.start();
try {
child.join();
} catch(InterruptedException e) {
throw(new RuntimeException("Interrupted", e));
}
}
x[threadId] = itlValue+1;
}
}
}