8284161: Implementation of Virtual Threads (Preview)

Co-authored-by: Ron Pressler <rpressler@openjdk.org>
Co-authored-by: Alan Bateman <alanb@openjdk.org>
Co-authored-by: Erik Österlund <eosterlund@openjdk.org>
Co-authored-by: Andrew Haley <aph@openjdk.org>
Co-authored-by: Rickard Bäckman <rbackman@openjdk.org>
Co-authored-by: Markus Grönlund <mgronlun@openjdk.org>
Co-authored-by: Leonid Mesnik <lmesnik@openjdk.org>
Co-authored-by: Serguei Spitsyn <sspitsyn@openjdk.org>
Co-authored-by: Chris Plummer <cjplummer@openjdk.org>
Co-authored-by: Coleen Phillimore <coleenp@openjdk.org>
Co-authored-by: Robbin Ehn <rehn@openjdk.org>
Co-authored-by: Stefan Karlsson <stefank@openjdk.org>
Co-authored-by: Thomas Schatzl <tschatzl@openjdk.org>
Co-authored-by: Sergey Kuksenko <skuksenko@openjdk.org>
Reviewed-by: lancea, eosterlund, rehn, sspitsyn, stefank, tschatzl, dfuchs, lmesnik, dcubed, kevinw, amenkov, dlong, mchung, psandoz, bpb, coleenp, smarks, egahlin, mseledtsov, coffeys, darcy
This commit is contained in:
Alan Bateman 2022-05-07 08:06:16 +00:00
parent 5212535a27
commit 9583e3657e
1133 changed files with 95935 additions and 8335 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2022, 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
@ -25,6 +25,7 @@
package java.lang;
import java.util.*;
import java.util.concurrent.RejectedExecutionException;
/*
* Class to track and run user level shutdown hooks registered through
@ -97,9 +98,15 @@ class ApplicationShutdownHooks {
threads = hooks.keySet();
hooks = null;
}
for (Thread hook : threads) {
hook.start();
try {
hook.start();
} catch (IllegalThreadStateException ignore) {
// already started
} catch (RejectedExecutionException ignore) {
// scheduler shutdown?
assert hook.isVirtual();
}
}
for (Thread hook : threads) {
while (true) {

View file

@ -24,7 +24,6 @@
*/
package java.lang;
import java.lang.ref.*;
/**
* This class extends {@code ThreadLocal} to provide inheritance of values
@ -49,6 +48,7 @@ import java.lang.ref.*;
* @author Josh Bloch and Doug Lea
* @see ThreadLocal
* @since 1.2
* @see Thread.Builder#inheritInheritableThreadLocals(boolean)
*/
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
@ -78,8 +78,9 @@ public class InheritableThreadLocal<T> extends ThreadLocal<T> {
*
* @param t the current thread
*/
@Override
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
return t.inheritableThreadLocals;
}
/**
@ -88,6 +89,7 @@ public class InheritableThreadLocal<T> extends ThreadLocal<T> {
* @param t the current thread
* @param firstValue value for the initial entry of the table.
*/
@Override
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2022, 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
@ -27,6 +27,8 @@ package java.lang;
import java.lang.StackWalker.StackFrame;
import java.util.EnumSet;
import java.util.Set;
import jdk.internal.vm.Continuation;
import jdk.internal.vm.ContinuationScope;
import static java.lang.StackWalker.ExtendedOption.LOCALS_AND_OPERANDS;
@ -177,11 +179,74 @@ interface LiveStackFrame extends StackFrame {
* and it denies access to {@code RuntimePermission("getStackWalkerWithClassReference")}.
*/
public static StackWalker getStackWalker(Set<StackWalker.Option> options) {
return getStackWalker(options, null);
}
/**
* Gets a {@code StackWalker} instance with the given options specifying
* the stack frame information it can access, and which will traverse at most
* the given {@code maxDepth} number of stack frames. If no option is
* specified, this {@code StackWalker} obtains the method name and
* the class name with all
* {@linkplain StackWalker.Option#SHOW_HIDDEN_FRAMES hidden frames} skipped.
* The returned {@code StackWalker} can get locals and operands.
*
* @param options stack walk {@link StackWalker.Option options}
* @param contScope the continuation scope up to which (inclusive) to walk the stack
*
* @throws SecurityException if the security manager is present and
* it denies access to {@code RuntimePermission("liveStackFrames")}; or
* or if the given {@code options} contains
* {@link StackWalker.Option#RETAIN_CLASS_REFERENCE Option.RETAIN_CLASS_REFERENCE}
* and it denies access to {@code RuntimePermission("getStackWalkerWithClassReference")}.
*/
public static StackWalker getStackWalker(Set<StackWalker.Option> options, ContinuationScope contScope) {
@SuppressWarnings("removal")
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("liveStackFrames"));
}
return StackWalker.newInstance(options, LOCALS_AND_OPERANDS);
return StackWalker.newInstance(options, LOCALS_AND_OPERANDS, contScope);
}
/**
* Gets {@code StackWalker} of the given unmounted continuation, that can get locals and operands.
*
* @param continuation the continuation to walk
*
* @throws SecurityException if the security manager is present and
* denies access to {@code RuntimePermission("liveStackFrames")}
*/
public static StackWalker getStackWalker(Continuation continuation) {
return getStackWalker(EnumSet.noneOf(StackWalker.Option.class), continuation.getScope(), continuation);
}
/**
* Gets a {@code StackWalker} instance with the given options specifying
* the stack frame information it can access, and which will traverse at most
* the given {@code maxDepth} number of stack frames. If no option is
* specified, this {@code StackWalker} obtains the method name and
* the class name with all
* {@linkplain StackWalker.Option#SHOW_HIDDEN_FRAMES hidden frames} skipped.
* The returned {@code StackWalker} can get locals and operands.
*
* @param options stack walk {@link StackWalker.Option options}
* @param continuation the continuation to walk
*
* @throws SecurityException if the security manager is present and
* it denies access to {@code RuntimePermission("liveStackFrames")}; or
* or if the given {@code options} contains
* {@link StackWalker.Option#RETAIN_CLASS_REFERENCE Option.RETAIN_CLASS_REFERENCE}
* and it denies access to {@code RuntimePermission("getStackWalkerWithClassReference")}.
*/
public static StackWalker getStackWalker(Set<StackWalker.Option> options,
ContinuationScope contScope,
Continuation continuation) {
@SuppressWarnings("removal")
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("liveStackFrames"));
}
return StackWalker.newInstance(options, LOCALS_AND_OPERANDS, contScope, continuation);
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1994, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1994, 2022, 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
@ -25,6 +25,7 @@
package java.lang;
import jdk.internal.misc.Blocker;
import jdk.internal.vm.annotation.IntrinsicCandidate;
/**
@ -79,11 +80,11 @@ public class Object {
* This integer need not remain consistent from one execution of an
* application to another execution of the same application.
* <li>If two objects are equal according to the {@link
* equals(Object) equals} method, then calling the {@code
* #equals(Object) equals} method, then calling the {@code
* hashCode} method on each of the two objects must produce the
* same integer result.
* <li>It is <em>not</em> required that if two objects are unequal
* according to the {@link equals(Object) equals} method, then
* according to the {@link #equals(Object) equals} method, then
* calling the {@code hashCode} method on each of the two objects
* must produce distinct integer results. However, the programmer
* should be aware that producing distinct integer results for
@ -148,7 +149,7 @@ public class Object {
* relation, each equivalence class only has a single element.
*
* @apiNote
* It is generally necessary to override the {@link hashCode hashCode}
* It is generally necessary to override the {@link #hashCode() hashCode}
* method whenever this method is overridden, so as to maintain the
* general contract for the {@code hashCode} method, which states
* that equal objects must have equal hash codes.
@ -359,7 +360,22 @@ public class Object {
* @see #wait()
* @see #wait(long, int)
*/
public final native void wait(long timeoutMillis) throws InterruptedException;
public final void wait(long timeoutMillis) throws InterruptedException {
long comp = Blocker.begin();
try {
wait0(timeoutMillis);
} catch (InterruptedException e) {
Thread thread = Thread.currentThread();
if (thread.isVirtual())
thread.getAndClearInterrupt();
throw e;
} finally {
Blocker.end(comp);
}
}
// final modifier so method not in vtable
private final native void wait0(long timeoutMillis) throws InterruptedException;
/**
* Causes the current thread to wait until it is awakened, typically

View file

@ -0,0 +1,132 @@
/*
* Copyright (c) 2020, 2022, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package java.lang;
import java.io.PrintStream;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import static java.lang.StackWalker.Option.*;
/**
* Helper class to print the virtual thread stack trace when pinned.
*
* The class maintains a ClassValue with the hashes of stack traces that are pinned by
* code in that Class. This is used to avoid printing the same stack trace many times.
*/
class PinnedThreadPrinter {
static final StackWalker STACK_WALKER;
static {
var options = Set.of(SHOW_REFLECT_FRAMES, RETAIN_CLASS_REFERENCE);
PrivilegedAction<StackWalker> pa = () ->
LiveStackFrame.getStackWalker(options, VirtualThread.continuationScope());
@SuppressWarnings("removal")
var stackWalker = AccessController.doPrivileged(pa);
STACK_WALKER = stackWalker;
}
private static final ClassValue<Hashes> HASHES = new ClassValue<>() {
@Override
protected Hashes computeValue(Class<?> type) {
return new Hashes();
}
};
@SuppressWarnings("serial")
private static class Hashes extends LinkedHashMap<Integer, Boolean> {
boolean add(int hash) {
return (putIfAbsent(hash, Boolean.TRUE) == null);
}
@Override
protected boolean removeEldestEntry(Map.Entry<Integer, Boolean> oldest) {
// limit number of hashes
return size() > 8;
}
}
/**
* Returns a hash of the given stack trace. The hash is based on the class,
* method and bytecode index.
*/
private static int hash(List<LiveStackFrame> stack) {
int hash = 0;
for (LiveStackFrame frame : stack) {
hash = (31 * hash) + Objects.hash(frame.getDeclaringClass(),
frame.getMethodName(),
frame.getByteCodeIndex());
}
return hash;
}
/**
* Prints the stack trace of the current mounted vthread continuation.
*
* @param printAll true to print all stack frames, false to only print the
* frames that are native or holding a monitor
*/
static void printStackTrace(PrintStream out, boolean printAll) {
List<LiveStackFrame> stack = STACK_WALKER.walk(s ->
s.map(f -> (LiveStackFrame) f)
.filter(f -> f.getDeclaringClass() != PinnedThreadPrinter.class)
.collect(Collectors.toList())
);
// find the closest frame that is causing the thread to be pinned
stack.stream()
.filter(f -> (f.isNativeMethod() || f.getMonitors().length > 0))
.map(LiveStackFrame::getDeclaringClass)
.findFirst()
.ifPresent(klass -> {
int hash = hash(stack);
Hashes hashes = HASHES.get(klass);
synchronized (hashes) {
// print the stack trace if not already seen
if (hashes.add(hash)) {
printStackTrace(stack, out, printAll);
}
}
});
}
private static void printStackTrace(List<LiveStackFrame> stack,
PrintStream out,
boolean printAll) {
out.println(Thread.currentThread());
for (LiveStackFrame frame : stack) {
var ste = frame.toStackTraceElement();
int monitorCount = frame.getMonitors().length;
if (monitorCount > 0 || frame.isNativeMethod()) {
out.format(" %s <== monitors:%d%n", ste, monitorCount);
} else if (printAll) {
out.format(" %s%n", ste);
}
}
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1994, 2022, 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
@ -26,44 +26,19 @@
package java.lang;
/**
* The {@code Runnable} interface should be implemented by any
* class whose instances are intended to be executed by a thread. The
* class must define a method of no arguments called {@code run}.
* <p>
* This interface is designed to provide a common protocol for objects that
* wish to execute code while they are active. For example,
* {@code Runnable} is implemented by class {@code Thread}.
* Being active simply means that a thread has been started and has not
* yet been stopped.
* <p>
* In addition, {@code Runnable} provides the means for a class to be
* active while not subclassing {@code Thread}. A class that implements
* {@code Runnable} can run without subclassing {@code Thread}
* by instantiating a {@code Thread} instance and passing itself in
* as the target. In most cases, the {@code Runnable} interface should
* be used if you are only planning to override the {@code run()}
* method and no other {@code Thread} methods.
* This is important because classes should not be subclassed
* unless the programmer intends on modifying or enhancing the fundamental
* behavior of the class.
* Represents an operation that does not return a result.
*
* <p> This is a {@linkplain java.util.function functional interface}
* whose functional method is {@link #run()}.
*
* @author Arthur van Hoff
* @see java.lang.Thread
* @see java.util.concurrent.Callable
* @since 1.0
*/
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface {@code Runnable} is used
* to create a thread, starting the thread causes the object's
* {@code run} method to be called in that separately executing
* thread.
* <p>
* The general contract of the method {@code run} is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
* Runs this operation.
*/
public abstract void run();
void run();
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2022, 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
@ -26,6 +26,7 @@ package java.lang;
import jdk.internal.access.JavaLangInvokeAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.vm.ContinuationScope;
import java.lang.StackWalker.StackFrame;
import java.lang.invoke.MethodType;
@ -35,8 +36,9 @@ class StackFrameInfo implements StackFrame {
SharedSecrets.getJavaLangInvokeAccess();
private final boolean retainClassRef;
private final Object memberName; // MemberName initialized by VM
private int bci; // initialized by VM to >= 0
private Object memberName; // MemberName initialized by VM
private int bci; // initialized by VM to >= 0
private ContinuationScope contScope;
private volatile StackTraceElement ste;
/*
@ -115,6 +117,10 @@ class StackFrameInfo implements StackFrame {
return JLIA.isNative(memberName);
}
private String getContinuationScopeName() {
return contScope != null ? contScope.getName() : null;
}
@Override
public String toString() {
return toStackTraceElement().toString();

View file

@ -41,6 +41,8 @@ import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import jdk.internal.vm.Continuation;
import jdk.internal.vm.ContinuationScope;
import sun.security.action.GetPropertyAction;
import static java.lang.StackStreamFactory.WalkerState.*;
@ -126,6 +128,8 @@ final class StackStreamFactory {
protected int depth; // traversed stack depth
protected FrameBuffer<? extends T> frameBuffer;
protected long anchor;
protected final ContinuationScope contScope;
protected Continuation continuation;
// buffers to fill in stack frame information
protected AbstractStackWalker(StackWalker walker, int mode) {
@ -137,6 +141,14 @@ final class StackStreamFactory {
this.walker = walker;
this.maxDepth = maxDepth;
this.depth = 0;
ContinuationScope scope = walker.getContScope();
if (scope == null && thread.isVirtual()) {
this.contScope = VirtualThread.continuationScope();
this.continuation = null;
} else {
this.contScope = scope;
this.continuation = walker.getContinuation();
}
}
private int toStackWalkMode(StackWalker walker, int mode) {
@ -236,6 +248,12 @@ final class StackStreamFactory {
*/
final R walk() {
checkState(NEW);
return (continuation != null)
? Continuation.wrapWalk(continuation, contScope, this::walkHelper)
: walkHelper();
}
private final R walkHelper() {
try {
// VM will need to stabilize the stack before walking. It will invoke
// the AbstractStackWalker::doStackWalk method once it fetches the first batch.
@ -311,7 +329,10 @@ final class StackStreamFactory {
*/
private int getNextBatch() {
int nextBatchSize = Math.min(maxDepth - depth, getNextBatchSize());
if (!frameBuffer.isActive() || nextBatchSize <= 0) {
if (!frameBuffer.isActive()
|| (nextBatchSize <= 0)
|| (frameBuffer.isAtBottom() && !hasMoreContinuations())) {
if (isDebug) {
System.out.format(" more stack walk done%n");
}
@ -319,7 +340,26 @@ final class StackStreamFactory {
return 0;
}
return fetchStackFrames(nextBatchSize);
if (frameBuffer.isAtBottom() && hasMoreContinuations()) {
setContinuation(continuation.getParent());
}
int numFrames = fetchStackFrames(nextBatchSize);
if (numFrames == 0 && !hasMoreContinuations()) {
frameBuffer.freeze(); // done stack walking
}
return numFrames;
}
private boolean hasMoreContinuations() {
return (continuation != null)
&& (continuation.getScope() != contScope)
&& (continuation.getParent() != null);
}
private void setContinuation(Continuation cont) {
this.continuation = cont;
setContinuation(anchor, frameBuffer.frames(), cont);
}
/*
@ -368,6 +408,7 @@ final class StackStreamFactory {
initFrameBuffer();
return callStackWalk(mode, 0,
contScope, continuation,
frameBuffer.curBatchFrameCount(),
frameBuffer.startIndex(),
frameBuffer.frames());
@ -390,10 +431,10 @@ final class StackStreamFactory {
System.out.format(" more stack walk requesting %d got %d to %d frames%n",
batchSize, frameBuffer.startIndex(), endIndex);
}
int numFrames = endIndex - startIndex;
if (numFrames == 0) {
frameBuffer.freeze(); // done stack walking
} else {
if (numFrames > 0) {
frameBuffer.setBatch(depth, startIndex, endIndex);
}
return numFrames;
@ -405,6 +446,8 @@ final class StackStreamFactory {
*
* @param mode mode of stack walking
* @param skipframes number of frames to be skipped before filling the frame buffer.
* @param contScope the continuation scope to walk.
* @param continuation the continuation to walk, or {@code null} if walking a thread.
* @param batchSize the batch size, max. number of elements to be filled in the frame buffers.
* @param startIndex start index of the frame buffers to be filled.
* @param frames Either a Class<?> array, if mode is {@link #FILL_CLASS_REFS_ONLY}
@ -412,6 +455,7 @@ final class StackStreamFactory {
* @return Result of AbstractStackWalker::doStackWalk
*/
private native R callStackWalk(long mode, int skipframes,
ContinuationScope contScope, Continuation continuation,
int batchSize, int startIndex,
T[] frames);
@ -430,6 +474,8 @@ final class StackStreamFactory {
private native int fetchStackFrames(long mode, long anchor,
int batchSize, int startIndex,
T[] frames);
private native void setContinuation(long anchor, T[] frames, Continuation cont);
}
/*
@ -497,6 +543,12 @@ final class StackStreamFactory {
final Class<?> at(int index) {
return stackFrames[index].declaringClass();
}
@Override
final boolean filter(int index) {
return stackFrames[index].declaringClass() == Continuation.class
&& "yield0".equals(stackFrames[index].getMethodName());
}
}
final Function<? super Stream<StackFrame>, ? extends T> function; // callback
@ -633,6 +685,9 @@ final class StackStreamFactory {
@Override
final Class<?> at(int index) { return classes[index];}
@Override
final boolean filter(int index) { return false; }
// ------ subclass may override the following methods -------
/**
@ -765,6 +820,12 @@ final class StackStreamFactory {
final Class<?> at(int index) {
return stackFrames[index].declaringClass();
}
@Override
final boolean filter(int index) {
return stackFrames[index].declaringClass() == Continuation.class
&& "yield0".equals(stackFrames[index].getMethodName());
}
}
LiveStackInfoTraverser(StackWalker walker,
@ -834,6 +895,13 @@ final class StackStreamFactory {
*/
abstract Class<?> at(int index);
/**
* Filter out frames at the top of a batch
* @param index the position of the frame.
* @return true if the frame should be skipped
*/
abstract boolean filter(int index);
// ------ subclass may override the following methods -------
/*
@ -881,7 +949,15 @@ final class StackStreamFactory {
* it is done for traversal. All stack frames have been traversed.
*/
final boolean isActive() {
return origin > 0 && (fence == 0 || origin < fence || fence == currentBatchSize);
return origin > 0; // && (fence == 0 || origin < fence || fence == currentBatchSize);
}
/*
* Tests if this frame buffer is at the end of the stack
* and all frames have been traversed.
*/
final boolean isAtBottom() {
return origin > 0 && origin >= fence && fence < currentBatchSize;
}
/**
@ -929,16 +1005,13 @@ final class StackStreamFactory {
this.origin = startIndex;
this.fence = endIndex;
if (depth == 0 && fence > 0) {
// filter the frames due to the stack stream implementation
for (int i = START_POS; i < fence; i++) {
Class<?> c = at(i);
if (isDebug) System.err.format(" frame %d: %s%n", i, c);
if (filterStackWalkImpl(c)) {
origin++;
} else {
break;
}
for (int i = START_POS; i < fence; i++) {
if (isDebug) System.err.format(" frame %d: %s%n", i, at(i));
if ((depth == 0 && filterStackWalkImpl(at(i))) // filter the frames due to the stack stream implementation
|| filter(i)) {
origin++;
} else {
break;
}
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2022, 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
@ -529,22 +529,17 @@ public final class StackTraceElement implements java.io.Serializable {
/*
* Returns an array of StackTraceElements of the given depth
* filled from the backtrace of a given Throwable.
* filled from the given backtrace.
*/
static StackTraceElement[] of(Throwable x, int depth) {
static StackTraceElement[] of(Object x, int depth) {
StackTraceElement[] stackTrace = new StackTraceElement[depth];
for (int i = 0; i < depth; i++) {
stackTrace[i] = new StackTraceElement();
}
// VM to fill in StackTraceElement
initStackTraceElements(stackTrace, x);
// ensure the proper StackTraceElement initialization
for (StackTraceElement ste : stackTrace) {
ste.computeFormat();
}
return stackTrace;
initStackTraceElements(stackTrace, x, depth);
return of(stackTrace);
}
/*
@ -558,12 +553,20 @@ public final class StackTraceElement implements java.io.Serializable {
return ste;
}
static StackTraceElement[] of(StackTraceElement[] stackTrace) {
// ensure the proper StackTraceElement initialization
for (StackTraceElement ste : stackTrace) {
ste.computeFormat();
}
return stackTrace;
}
/*
* Sets the given stack trace elements with the backtrace
* of the given Throwable.
*/
private static native void initStackTraceElements(StackTraceElement[] elements,
Throwable x);
Object x, int depth);
/*
* Sets the given stack trace element with the given StackFrameInfo
*/

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2022, 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
@ -33,6 +33,8 @@ import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Stream;
import jdk.internal.vm.Continuation;
import jdk.internal.vm.ContinuationScope;
/**
* A stack walker.
@ -293,6 +295,8 @@ public final class StackWalker {
private static final StackWalker DEFAULT_WALKER =
new StackWalker(DEFAULT_EMPTY_OPTION);
private final Continuation continuation;
private final ContinuationScope contScope;
private final Set<Option> options;
private final ExtendedOption extendedOption;
private final int estimateDepth;
@ -315,6 +319,24 @@ public final class StackWalker {
return DEFAULT_WALKER;
}
/**
* Returns a {@code StackWalker} instance.
*
* <p> This {@code StackWalker} is configured to skip all
* {@linkplain Option#SHOW_HIDDEN_FRAMES hidden frames} and
* no {@linkplain Option#RETAIN_CLASS_REFERENCE class reference} is retained.
*
* @param contScope the continuation scope up to which (inclusive) to walk the stack
*
* @return a {@code StackWalker} configured to skip all
* {@linkplain Option#SHOW_HIDDEN_FRAMES hidden frames} and
* no {@linkplain Option#RETAIN_CLASS_REFERENCE class reference} is retained.
*
*/
static StackWalker getInstance(ContinuationScope contScope) {
return getInstance(EnumSet.noneOf(Option.class), contScope);
}
/**
* Returns a {@code StackWalker} instance with the given option specifying
* the stack frame information it can access.
@ -336,6 +358,28 @@ public final class StackWalker {
return getInstance(EnumSet.of(Objects.requireNonNull(option)));
}
/**
* Returns a {@code StackWalker} instance with the given option specifying
* the stack frame information it can access.
*
* <p>
* If a security manager is present and the given {@code option} is
* {@link Option#RETAIN_CLASS_REFERENCE Option.RETAIN_CLASS_REFERENCE},
* it calls its {@link SecurityManager#checkPermission checkPermission}
* method for {@code RuntimePermission("getStackWalkerWithClassReference")}.
*
* @param option {@link Option stack walking option}
* @param contScope the continuation scope up to which (inclusive) to walk the stack
*
* @return a {@code StackWalker} configured with the given option
*
* @throws SecurityException if a security manager exists and its
* {@code checkPermission} method denies access.
*/
static StackWalker getInstance(Option option, ContinuationScope contScope) {
return getInstance(EnumSet.of(Objects.requireNonNull(option)), contScope);
}
/**
* Returns a {@code StackWalker} instance with the given {@code options} specifying
* the stack frame information it can access. If the given {@code options}
@ -357,13 +401,38 @@ public final class StackWalker {
* {@code checkPermission} method denies access.
*/
public static StackWalker getInstance(Set<Option> options) {
if (options.isEmpty()) {
return getInstance(options, null);
}
/**
* Returns a {@code StackWalker} instance with the given {@code options} specifying
* the stack frame information it can access. If the given {@code options}
* is empty, this {@code StackWalker} is configured to skip all
* {@linkplain Option#SHOW_HIDDEN_FRAMES hidden frames} and no
* {@linkplain Option#RETAIN_CLASS_REFERENCE class reference} is retained.
*
* <p>
* If a security manager is present and the given {@code options} contains
* {@link Option#RETAIN_CLASS_REFERENCE Option.RETAIN_CLASS_REFERENCE},
* it calls its {@link SecurityManager#checkPermission checkPermission}
* method for {@code RuntimePermission("getStackWalkerWithClassReference")}.
*
* @param options {@link Option stack walking option}
* @param contScope the continuation scope up to which (inclusive) to walk the stack
*
* @return a {@code StackWalker} configured with the given options
*
* @throws SecurityException if a security manager exists and its
* {@code checkPermission} method denies access.
*/
static StackWalker getInstance(Set<Option> options, ContinuationScope contScope) {
if (options.isEmpty() && contScope == null) {
return DEFAULT_WALKER;
}
EnumSet<Option> optionSet = toEnumSet(options);
checkPermission(optionSet);
return new StackWalker(optionSet);
return new StackWalker(optionSet, contScope);
}
/**
@ -404,16 +473,37 @@ public final class StackWalker {
// ----- private constructors ------
private StackWalker(EnumSet<Option> options) {
this(options, 0, null);
this(options, 0, null, null, null);
}
private StackWalker(EnumSet<Option> options, ContinuationScope contScope) {
this(options, 0, null, contScope, null);
}
private StackWalker(EnumSet<Option> options, ContinuationScope contScope, Continuation continuation) {
this(options, 0, null, contScope, continuation);
}
private StackWalker(EnumSet<Option> options, int estimateDepth) {
this(options, estimateDepth, null);
this(options, estimateDepth, null, null, null);
}
private StackWalker(EnumSet<Option> options, int estimateDepth, ExtendedOption extendedOption) {
private StackWalker(EnumSet<Option> options, int estimateDepth, ContinuationScope contScope) {
this(options, estimateDepth, null, contScope, null);
}
private StackWalker(EnumSet<Option> options,
int estimateDepth,
ContinuationScope contScope,
Continuation continuation) {
this(options, estimateDepth, null, contScope, continuation);
}
private StackWalker(EnumSet<Option> options,
int estimateDepth,
ExtendedOption extendedOption,
ContinuationScope contScope,
Continuation continuation) {
this.options = options;
this.estimateDepth = estimateDepth;
this.extendedOption = extendedOption;
this.retainClassRef = hasOption(Option.RETAIN_CLASS_REFERENCE);
this.contScope = contScope;
this.continuation = continuation;
}
private static void checkPermission(Set<Option> options) {
@ -597,6 +687,9 @@ public final class StackWalker {
throw new UnsupportedOperationException("This stack walker " +
"does not have RETAIN_CLASS_REFERENCE access");
}
if (continuation != null) {
throw new UnsupportedOperationException("This stack walker walks a continuation");
}
return StackStreamFactory.makeCallerFinder(this).findCaller();
}
@ -604,9 +697,19 @@ public final class StackWalker {
// ---- package access ----
static StackWalker newInstance(Set<Option> options, ExtendedOption extendedOption) {
return newInstance(options, extendedOption, null);
}
static StackWalker newInstance(Set<Option> options, ExtendedOption extendedOption, ContinuationScope contScope) {
EnumSet<Option> optionSet = toEnumSet(options);
checkPermission(optionSet);
return new StackWalker(optionSet, 0, extendedOption);
return new StackWalker(optionSet, 0, extendedOption, contScope, null);
}
static StackWalker newInstance(Set<Option> options, ExtendedOption extendedOption, ContinuationScope contScope, Continuation continuation) {
EnumSet<Option> optionSet = toEnumSet(options);
checkPermission(optionSet);
return new StackWalker(optionSet, 0, extendedOption, contScope, continuation);
}
int estimateDepth() {
@ -620,4 +723,12 @@ public final class StackWalker {
boolean hasLocalsOperandsOption() {
return extendedOption == ExtendedOption.LOCALS_AND_OPERANDS;
}
ContinuationScope getContScope() {
return contScope;
}
Continuation getContinuation() {
return continuation;
}
}

View file

@ -32,6 +32,7 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.annotation.Annotation;
import java.lang.invoke.MethodHandle;
@ -62,6 +63,7 @@ import java.util.PropertyPermission;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.Callable;
import java.util.function.Supplier;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;
@ -78,8 +80,13 @@ import jdk.internal.logger.LoggerFinderLoader;
import jdk.internal.logger.LazyLoggers;
import jdk.internal.logger.LocalizedLoggerWrapper;
import jdk.internal.util.SystemProps;
import jdk.internal.vm.Continuation;
import jdk.internal.vm.ContinuationScope;
import jdk.internal.vm.StackableScope;
import jdk.internal.vm.ThreadContainer;
import jdk.internal.vm.annotation.IntrinsicCandidate;
import jdk.internal.vm.annotation.Stable;
import jdk.internal.vm.annotation.ChangesCurrentThread;
import sun.nio.fs.DefaultFileSystemProvider;
import sun.reflect.annotation.AnnotationType;
import sun.nio.ch.Interruptible;
@ -2054,12 +2061,12 @@ public final class System {
/**
* Create PrintStream for stdout/err based on encoding.
*/
private static PrintStream newPrintStream(FileOutputStream fos, String enc) {
private static PrintStream newPrintStream(OutputStream out, String enc) {
if (enc != null) {
return new PrintStream(new BufferedOutputStream(fos, 128), true,
return new PrintStream(new BufferedOutputStream(out, 128), true,
Charset.forName(enc, UTF_8.INSTANCE));
}
return new PrintStream(new BufferedOutputStream(fos, 128), true);
return new PrintStream(new BufferedOutputStream(out, 128), true);
}
/**
@ -2180,17 +2187,10 @@ public final class System {
// classes are used.
VM.initializeOSEnvironment();
// The main thread is not added to its thread group in the same
// way as other threads; we must do it ourselves here.
Thread current = Thread.currentThread();
current.getThreadGroup().add(current);
// start Finalizer and Reference Handler threads
SharedSecrets.getJavaLangRefAccess().startThreads();
// Subsystems that are invoked during initialization can invoke
// VM.isBooted() in order to avoid doing things that should
// wait until the VM is fully initialized. The initialization level
// is incremented from 0 to 1 here to indicate the first phase of
// initialization has completed.
// IMPORTANT: Ensure that this remains the last initialization action!
// system properties, java.lang and other core classes are now initialized
VM.initLevel(1);
}
@ -2513,6 +2513,104 @@ public final class System {
public void exit(int statusCode) {
Shutdown.exit(statusCode);
}
public Thread[] getAllThreads() {
return Thread.getAllThreads();
}
public ThreadContainer threadContainer(Thread thread) {
return thread.threadContainer();
}
public void start(Thread thread, ThreadContainer container) {
thread.start(container);
}
public StackableScope headStackableScope(Thread thread) {
return thread.headStackableScopes();
}
public void setHeadStackableScope(StackableScope scope) {
Thread.setHeadStackableScope(scope);
}
public Thread currentCarrierThread() {
return Thread.currentCarrierThread();
}
@ChangesCurrentThread
public <V> V executeOnCarrierThread(Callable<V> task) throws Exception {
Thread thread = Thread.currentThread();
if (thread.isVirtual()) {
Thread carrier = Thread.currentCarrierThread();
carrier.setCurrentThread(carrier);
try {
return task.call();
} finally {
carrier.setCurrentThread(thread);
}
} else {
return task.call();
}
}
public <T> T getCarrierThreadLocal(ThreadLocal<T> local) {
return local.getCarrierThreadLocal();
}
public <T> void setCarrierThreadLocal(ThreadLocal<T> local, T value) {
local.setCarrierThreadLocal(value);
}
public Object[] extentLocalCache() {
return Thread.extentLocalCache();
}
public void setExtentLocalCache(Object[] cache) {
Thread.setExtentLocalCache(cache);
}
public Object extentLocalBindings() {
return Thread.extentLocalBindings();
}
public void setExtentLocalBindings(Object bindings) {
Thread.setExtentLocalBindings(bindings);
}
public Continuation getContinuation(Thread thread) {
return thread.getContinuation();
}
public void setContinuation(Thread thread, Continuation continuation) {
thread.setContinuation(continuation);
}
public ContinuationScope virtualThreadContinuationScope() {
return VirtualThread.continuationScope();
}
public void parkVirtualThread() {
VirtualThread.park();
}
public void parkVirtualThread(long nanos) {
VirtualThread.parkNanos(nanos);
}
public void unparkVirtualThread(Thread thread) {
if (thread instanceof VirtualThread vthread) {
vthread.unpark();
} else {
throw new IllegalArgumentException("Not a virtual thread");
}
}
public StackWalker newStackWalkerInstance(Set<StackWalker.Option> options,
ContinuationScope contScope,
Continuation continuation) {
return StackWalker.newInstance(options, null, contScope, continuation);
}
});
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,359 @@
/*
* Copyright (c) 2021, 2022, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package java.lang;
import java.lang.Thread.Builder;
import java.lang.Thread.Builder.OfPlatform;
import java.lang.Thread.Builder.OfVirtual;
import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadFactory;
/**
* Defines static methods to create platform and virtual thread builders.
*/
class ThreadBuilders {
/**
* Base implementation of ThreadBuilder.
*/
static abstract non-sealed
class BaseThreadBuilder<T extends Builder> implements Builder {
private String name;
private long counter;
private int characteristics;
private UncaughtExceptionHandler uhe;
String name() {
return name;
}
long counter() {
return counter;
}
int characteristics() {
return characteristics;
}
UncaughtExceptionHandler uncaughtExceptionHandler() {
return uhe;
}
String nextThreadName() {
if (name != null && counter >= 0) {
return name + (counter++);
} else {
return name;
}
}
@Override
@SuppressWarnings("unchecked")
public T name(String name) {
this.name = Objects.requireNonNull(name);
this.counter = -1;
return (T) this;
}
@Override
@SuppressWarnings("unchecked")
public T name(String prefix, long start) {
Objects.requireNonNull(prefix);
if (start < 0)
throw new IllegalArgumentException("'start' is negative");
this.name = prefix;
this.counter = start;
return (T) this;
}
@Override
@SuppressWarnings("unchecked")
public T allowSetThreadLocals(boolean allow) {
if (allow) {
characteristics &= ~Thread.NO_THREAD_LOCALS;
} else {
characteristics |= Thread.NO_THREAD_LOCALS;
}
return (T) this;
}
@Override
@SuppressWarnings("unchecked")
public T inheritInheritableThreadLocals(boolean inherit) {
if (inherit) {
characteristics &= ~Thread.NO_INHERIT_THREAD_LOCALS;
} else {
characteristics |= Thread.NO_INHERIT_THREAD_LOCALS;
}
return (T) this;
}
@Override
@SuppressWarnings("unchecked")
public T uncaughtExceptionHandler(UncaughtExceptionHandler ueh) {
this.uhe = Objects.requireNonNull(ueh);
return (T) this;
}
}
/**
* ThreadBuilder.OfPlatform implementation.
*/
static final class PlatformThreadBuilder
extends BaseThreadBuilder<OfPlatform> implements OfPlatform {
private ThreadGroup group;
private boolean daemon;
private boolean daemonChanged;
private int priority;
private long stackSize;
@Override
String nextThreadName() {
String name = super.nextThreadName();
return (name != null) ? name : Thread.genThreadName();
}
@Override
public OfPlatform group(ThreadGroup group) {
this.group = Objects.requireNonNull(group);
return this;
}
@Override
public OfPlatform daemon(boolean on) {
daemon = on;
daemonChanged = true;
return this;
}
@Override
public OfPlatform priority(int priority) {
if (priority < Thread.MIN_PRIORITY || priority > Thread.MAX_PRIORITY)
throw new IllegalArgumentException();
this.priority = priority;
return this;
}
@Override
public OfPlatform stackSize(long stackSize) {
if (stackSize < 0L)
throw new IllegalArgumentException();
this.stackSize = stackSize;
return this;
}
@Override
public Thread unstarted(Runnable task) {
Objects.requireNonNull(task);
String name = nextThreadName();
var thread = new Thread(group, name, characteristics(), task, stackSize, null);
if (daemonChanged)
thread.daemon(daemon);
if (priority != 0)
thread.priority(priority);
UncaughtExceptionHandler uhe = uncaughtExceptionHandler();
if (uhe != null)
thread.uncaughtExceptionHandler(uhe);
return thread;
}
@Override
public Thread start(Runnable task) {
Thread thread = unstarted(task);
thread.start();
return thread;
}
@Override
public ThreadFactory factory() {
return new PlatformThreadFactory(group, name(), counter(), characteristics(),
daemonChanged, daemon, priority, stackSize, uncaughtExceptionHandler());
}
}
/**
* ThreadBuilder.OfVirtual implementation.
*/
static final class VirtualThreadBuilder
extends BaseThreadBuilder<OfVirtual> implements OfVirtual {
private Executor scheduler; // set by tests
@Override
public Thread unstarted(Runnable task) {
Objects.requireNonNull(task);
var thread = new VirtualThread(scheduler, nextThreadName(), characteristics(), task);
UncaughtExceptionHandler uhe = uncaughtExceptionHandler();
if (uhe != null)
thread.uncaughtExceptionHandler(uhe);
return thread;
}
@Override
public Thread start(Runnable task) {
Thread thread = unstarted(task);
thread.start();
return thread;
}
@Override
public ThreadFactory factory() {
return new VirtualThreadFactory(scheduler, name(), counter(), characteristics(),
uncaughtExceptionHandler());
}
}
/**
* Base ThreadFactory implementation.
*/
private static abstract class BaseThreadFactory implements ThreadFactory {
private static final VarHandle COUNT;
static {
try {
MethodHandles.Lookup l = MethodHandles.lookup();
COUNT = l.findVarHandle(BaseThreadFactory.class, "count", long.class);
} catch (Exception e) {
throw new InternalError(e);
}
}
private final String name;
private final int characteristics;
private final UncaughtExceptionHandler uhe;
private final boolean hasCounter;
private volatile long count;
BaseThreadFactory(String name,
long start,
int characteristics,
UncaughtExceptionHandler uhe) {
this.name = name;
if (name != null && start >= 0) {
this.hasCounter = true;
this.count = start;
} else {
this.hasCounter = false;
}
this.characteristics = characteristics;
this.uhe = uhe;
}
int characteristics() {
return characteristics;
}
UncaughtExceptionHandler uncaughtExceptionHandler() {
return uhe;
}
String nextThreadName() {
if (hasCounter) {
return name + (long) COUNT.getAndAdd(this, 1);
} else {
return name;
}
}
}
/**
* ThreadFactory for platform threads.
*/
private static class PlatformThreadFactory extends BaseThreadFactory {
private final ThreadGroup group;
private final boolean daemonChanged;
private final boolean daemon;
private final int priority;
private final long stackSize;
PlatformThreadFactory(ThreadGroup group,
String name,
long start,
int characteristics,
boolean daemonChanged,
boolean daemon,
int priority,
long stackSize,
UncaughtExceptionHandler uhe) {
super(name, start, characteristics, uhe);
this.group = group;
this.daemonChanged = daemonChanged;
this.daemon = daemon;
this.priority = priority;
this.stackSize = stackSize;
}
@Override
String nextThreadName() {
String name = super.nextThreadName();
return (name != null) ? name : Thread.genThreadName();
}
@Override
public Thread newThread(Runnable task) {
Objects.requireNonNull(task);
String name = nextThreadName();
Thread thread = new Thread(group, name, characteristics(), task, stackSize, null);
if (daemonChanged)
thread.daemon(daemon);
if (priority != 0)
thread.priority(priority);
UncaughtExceptionHandler uhe = uncaughtExceptionHandler();
if (uhe != null)
thread.uncaughtExceptionHandler(uhe);
return thread;
}
}
/**
* ThreadFactory for virtual threads.
*/
private static class VirtualThreadFactory extends BaseThreadFactory {
private final Executor scheduler;
VirtualThreadFactory(Executor scheduler,
String name,
long start,
int characteristics,
UncaughtExceptionHandler uhe) {
super(name, start, characteristics, uhe);
this.scheduler = scheduler;
}
@Override
public Thread newThread(Runnable task) {
Objects.requireNonNull(task);
String name = nextThreadName();
Thread thread = new VirtualThread(scheduler, name, characteristics(), task);
UncaughtExceptionHandler uhe = uncaughtExceptionHandler();
if (uhe != null)
thread.uncaughtExceptionHandler(uhe);
return thread;
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -24,12 +24,12 @@
*/
package java.lang;
import jdk.internal.misc.TerminatingThreadLocal;
import java.lang.ref.WeakReference;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import jdk.internal.misc.TerminatingThreadLocal;
/**
* This class provides thread-local variables. These variables differ from
@ -156,21 +156,40 @@ public class ThreadLocal<T> {
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
* If the current thread does not support thread locals then
* this method returns its {@link #initialValue} (or {@code null}
* if the {@code initialValue} method is not overridden).
*
* @return the current thread's value of this thread-local
* @see Thread.Builder#allowSetThreadLocals(boolean)
*/
public T get() {
Thread t = Thread.currentThread();
return get(Thread.currentThread());
}
/**
* Returns the value in the current carrier thread's copy of this
* thread-local variable.
*/
T getCarrierThreadLocal() {
return get(Thread.currentCarrierThread());
}
private T get(Thread t) {
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
if (map == ThreadLocalMap.NOT_SUPPORTED) {
return initialValue();
} else {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T) e.value;
return result;
}
}
}
return setInitialValue();
return setInitialValue(t);
}
/**
@ -183,7 +202,11 @@ public class ThreadLocal<T> {
boolean isPresent() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
return map != null && map.getEntry(this) != null;
if (map != null && map != ThreadLocalMap.NOT_SUPPORTED) {
return map.getEntry(this) != null;
} else {
return false;
}
}
/**
@ -192,10 +215,10 @@ public class ThreadLocal<T> {
*
* @return the initial value
*/
private T setInitialValue() {
private T setInitialValue(Thread t) {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
assert map != ThreadLocalMap.NOT_SUPPORTED;
if (map != null) {
map.set(this, value);
} else {
@ -215,10 +238,25 @@ public class ThreadLocal<T> {
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*
* @throws UnsupportedOperationException if the current thread is not
* allowed to set its copy of thread-local variables
*
* @see Thread.Builder#allowSetThreadLocals(boolean)
*/
public void set(T value) {
Thread t = Thread.currentThread();
set(Thread.currentThread(), value);
}
void setCarrierThreadLocal(T value) {
set(Thread.currentCarrierThread(), value);
}
private void set(Thread t, T value) {
ThreadLocalMap map = getMap(t);
if (map == ThreadLocalMap.NOT_SUPPORTED) {
throw new UnsupportedOperationException();
}
if (map != null) {
map.set(this, value);
} else {
@ -239,7 +277,7 @@ public class ThreadLocal<T> {
*/
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null) {
if (m != null && m != ThreadLocalMap.NOT_SUPPORTED) {
m.remove(this);
}
}
@ -337,6 +375,9 @@ public class ThreadLocal<T> {
}
}
// Placeholder when thread locals not supported
static final ThreadLocalMap NOT_SUPPORTED = new ThreadLocalMap();
/**
* The initial capacity -- MUST be a power of two.
*/
@ -379,6 +420,12 @@ public class ThreadLocal<T> {
return ((i - 1 >= 0) ? i - 1 : len - 1);
}
/**
* Construct a new map without a table.
*/
private ThreadLocalMap() {
}
/**
* Construct a new map initially containing (firstKey, firstValue).
* ThreadLocalMaps are constructed lazily, so we only create
@ -421,6 +468,13 @@ public class ThreadLocal<T> {
}
}
/**
* Returns the number of elements in the map.
*/
int size() {
return size;
}
/**
* Get the entry associated with key. This method
* itself handles only the fast path: a direct hit of existing

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1994, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1994, 2022, 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
@ -27,6 +27,8 @@ package java.lang;
import java.io.*;
import java.util.*;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.InternalLock;
/**
* The {@code Throwable} class is the superclass of all errors and
@ -659,27 +661,39 @@ public class Throwable implements Serializable {
}
private void printStackTrace(PrintStreamOrWriter s) {
Object lock = s.lock();
if (lock instanceof InternalLock locker) {
locker.lock();
try {
lockedPrintStackTrace(s);
} finally {
locker.unlock();
}
} else synchronized (lock) {
lockedPrintStackTrace(s);
}
}
private void lockedPrintStackTrace(PrintStreamOrWriter s) {
// Guard against malicious overrides of Throwable.equals by
// using a Set with identity equality semantics.
Set<Throwable> dejaVu = Collections.newSetFromMap(new IdentityHashMap<>());
dejaVu.add(this);
synchronized (s.lock()) {
// Print our stack trace
s.println(this);
StackTraceElement[] trace = getOurStackTrace();
for (StackTraceElement traceElement : trace)
s.println("\tat " + traceElement);
// Print our stack trace
s.println(this);
StackTraceElement[] trace = getOurStackTrace();
for (StackTraceElement traceElement : trace)
s.println("\tat " + traceElement);
// Print suppressed exceptions, if any
for (Throwable se : getSuppressed())
se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION, "\t", dejaVu);
// Print suppressed exceptions, if any
for (Throwable se : getSuppressed())
se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION, "\t", dejaVu);
// Print cause, if any
Throwable ourCause = getCause();
if (ourCause != null)
ourCause.printEnclosedStackTrace(s, trace, CAUSE_CAPTION, "", dejaVu);
}
// Print cause, if any
Throwable ourCause = getCause();
if (ourCause != null)
ourCause.printEnclosedStackTrace(s, trace, CAUSE_CAPTION, "", dejaVu);
}
/**
@ -691,7 +705,7 @@ public class Throwable implements Serializable {
String caption,
String prefix,
Set<Throwable> dejaVu) {
assert Thread.holdsLock(s.lock());
assert s.isLockedByCurrentThread();
if (dejaVu.contains(this)) {
s.println(prefix + caption + "[CIRCULAR REFERENCE: " + this + "]");
} else {
@ -743,6 +757,15 @@ public class Throwable implements Serializable {
/** Returns the object to be locked when using this StreamOrWriter */
abstract Object lock();
boolean isLockedByCurrentThread() {
Object lock = lock();
if (lock instanceof InternalLock locker) {
return locker.isHeldByCurrentThread();
} else {
return Thread.holdsLock(lock);
}
}
/** Prints the specified string as a line on this StreamOrWriter */
abstract void println(Object o);
}
@ -755,7 +778,7 @@ public class Throwable implements Serializable {
}
Object lock() {
return printStream;
return SharedSecrets.getJavaIOPrintStreamAccess().lock(printStream);
}
void println(Object o) {
@ -771,7 +794,7 @@ public class Throwable implements Serializable {
}
Object lock() {
return printWriter;
return SharedSecrets.getJavaIOPrintWriterAccess().lock(printWriter);
}
void println(Object o) {
@ -833,11 +856,13 @@ public class Throwable implements Serializable {
private synchronized StackTraceElement[] getOurStackTrace() {
// Initialize stack trace field with information from
// backtrace if this is the first call to this method
if (stackTrace == UNASSIGNED_STACK ||
(stackTrace == null && backtrace != null) /* Out of protocol state */) {
stackTrace = StackTraceElement.of(this, depth);
} else if (stackTrace == null) {
return UNASSIGNED_STACK;
if (stackTrace == UNASSIGNED_STACK || stackTrace == null) {
if (backtrace != null) { /* Out of protocol state */
stackTrace = StackTraceElement.of(backtrace, depth);
} else {
// no backtrace, fillInStackTrace overridden or not called
return UNASSIGNED_STACK;
}
}
return stackTrace;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,73 @@
/*
* Copyright (c) 2021, 2022, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package java.lang;
/**
* Thrown to indicate that a method has been called on the wrong thread.
*
* @since 19
*/
public final class WrongThreadException extends RuntimeException {
@java.io.Serial
static final long serialVersionUID = 4676498871006316905L;
/**
* Constructs a WrongThreadException with no detail message.
*/
public WrongThreadException() {
super();
}
/**
* Constructs a WrongThreadException with the given detail message.
*
* @param s the String that contains a detailed message, can be null
*/
public WrongThreadException(String s) {
super(s);
}
/**
* Constructs a WrongThreadException with the given detail message and cause.
*
* @param message the detail message, can be null
* @param cause the cause, can be null
*/
public WrongThreadException(String message, Throwable cause) {
super(message, cause);
}
/**
* Constructs a WrongThreadException with the given cause and a detail
* message of {@code (cause==null ? null : cause.toString())} (which
* typically contains the class and detail message of {@code cause}).
*
* @param cause the cause, can be null
*/
public WrongThreadException(Throwable cause) {
super(cause);
}
}

View file

@ -42,6 +42,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Stream;
import jdk.internal.access.SharedSecrets;
import jdk.internal.vm.annotation.Stable;
import sun.invoke.util.BytecodeDescriptor;
import sun.invoke.util.VerifyType;
@ -1402,7 +1403,7 @@ s.writeObject(this.parameterArray());
public ConcurrentWeakInternSet() {
this.map = new ConcurrentHashMap<>(512);
this.stale = new ReferenceQueue<>();
this.stale = SharedSecrets.getJavaLangRefAccess().newNativeReferenceQueue();
}
/**

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2022, 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
@ -35,7 +35,7 @@ final class Finalizer extends FinalReference<Object> { /* Package-private; must
same package as the Reference
class */
private static ReferenceQueue<Object> queue = new ReferenceQueue<>();
private static ReferenceQueue<Object> queue = new NativeReferenceQueue<>();
/** Head of doubly linked list of Finalizers awaiting finalization. */
private static Finalizer unfinalized = null;
@ -166,16 +166,6 @@ final class Finalizer extends FinalReference<Object> { /* Package-private; must
if (running)
return;
// Finalizer thread starts before System.initializeSystemClass
// is called. Wait until JavaLangAccess is available
while (VM.initLevel() == 0) {
// delay until VM completes initialization
try {
VM.awaitInitLevel(1);
} catch (InterruptedException x) {
// ignore and continue
}
}
final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
running = true;
for (;;) {
@ -189,17 +179,15 @@ final class Finalizer extends FinalReference<Object> { /* Package-private; must
}
}
static {
/**
* Start the Finalizer thread as a daemon thread.
*/
static void startFinalizerThread(ThreadGroup tg) {
if (ENABLED) {
ThreadGroup tg = Thread.currentThread().getThreadGroup();
for (ThreadGroup tgn = tg;
tgn != null;
tg = tgn, tgn = tg.getParent());
Thread finalizer = new FinalizerThread(tg);
finalizer.setPriority(Thread.MAX_PRIORITY - 2);
finalizer.setDaemon(true);
finalizer.start();
}
}
}

View file

@ -0,0 +1,92 @@
/*
* Copyright (c) 2021, 2022, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package java.lang.ref;
/**
* An implementation of a ReferenceQueue that uses native monitors.
* The use of java.util.concurrent.lock locks interacts with various mechanisms,
* such as virtual threads and ForkJoinPool, that might not be appropriate for some
* low-level mechanisms, in particular MethodType's weak intern set.
*/
final class NativeReferenceQueue<T> extends ReferenceQueue<T> {
public NativeReferenceQueue() {
super(0);
}
private static class Lock { };
private final Lock lock = new Lock();
@Override
void signal() {
lock.notifyAll();
}
@Override
void await() throws InterruptedException {
lock.wait();
}
@Override
void await(long timeoutMillis) throws InterruptedException {
lock.wait(timeoutMillis);
}
@Override
boolean enqueue(Reference<? extends T> r) {
synchronized(lock) {
return enqueue0(r);
}
}
@Override
public Reference<? extends T> poll() {
if (headIsNull())
return null;
synchronized(lock) {
return poll0();
}
}
@Override
public Reference<? extends T> remove(long timeout)
throws IllegalArgumentException, InterruptedException {
if (timeout < 0)
throw new IllegalArgumentException("Negative timeout value");
if (timeout == 0)
return remove();
synchronized(lock) {
return remove0(timeout);
}
}
@Override
public Reference<? extends T> remove() throws InterruptedException {
synchronized(lock) {
return remove0();
}
}
}

View file

@ -25,6 +25,7 @@
package java.lang.ref;
import jdk.internal.misc.Unsafe;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.IntrinsicCandidate;
import jdk.internal.access.JavaLangRefAccess;
@ -192,27 +193,16 @@ public abstract sealed class Reference<T>
/* High-priority thread to enqueue pending References
*/
private static class ReferenceHandler extends Thread {
private static void ensureClassInitialized(Class<?> clazz) {
try {
Class.forName(clazz.getName(), true, clazz.getClassLoader());
} catch (ClassNotFoundException e) {
throw (Error) new NoClassDefFoundError(e.getMessage()).initCause(e);
}
}
static {
// pre-load and initialize Cleaner class so that we don't
// get into trouble later in the run loop if there's
// memory shortage while loading/initializing it lazily.
ensureClassInitialized(Cleaner.class);
}
ReferenceHandler(ThreadGroup g, String name) {
super(g, null, name, 0, false);
}
public void run() {
// pre-load and initialize Cleaner class so that we don't
// get into trouble later in the run loop if there's
// memory shortage while loading/initializing it lazily.
Unsafe.getUnsafe().ensureClassInitialized(Cleaner.class);
while (true) {
processPendingReferences();
}
@ -302,11 +292,10 @@ public abstract sealed class Reference<T>
}
}
static {
ThreadGroup tg = Thread.currentThread().getThreadGroup();
for (ThreadGroup tgn = tg;
tgn != null;
tg = tgn, tgn = tg.getParent());
/**
* Start the Reference Handler thread as a daemon thread.
*/
static void startReferenceHandlerThread(ThreadGroup tg) {
Thread handler = new ReferenceHandler(tg, "Reference Handler");
/* If there were a special system-only priority greater than
* MAX_PRIORITY, it would be used here
@ -314,9 +303,21 @@ public abstract sealed class Reference<T>
handler.setPriority(Thread.MAX_PRIORITY);
handler.setDaemon(true);
handler.start();
}
static {
// provide access in SharedSecrets
SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {
@Override
public void startThreads() {
ThreadGroup tg = Thread.currentThread().getThreadGroup();
for (ThreadGroup tgn = tg;
tgn != null;
tg = tgn, tgn = tg.getParent());
Reference.startReferenceHandlerThread(tg);
Finalizer.startFinalizerThread(tg);
}
@Override
public boolean waitForReferenceProcessing()
throws InterruptedException
@ -328,6 +329,11 @@ public abstract sealed class Reference<T>
public void runFinalization() {
Finalizer.runFinalization();
}
@Override
public <T> ReferenceQueue<T> newNativeReferenceQueue() {
return new NativeReferenceQueue<T>();
}
});
}

View file

@ -25,6 +25,9 @@
package java.lang.ref;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
import jdk.internal.misc.VM;
@ -38,13 +41,10 @@ import jdk.internal.misc.VM;
*/
public class ReferenceQueue<T> {
/**
* Constructs a new reference-object queue.
*/
public ReferenceQueue() { }
private static class Null extends ReferenceQueue<Object> {
public Null() { super(0); }
@Override
boolean enqueue(Reference<?> r) {
return false;
}
@ -53,37 +53,65 @@ public class ReferenceQueue<T> {
static final ReferenceQueue<Object> NULL = new Null();
static final ReferenceQueue<Object> ENQUEUED = new Null();
private static class Lock { };
private final Lock lock = new Lock();
private volatile Reference<? extends T> head;
private long queueLength = 0;
boolean enqueue(Reference<? extends T> r) { /* Called only by Reference class */
synchronized (lock) {
// Check that since getting the lock this reference hasn't already been
// enqueued (and even then removed)
ReferenceQueue<?> queue = r.queue;
if ((queue == NULL) || (queue == ENQUEUED)) {
return false;
}
assert queue == this;
// Self-loop end, so if a FinalReference it remains inactive.
r.next = (head == null) ? r : head;
head = r;
queueLength++;
// Update r.queue *after* adding to list, to avoid race
// with concurrent enqueued checks and fast-path poll().
// Volatiles ensure ordering.
r.queue = ENQUEUED;
if (r instanceof FinalReference) {
VM.addFinalRefCount(1);
}
lock.notifyAll();
return true;
}
private final ReentrantLock lock;
private final Condition notEmpty;
void signal() {
notEmpty.signalAll();
}
private Reference<? extends T> reallyPoll() { /* Must hold lock */
void await() throws InterruptedException {
notEmpty.await();
}
void await(long timeoutMillis) throws InterruptedException {
notEmpty.await(timeoutMillis, TimeUnit.MILLISECONDS);
}
/**
* Constructs a new reference-object queue.
*/
public ReferenceQueue() {
this.lock = new ReentrantLock();
this.notEmpty = lock.newCondition();
}
ReferenceQueue(int dummy) {
this.lock = null;
this.notEmpty = null;
}
final boolean enqueue0(Reference<? extends T> r) { // must hold lock
// Check that since getting the lock this reference hasn't already been
// enqueued (and even then removed)
ReferenceQueue<?> queue = r.queue;
if ((queue == NULL) || (queue == ENQUEUED)) {
return false;
}
assert queue == this;
// Self-loop end, so if a FinalReference it remains inactive.
r.next = (head == null) ? r : head;
head = r;
queueLength++;
// Update r.queue *after* adding to list, to avoid race
// with concurrent enqueued checks and fast-path poll().
// Volatiles ensure ordering.
r.queue = ENQUEUED;
if (r instanceof FinalReference) {
VM.addFinalRefCount(1);
}
signal();
return true;
}
final boolean headIsNull() {
return head == null;
}
final Reference<? extends T> poll0() { // must hold lock
Reference<? extends T> r = head;
if (r != null) {
r.queue = NULL;
@ -106,6 +134,40 @@ public class ReferenceQueue<T> {
return null;
}
final Reference<? extends T> remove0(long timeout)
throws IllegalArgumentException, InterruptedException { // must hold lock
Reference<? extends T> r = poll0();
if (r != null) return r;
long start = System.nanoTime();
for (;;) {
await(timeout);
r = poll0();
if (r != null) return r;
long end = System.nanoTime();
timeout -= (end - start) / 1000_000;
if (timeout <= 0) return null;
start = end;
}
}
final Reference<? extends T> remove0() throws InterruptedException { // must hold lock
for (;;) {
var r = poll0();
if (r != null) return r;
await();
}
}
boolean enqueue(Reference<? extends T> r) { /* Called only by Reference class */
lock.lock();
try {
return enqueue0(r);
} finally {
lock.unlock();
}
}
/**
* Polls this queue to see if a reference object is available. If one is
* available without further delay then it is removed from the queue and
@ -115,10 +177,13 @@ public class ReferenceQueue<T> {
* otherwise {@code null}
*/
public Reference<? extends T> poll() {
if (head == null)
if (headIsNull())
return null;
synchronized (lock) {
return reallyPoll();
lock.lock();
try {
return poll0();
} finally {
lock.unlock();
}
}
@ -142,27 +207,17 @@ public class ReferenceQueue<T> {
* @throws InterruptedException
* If the timeout wait is interrupted
*/
public Reference<? extends T> remove(long timeout)
throws IllegalArgumentException, InterruptedException
{
if (timeout < 0) {
public Reference<? extends T> remove(long timeout) throws InterruptedException {
if (timeout < 0)
throw new IllegalArgumentException("Negative timeout value");
}
synchronized (lock) {
Reference<? extends T> r = reallyPoll();
if (r != null) return r;
long start = (timeout == 0) ? 0 : System.nanoTime();
for (;;) {
lock.wait(timeout);
r = reallyPoll();
if (r != null) return r;
if (timeout != 0) {
long end = System.nanoTime();
timeout -= (end - start) / 1000_000;
if (timeout <= 0) return null;
start = end;
}
}
if (timeout == 0)
return remove();
lock.lock();
try {
return remove0(timeout);
} finally {
lock.unlock();
}
}
@ -174,7 +229,12 @@ public class ReferenceQueue<T> {
* @throws InterruptedException If the wait is interrupted
*/
public Reference<? extends T> remove() throws InterruptedException {
return remove(0);
lock.lock();
try {
return remove0();
} finally {
lock.unlock();
}
}
/**