mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 07:14:30 +02:00
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:
parent
5212535a27
commit
9583e3657e
1133 changed files with 95935 additions and 8335 deletions
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
132
src/java.base/share/classes/java/lang/PinnedThreadPrinter.java
Normal file
132
src/java.base/share/classes/java/lang/PinnedThreadPrinter.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
359
src/java.base/share/classes/java/lang/ThreadBuilders.java
Normal file
359
src/java.base/share/classes/java/lang/ThreadBuilders.java
Normal 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
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
1105
src/java.base/share/classes/java/lang/VirtualThread.java
Normal file
1105
src/java.base/share/classes/java/lang/VirtualThread.java
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue