mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-26 14:24:46 +02:00
8338383: Implement JEP 491: Synchronize Virtual Threads without Pinning
Co-authored-by: Patricio Chilano Mateo <pchilanomate@openjdk.org> Co-authored-by: Alan Bateman <alanb@openjdk.org> Co-authored-by: Andrew Haley <aph@openjdk.org> Co-authored-by: Fei Yang <fyang@openjdk.org> Co-authored-by: Coleen Phillimore <coleenp@openjdk.org> Co-authored-by: Richard Reingruber <rrich@openjdk.org> Co-authored-by: Martin Doerr <mdoerr@openjdk.org> Reviewed-by: aboldtch, dholmes, coleenp, fbredberg, dlong, sspitsyn
This commit is contained in:
parent
8a2a75e56d
commit
78b80150e0
246 changed files with 8295 additions and 2755 deletions
|
@ -159,16 +159,8 @@ public class ByteArrayOutputStream extends OutputStream {
|
|||
* @throws NullPointerException if {@code out} is {@code null}.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public void writeTo(OutputStream out) throws IOException {
|
||||
if (Thread.currentThread().isVirtual()) {
|
||||
byte[] bytes;
|
||||
synchronized (this) {
|
||||
bytes = Arrays.copyOf(buf, count);
|
||||
}
|
||||
out.write(bytes);
|
||||
} else synchronized (this) {
|
||||
out.write(buf, 0, count);
|
||||
}
|
||||
public synchronized void writeTo(OutputStream out) throws IOException {
|
||||
out.write(buf, 0, count);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
|
||||
package java.lang;
|
||||
|
||||
import jdk.internal.misc.Blocker;
|
||||
import jdk.internal.vm.annotation.IntrinsicCandidate;
|
||||
|
||||
/**
|
||||
|
@ -374,21 +373,20 @@ public class Object {
|
|||
* @see #wait(long, int)
|
||||
*/
|
||||
public final void wait(long timeoutMillis) throws InterruptedException {
|
||||
if (!Thread.currentThread().isVirtual()) {
|
||||
wait0(timeoutMillis);
|
||||
return;
|
||||
if (timeoutMillis < 0) {
|
||||
throw new IllegalArgumentException("timeout value is negative");
|
||||
}
|
||||
|
||||
// virtual thread waiting
|
||||
boolean attempted = Blocker.begin();
|
||||
try {
|
||||
if (Thread.currentThread() instanceof VirtualThread vthread) {
|
||||
try {
|
||||
wait0(timeoutMillis);
|
||||
} catch (InterruptedException e) {
|
||||
// virtual thread's interrupt status needs to be cleared
|
||||
vthread.getAndClearInterrupt();
|
||||
throw e;
|
||||
}
|
||||
} else {
|
||||
wait0(timeoutMillis);
|
||||
} catch (InterruptedException e) {
|
||||
// virtual thread's interrupt status needs to be cleared
|
||||
Thread.currentThread().getAndClearInterrupt();
|
||||
throw e;
|
||||
} finally {
|
||||
Blocker.end(attempted);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,151 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 2023, 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.*;
|
||||
import jdk.internal.access.JavaIOPrintStreamAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.InternalLock;
|
||||
import jdk.internal.vm.Continuation;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
private static final JavaIOPrintStreamAccess JIOPSA = SharedSecrets.getJavaIOPrintStreamAccess();
|
||||
private 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the frame is native, a class initializer, or holds monitors.
|
||||
*/
|
||||
private static boolean isInterestingFrame(LiveStackFrame f) {
|
||||
return f.isNativeMethod()
|
||||
|| "<clinit>".equals(f.getMethodName())
|
||||
|| (f.getMonitors().length > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the current thread's stack trace.
|
||||
*
|
||||
* @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, Continuation.Pinned reason, boolean printAll) {
|
||||
List<LiveStackFrame> stack = STACK_WALKER.walk(s ->
|
||||
s.map(f -> (LiveStackFrame) f)
|
||||
.filter(f -> f.getDeclaringClass() != PinnedThreadPrinter.class)
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
Object lockObj = JIOPSA.lock(out);
|
||||
if (lockObj instanceof InternalLock lock && lock.tryLock()) {
|
||||
try {
|
||||
// find the closest frame that is causing the thread to be pinned
|
||||
stack.stream()
|
||||
.filter(f -> isInterestingFrame(f))
|
||||
.map(LiveStackFrame::getDeclaringClass)
|
||||
.findFirst()
|
||||
.ifPresentOrElse(klass -> {
|
||||
// print the stack trace if not already seen
|
||||
int hash = hash(stack);
|
||||
if (HASHES.get(klass).add(hash)) {
|
||||
printStackTrace(out, reason, stack, printAll);
|
||||
}
|
||||
}, () -> printStackTrace(out, reason, stack, true)); // not found
|
||||
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void printStackTrace(PrintStream out,
|
||||
Continuation.Pinned reason,
|
||||
List<LiveStackFrame> stack,
|
||||
boolean printAll) {
|
||||
out.format("%s reason:%s%n", Thread.currentThread(), reason);
|
||||
for (LiveStackFrame frame : stack) {
|
||||
var ste = frame.toStackTraceElement();
|
||||
int monitorCount = frame.getMonitors().length;
|
||||
if (monitorCount > 0) {
|
||||
out.format(" %s <== monitors:%d%n", ste, monitorCount);
|
||||
} else if (printAll || isInterestingFrame(frame)) {
|
||||
out.format(" %s%n", ste);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -636,12 +636,17 @@ public class Thread implements Runnable {
|
|||
*/
|
||||
static final int NO_INHERIT_THREAD_LOCALS = 1 << 2;
|
||||
|
||||
/**
|
||||
* Thread identifier assigned to the primordial thread.
|
||||
*/
|
||||
static final long PRIMORDIAL_TID = 3;
|
||||
|
||||
/**
|
||||
* Helper class to generate thread identifiers. The identifiers start at
|
||||
* 2 as this class cannot be used during early startup to generate the
|
||||
* identifier for the primordial thread. The counter is off-heap and
|
||||
* shared with the VM to allow it assign thread identifiers to non-Java
|
||||
* threads.
|
||||
* {@link Thread#PRIMORDIAL_TID} +1 as this class cannot be used during
|
||||
* early startup to generate the identifier for the primordial thread. The
|
||||
* counter is off-heap and shared with the VM to allow it to assign thread
|
||||
* identifiers to non-Java threads.
|
||||
* See Thread initialization.
|
||||
*/
|
||||
private static class ThreadIdentifiers {
|
||||
|
@ -722,10 +727,11 @@ public class Thread implements Runnable {
|
|||
}
|
||||
|
||||
if (attached && VM.initLevel() < 1) {
|
||||
this.tid = 1; // primordial thread
|
||||
this.tid = PRIMORDIAL_TID; // primordial thread
|
||||
} else {
|
||||
this.tid = ThreadIdentifiers.next();
|
||||
}
|
||||
|
||||
this.name = (name != null) ? name : genThreadName();
|
||||
|
||||
if (acc != null) {
|
||||
|
|
|
@ -41,7 +41,6 @@ import java.util.concurrent.ScheduledExecutorService;
|
|||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import jdk.internal.event.VirtualThreadEndEvent;
|
||||
import jdk.internal.event.VirtualThreadPinnedEvent;
|
||||
import jdk.internal.event.VirtualThreadStartEvent;
|
||||
import jdk.internal.event.VirtualThreadSubmitFailedEvent;
|
||||
import jdk.internal.misc.CarrierThread;
|
||||
|
@ -70,12 +69,12 @@ final class VirtualThread extends BaseVirtualThread {
|
|||
private static final ContinuationScope VTHREAD_SCOPE = new ContinuationScope("VirtualThreads");
|
||||
private static final ForkJoinPool DEFAULT_SCHEDULER = createDefaultScheduler();
|
||||
private static final ScheduledExecutorService[] DELAYED_TASK_SCHEDULERS = createDelayedTaskSchedulers();
|
||||
private static final int TRACE_PINNING_MODE = tracePinningMode();
|
||||
|
||||
private static final long STATE = U.objectFieldOffset(VirtualThread.class, "state");
|
||||
private static final long PARK_PERMIT = U.objectFieldOffset(VirtualThread.class, "parkPermit");
|
||||
private static final long CARRIER_THREAD = U.objectFieldOffset(VirtualThread.class, "carrierThread");
|
||||
private static final long TERMINATION = U.objectFieldOffset(VirtualThread.class, "termination");
|
||||
private static final long ON_WAITING_LIST = U.objectFieldOffset(VirtualThread.class, "onWaitingList");
|
||||
|
||||
// scheduler and continuation
|
||||
private final Executor scheduler;
|
||||
|
@ -106,6 +105,21 @@ final class VirtualThread extends BaseVirtualThread {
|
|||
* TIMED_PARKED -> UNPARKED // unparked, may be scheduled to continue
|
||||
* TIMED_PINNED -> RUNNING // unparked, continue execution on same carrier
|
||||
*
|
||||
* RUNNING -> BLOCKING // blocking on monitor enter
|
||||
* BLOCKING -> BLOCKED // blocked on monitor enter
|
||||
* BLOCKED -> UNBLOCKED // unblocked, may be scheduled to continue
|
||||
* UNBLOCKED -> RUNNING // continue execution after blocked on monitor enter
|
||||
*
|
||||
* RUNNING -> WAITING // transitional state during wait on monitor
|
||||
* WAITING -> WAITED // waiting on monitor
|
||||
* WAITED -> BLOCKED // notified, waiting to be unblocked by monitor owner
|
||||
* WAITED -> UNBLOCKED // timed-out/interrupted
|
||||
*
|
||||
* RUNNING -> TIMED_WAITING // transition state during timed-waiting on monitor
|
||||
* TIMED_WAITING -> TIMED_WAITED // timed-waiting on monitor
|
||||
* TIMED_WAITED -> BLOCKED // notified, waiting to be unblocked by monitor owner
|
||||
* TIMED_WAITED -> UNBLOCKED // timed-out/interrupted
|
||||
*
|
||||
* RUNNING -> YIELDING // Thread.yield
|
||||
* YIELDING -> YIELDED // cont.yield successful, may be scheduled to continue
|
||||
* YIELDING -> RUNNING // cont.yield failed
|
||||
|
@ -128,18 +142,44 @@ final class VirtualThread extends BaseVirtualThread {
|
|||
private static final int YIELDING = 10;
|
||||
private static final int YIELDED = 11; // unmounted but runnable
|
||||
|
||||
// monitor enter
|
||||
private static final int BLOCKING = 12;
|
||||
private static final int BLOCKED = 13; // unmounted
|
||||
private static final int UNBLOCKED = 14; // unmounted but runnable
|
||||
|
||||
// monitor wait/timed-wait
|
||||
private static final int WAITING = 15;
|
||||
private static final int WAIT = 16; // waiting in Object.wait
|
||||
private static final int TIMED_WAITING = 17;
|
||||
private static final int TIMED_WAIT = 18; // waiting in timed-Object.wait
|
||||
|
||||
private static final int TERMINATED = 99; // final state
|
||||
|
||||
// can be suspended from scheduling when unmounted
|
||||
private static final int SUSPENDED = 1 << 8;
|
||||
|
||||
// parking permit
|
||||
// parking permit made available by LockSupport.unpark
|
||||
private volatile boolean parkPermit;
|
||||
|
||||
// timeout for timed-park, in nanoseconds, only accessed on current/carrier thread
|
||||
private long parkTimeout;
|
||||
// blocking permit made available by unblocker thread when another thread exits monitor
|
||||
private volatile boolean blockPermit;
|
||||
|
||||
// timer task for timed-park, only accessed on current/carrier thread
|
||||
// true when on the list of virtual threads waiting to be unblocked
|
||||
private volatile boolean onWaitingList;
|
||||
|
||||
// next virtual thread on the list of virtual threads waiting to be unblocked
|
||||
private volatile VirtualThread next;
|
||||
|
||||
// notified by Object.notify/notifyAll while waiting in Object.wait
|
||||
private volatile boolean notified;
|
||||
|
||||
// timed-wait support
|
||||
private byte timedWaitSeqNo;
|
||||
|
||||
// timeout for timed-park and timed-wait, only accessed on current/carrier thread
|
||||
private long timeout;
|
||||
|
||||
// timer task for timed-park and timed-wait, only accessed on current/carrier thread
|
||||
private Future<?> timeoutTask;
|
||||
|
||||
// carrier thread when mounted, accessed by VM
|
||||
|
@ -202,18 +242,6 @@ final class VirtualThread extends BaseVirtualThread {
|
|||
}
|
||||
@Override
|
||||
protected void onPinned(Continuation.Pinned reason) {
|
||||
if (TRACE_PINNING_MODE > 0) {
|
||||
boolean printAll = (TRACE_PINNING_MODE == 1);
|
||||
VirtualThread vthread = (VirtualThread) Thread.currentThread();
|
||||
int oldState = vthread.state();
|
||||
try {
|
||||
// avoid printing when in transition states
|
||||
vthread.setState(RUNNING);
|
||||
PinnedThreadPrinter.printStackTrace(System.out, reason, printAll);
|
||||
} finally {
|
||||
vthread.setState(oldState);
|
||||
}
|
||||
}
|
||||
}
|
||||
private static Runnable wrap(VirtualThread vthread, Runnable task) {
|
||||
return new Runnable() {
|
||||
|
@ -245,16 +273,20 @@ final class VirtualThread extends BaseVirtualThread {
|
|||
|
||||
// set state to RUNNING
|
||||
int initialState = state();
|
||||
if (initialState == STARTED || initialState == UNPARKED || initialState == YIELDED) {
|
||||
if (initialState == STARTED || initialState == UNPARKED
|
||||
|| initialState == UNBLOCKED || initialState == YIELDED) {
|
||||
// newly started or continue after parking/blocking/Thread.yield
|
||||
if (!compareAndSetState(initialState, RUNNING)) {
|
||||
return;
|
||||
}
|
||||
// consume permit when continuing after parking. If continuing after a
|
||||
// timed-park then the timeout task is cancelled.
|
||||
// consume permit when continuing after parking or blocking. If continue
|
||||
// after a timed-park or timed-wait then the timeout task is cancelled.
|
||||
if (initialState == UNPARKED) {
|
||||
cancelTimeoutTask();
|
||||
setParkPermit(false);
|
||||
} else if (initialState == UNBLOCKED) {
|
||||
cancelTimeoutTask();
|
||||
blockPermit = false;
|
||||
}
|
||||
} else {
|
||||
// not runnable
|
||||
|
@ -275,8 +307,8 @@ final class VirtualThread extends BaseVirtualThread {
|
|||
}
|
||||
|
||||
/**
|
||||
* Cancel timeout task when continuing after a timed-park. The
|
||||
* timeout task may be executing, or may have already completed.
|
||||
* Cancel timeout task when continuing after timed-park or timed-wait.
|
||||
* The timeout task may be executing, or may have already completed.
|
||||
*/
|
||||
private void cancelTimeoutTask() {
|
||||
if (timeoutTask != null) {
|
||||
|
@ -511,7 +543,7 @@ final class VirtualThread extends BaseVirtualThread {
|
|||
|
||||
/**
|
||||
* Invoked in the context of the carrier thread after the Continuation yields when
|
||||
* parking or Thread.yield.
|
||||
* parking, blocking on monitor enter, Object.wait, or Thread.yield.
|
||||
*/
|
||||
private void afterYield() {
|
||||
assert carrierThread == null;
|
||||
|
@ -530,8 +562,8 @@ final class VirtualThread extends BaseVirtualThread {
|
|||
setState(newState = PARKED);
|
||||
} else {
|
||||
// schedule unpark
|
||||
assert parkTimeout > 0;
|
||||
timeoutTask = schedule(this::unpark, parkTimeout, NANOSECONDS);
|
||||
assert timeout > 0;
|
||||
timeoutTask = schedule(this::unpark, timeout, NANOSECONDS);
|
||||
setState(newState = TIMED_PARKED);
|
||||
}
|
||||
|
||||
|
@ -556,6 +588,56 @@ final class VirtualThread extends BaseVirtualThread {
|
|||
return;
|
||||
}
|
||||
|
||||
// blocking on monitorenter
|
||||
if (s == BLOCKING) {
|
||||
setState(BLOCKED);
|
||||
|
||||
// may have been unblocked while blocking
|
||||
if (blockPermit && compareAndSetState(BLOCKED, UNBLOCKED)) {
|
||||
// lazy submit if local queue is empty
|
||||
lazySubmitRunContinuation();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Object.wait
|
||||
if (s == WAITING || s == TIMED_WAITING) {
|
||||
int newState;
|
||||
if (s == WAITING) {
|
||||
setState(newState = WAIT);
|
||||
} else {
|
||||
// For timed-wait, a timeout task is scheduled to execute. The timeout
|
||||
// task will change the thread state to UNBLOCKED and submit the thread
|
||||
// to the scheduler. A sequence number is used to ensure that the timeout
|
||||
// task only unblocks the thread for this timed-wait. We synchronize with
|
||||
// the timeout task to coordinate access to the sequence number and to
|
||||
// ensure the timeout task doesn't execute until the thread has got to
|
||||
// the TIMED_WAIT state.
|
||||
assert timeout > 0;
|
||||
synchronized (timedWaitLock()) {
|
||||
byte seqNo = ++timedWaitSeqNo;
|
||||
timeoutTask = schedule(() -> waitTimeoutExpired(seqNo), timeout, MILLISECONDS);
|
||||
setState(newState = TIMED_WAIT);
|
||||
}
|
||||
}
|
||||
|
||||
// may have been notified while in transition to wait state
|
||||
if (notified && compareAndSetState(newState, BLOCKED)) {
|
||||
// may have even been unblocked already
|
||||
if (blockPermit && compareAndSetState(BLOCKED, UNBLOCKED)) {
|
||||
submitRunContinuation();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// may have been interrupted while in transition to wait state
|
||||
if (interrupted && compareAndSetState(newState, UNBLOCKED)) {
|
||||
submitRunContinuation();
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
assert false;
|
||||
}
|
||||
|
||||
|
@ -695,7 +777,7 @@ final class VirtualThread extends BaseVirtualThread {
|
|||
|
||||
// park the thread, afterYield will schedule the thread to unpark
|
||||
boolean yielded = false;
|
||||
setParkTimeout(nanos);
|
||||
timeout = nanos;
|
||||
setState(TIMED_PARKING);
|
||||
try {
|
||||
yielded = yieldContinuation();
|
||||
|
@ -727,14 +809,6 @@ final class VirtualThread extends BaseVirtualThread {
|
|||
private void parkOnCarrierThread(boolean timed, long nanos) {
|
||||
assert state() == RUNNING;
|
||||
|
||||
VirtualThreadPinnedEvent event;
|
||||
try {
|
||||
event = new VirtualThreadPinnedEvent();
|
||||
event.begin();
|
||||
} catch (OutOfMemoryError e) {
|
||||
event = null;
|
||||
}
|
||||
|
||||
setState(timed ? TIMED_PINNED : PINNED);
|
||||
try {
|
||||
if (!parkPermit) {
|
||||
|
@ -751,15 +825,18 @@ final class VirtualThread extends BaseVirtualThread {
|
|||
// consume parking permit
|
||||
setParkPermit(false);
|
||||
|
||||
if (event != null) {
|
||||
try {
|
||||
event.commit();
|
||||
} catch (OutOfMemoryError e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
// JFR jdk.VirtualThreadPinned event
|
||||
postPinnedEvent("LockSupport.park");
|
||||
}
|
||||
|
||||
/**
|
||||
* Call into VM when pinned to record a JFR jdk.VirtualThreadPinned event.
|
||||
* Recording the event in the VM avoids having JFR event recorded in Java
|
||||
* with the same name, but different ID, to events recorded by the VM.
|
||||
*/
|
||||
@Hidden
|
||||
private static native void postPinnedEvent(String op);
|
||||
|
||||
/**
|
||||
* Re-enables this virtual thread for scheduling. If this virtual thread is parked
|
||||
* then its task is scheduled to continue, otherwise its next call to {@code park} or
|
||||
|
@ -796,6 +873,49 @@ final class VirtualThread extends BaseVirtualThread {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked by unblocker thread to unblock this virtual thread.
|
||||
*/
|
||||
private void unblock() {
|
||||
assert !Thread.currentThread().isVirtual();
|
||||
blockPermit = true;
|
||||
if (state() == BLOCKED && compareAndSetState(BLOCKED, UNBLOCKED)) {
|
||||
submitRunContinuation();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked by timer thread when wait timeout for virtual thread has expired.
|
||||
* If the virtual thread is in timed-wait then this method will unblock the thread
|
||||
* and submit its task so that it continues and attempts to reenter the monitor.
|
||||
* This method does nothing if the thread has been woken by notify or interrupt.
|
||||
*/
|
||||
private void waitTimeoutExpired(byte seqNo) {
|
||||
assert !Thread.currentThread().isVirtual();
|
||||
for (;;) {
|
||||
boolean unblocked = false;
|
||||
synchronized (timedWaitLock()) {
|
||||
if (seqNo != timedWaitSeqNo) {
|
||||
// this timeout task is for a past timed-wait
|
||||
return;
|
||||
}
|
||||
int s = state();
|
||||
if (s == TIMED_WAIT) {
|
||||
unblocked = compareAndSetState(TIMED_WAIT, UNBLOCKED);
|
||||
} else if (s != (TIMED_WAIT | SUSPENDED)) {
|
||||
// notified or interrupted, no longer waiting
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (unblocked) {
|
||||
submitRunContinuation();
|
||||
return;
|
||||
}
|
||||
// need to retry when thread is suspended in time-wait
|
||||
Thread.yield();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to yield the current virtual thread (Thread.yield).
|
||||
*/
|
||||
|
@ -926,6 +1046,12 @@ final class VirtualThread extends BaseVirtualThread {
|
|||
// make available parking permit, unpark thread if parked
|
||||
unpark();
|
||||
|
||||
// if thread is waiting in Object.wait then schedule to try to reenter
|
||||
int s = state();
|
||||
if ((s == WAIT || s == TIMED_WAIT) && compareAndSetState(s, UNBLOCKED)) {
|
||||
submitRunContinuation();
|
||||
}
|
||||
|
||||
} else {
|
||||
interrupted = true;
|
||||
carrierThread.setInterrupt();
|
||||
|
@ -970,6 +1096,7 @@ final class VirtualThread extends BaseVirtualThread {
|
|||
return Thread.State.RUNNABLE;
|
||||
}
|
||||
case UNPARKED:
|
||||
case UNBLOCKED:
|
||||
case YIELDED:
|
||||
// runnable, not mounted
|
||||
return Thread.State.RUNNABLE;
|
||||
|
@ -992,15 +1119,22 @@ final class VirtualThread extends BaseVirtualThread {
|
|||
return Thread.State.RUNNABLE;
|
||||
case PARKING:
|
||||
case TIMED_PARKING:
|
||||
case WAITING:
|
||||
case TIMED_WAITING:
|
||||
case YIELDING:
|
||||
// runnable, in transition
|
||||
return Thread.State.RUNNABLE;
|
||||
case PARKED:
|
||||
case PINNED:
|
||||
case WAIT:
|
||||
return Thread.State.WAITING;
|
||||
case TIMED_PARKED:
|
||||
case TIMED_PINNED:
|
||||
case TIMED_WAIT:
|
||||
return Thread.State.TIMED_WAITING;
|
||||
case BLOCKING:
|
||||
case BLOCKED:
|
||||
return Thread.State.BLOCKED;
|
||||
case TERMINATED:
|
||||
return Thread.State.TERMINATED;
|
||||
default:
|
||||
|
@ -1046,13 +1180,13 @@ final class VirtualThread extends BaseVirtualThread {
|
|||
case RUNNING, PINNED, TIMED_PINNED -> {
|
||||
return null; // mounted
|
||||
}
|
||||
case PARKED, TIMED_PARKED -> {
|
||||
case PARKED, TIMED_PARKED, BLOCKED, WAIT, TIMED_WAIT -> {
|
||||
// unmounted, not runnable
|
||||
}
|
||||
case UNPARKED, YIELDED -> {
|
||||
case UNPARKED, UNBLOCKED, YIELDED -> {
|
||||
// unmounted, runnable
|
||||
}
|
||||
case PARKING, TIMED_PARKING, YIELDING -> {
|
||||
case PARKING, TIMED_PARKING, BLOCKING, YIELDING, WAITING, TIMED_WAITING -> {
|
||||
return null; // in transition
|
||||
}
|
||||
default -> throw new InternalError("" + initialState);
|
||||
|
@ -1073,7 +1207,7 @@ final class VirtualThread extends BaseVirtualThread {
|
|||
setState(initialState);
|
||||
}
|
||||
boolean resubmit = switch (initialState) {
|
||||
case UNPARKED, YIELDED -> {
|
||||
case UNPARKED, UNBLOCKED, YIELDED -> {
|
||||
// resubmit as task may have run while suspended
|
||||
yield true;
|
||||
}
|
||||
|
@ -1081,6 +1215,15 @@ final class VirtualThread extends BaseVirtualThread {
|
|||
// resubmit if unparked while suspended
|
||||
yield parkPermit && compareAndSetState(initialState, UNPARKED);
|
||||
}
|
||||
case BLOCKED -> {
|
||||
// resubmit if unblocked while suspended
|
||||
yield blockPermit && compareAndSetState(BLOCKED, UNBLOCKED);
|
||||
}
|
||||
case WAIT, TIMED_WAIT -> {
|
||||
// resubmit if notified or interrupted while waiting (Object.wait)
|
||||
// waitTimeoutExpired will retry if the timed expired when suspended
|
||||
yield (notified || interrupted) && compareAndSetState(initialState, UNBLOCKED);
|
||||
}
|
||||
default -> throw new InternalError();
|
||||
};
|
||||
if (resubmit) {
|
||||
|
@ -1175,6 +1318,14 @@ final class VirtualThread extends BaseVirtualThread {
|
|||
return interruptLock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a lock object for coordinating timed-wait setup and timeout handling.
|
||||
*/
|
||||
private Object timedWaitLock() {
|
||||
// use this object for now to avoid the overhead of introducing another lock
|
||||
return runContinuation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disallow the current thread be suspended or preempted.
|
||||
*/
|
||||
|
@ -1205,6 +1356,10 @@ final class VirtualThread extends BaseVirtualThread {
|
|||
return U.compareAndSetInt(this, STATE, expectedValue, newValue);
|
||||
}
|
||||
|
||||
private boolean compareAndSetOnWaitingList(boolean expectedValue, boolean newValue) {
|
||||
return U.compareAndSetBoolean(this, ON_WAITING_LIST, expectedValue, newValue);
|
||||
}
|
||||
|
||||
private void setParkPermit(boolean newValue) {
|
||||
if (parkPermit != newValue) {
|
||||
parkPermit = newValue;
|
||||
|
@ -1219,10 +1374,6 @@ final class VirtualThread extends BaseVirtualThread {
|
|||
}
|
||||
}
|
||||
|
||||
private void setParkTimeout(long timeout) {
|
||||
parkTimeout = timeout;
|
||||
}
|
||||
|
||||
private void setCarrierThread(Thread carrier) {
|
||||
// U.putReferenceRelease(this, CARRIER_THREAD, carrier);
|
||||
this.carrierThread = carrier;
|
||||
|
@ -1255,9 +1406,6 @@ final class VirtualThread extends BaseVirtualThread {
|
|||
|
||||
// ensure VTHREAD_GROUP is created, may be accessed by JVMTI
|
||||
var group = Thread.virtualThreadGroup();
|
||||
|
||||
// ensure VirtualThreadPinnedEvent is loaded/initialized
|
||||
U.ensureClassInitialized(VirtualThreadPinnedEvent.class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1338,18 +1486,37 @@ final class VirtualThread extends BaseVirtualThread {
|
|||
}
|
||||
|
||||
/**
|
||||
* Reads the value of the jdk.tracePinnedThreads property to determine if stack
|
||||
* traces should be printed when a carrier thread is pinned when a virtual thread
|
||||
* attempts to park.
|
||||
* Schedule virtual threads that are ready to be scheduled after they blocked on
|
||||
* monitor enter.
|
||||
*/
|
||||
private static int tracePinningMode() {
|
||||
String propValue = GetPropertyAction.privilegedGetProperty("jdk.tracePinnedThreads");
|
||||
if (propValue != null) {
|
||||
if (propValue.length() == 0 || "full".equalsIgnoreCase(propValue))
|
||||
return 1;
|
||||
if ("short".equalsIgnoreCase(propValue))
|
||||
return 2;
|
||||
private static void unblockVirtualThreads() {
|
||||
while (true) {
|
||||
VirtualThread vthread = takeVirtualThreadListToUnblock();
|
||||
while (vthread != null) {
|
||||
assert vthread.onWaitingList;
|
||||
VirtualThread nextThread = vthread.next;
|
||||
|
||||
// remove from list and unblock
|
||||
vthread.next = null;
|
||||
boolean changed = vthread.compareAndSetOnWaitingList(true, false);
|
||||
assert changed;
|
||||
vthread.unblock();
|
||||
|
||||
vthread = nextThread;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the list of virtual threads that are waiting to be unblocked, waiting
|
||||
* if necessary until a list of one or more threads becomes available.
|
||||
*/
|
||||
private static native VirtualThread takeVirtualThreadListToUnblock();
|
||||
|
||||
static {
|
||||
var unblocker = InnocuousThread.newThread("VirtualThread-unblocker",
|
||||
VirtualThread::unblockVirtualThreads);
|
||||
unblocker.setDaemon(true);
|
||||
unblocker.start();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2008, 2024, 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
|
||||
|
@ -231,7 +231,7 @@ class MethodType
|
|||
}
|
||||
|
||||
static final ReferencedKeySet<MethodType> internTable =
|
||||
ReferencedKeySet.create(false, true, new Supplier<>() {
|
||||
ReferencedKeySet.create(false, new Supplier<>() {
|
||||
@Override
|
||||
public Map<ReferenceKey<MethodType>, ReferenceKey<MethodType>> get() {
|
||||
return new ConcurrentHashMap<>(512);
|
||||
|
|
|
@ -1134,7 +1134,7 @@ public final class StringConcatFactory {
|
|||
};
|
||||
|
||||
static final ReferencedKeyMap<MethodType, SoftReference<MethodHandlePair>> CACHE =
|
||||
ReferencedKeyMap.create(true, true,
|
||||
ReferencedKeyMap.create(true,
|
||||
new Supplier<>() {
|
||||
@Override
|
||||
public Map<ReferenceKey<MethodType>, SoftReference<MethodHandlePair>> get() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2024, 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 NativeReferenceQueue<>();
|
||||
private static ReferenceQueue<Object> queue = new ReferenceQueue<>();
|
||||
|
||||
/** Head of doubly linked list of Finalizers awaiting finalization. */
|
||||
private static Finalizer unfinalized = null;
|
||||
|
|
|
@ -1,92 +0,0 @@
|
|||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -330,11 +330,6 @@ public abstract sealed class Reference<T>
|
|||
public void runFinalization() {
|
||||
Finalizer.runFinalization();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ReferenceQueue<T> newNativeReferenceQueue() {
|
||||
return new NativeReferenceQueue<T>();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2024, 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,11 +25,10 @@
|
|||
|
||||
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;
|
||||
import jdk.internal.vm.Continuation;
|
||||
import jdk.internal.vm.ContinuationSupport;
|
||||
|
||||
/**
|
||||
* Reference queues, to which registered reference objects are appended by the
|
||||
|
@ -50,8 +49,6 @@ import jdk.internal.misc.VM;
|
|||
|
||||
public class ReferenceQueue<T> {
|
||||
private static class Null extends ReferenceQueue<Object> {
|
||||
public Null() { super(0); }
|
||||
|
||||
@Override
|
||||
boolean enqueue(Reference<?> r) {
|
||||
return false;
|
||||
|
@ -64,35 +61,16 @@ public class ReferenceQueue<T> {
|
|||
private volatile Reference<? extends T> head;
|
||||
private long queueLength = 0;
|
||||
|
||||
private final ReentrantLock lock;
|
||||
private final Condition notEmpty;
|
||||
|
||||
void signal() {
|
||||
notEmpty.signalAll();
|
||||
}
|
||||
|
||||
void await() throws InterruptedException {
|
||||
notEmpty.await();
|
||||
}
|
||||
|
||||
void await(long timeoutMillis) throws InterruptedException {
|
||||
notEmpty.await(timeoutMillis, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
private static class Lock { };
|
||||
private final Lock lock = new Lock();
|
||||
|
||||
/**
|
||||
* 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
|
||||
private 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;
|
||||
|
@ -111,15 +89,11 @@ public class ReferenceQueue<T> {
|
|||
if (r instanceof FinalReference) {
|
||||
VM.addFinalRefCount(1);
|
||||
}
|
||||
signal();
|
||||
lock.notifyAll();
|
||||
return true;
|
||||
}
|
||||
|
||||
final boolean headIsNull() {
|
||||
return head == null;
|
||||
}
|
||||
|
||||
final Reference<? extends T> poll0() { // must hold lock
|
||||
private Reference<? extends T> poll0() { // must hold lock
|
||||
Reference<? extends T> r = head;
|
||||
if (r != null) {
|
||||
r.queue = NULL;
|
||||
|
@ -142,16 +116,14 @@ public class ReferenceQueue<T> {
|
|||
return null;
|
||||
}
|
||||
|
||||
final Reference<? extends T> remove0(long timeout)
|
||||
throws IllegalArgumentException, InterruptedException { // must hold lock
|
||||
private Reference<? extends T> remove0(long timeout) throws InterruptedException { // must hold lock
|
||||
Reference<? extends T> r = poll0();
|
||||
if (r != null) return r;
|
||||
long start = System.nanoTime();
|
||||
for (;;) {
|
||||
await(timeout);
|
||||
lock.wait(timeout);
|
||||
r = poll0();
|
||||
if (r != null) return r;
|
||||
|
||||
long end = System.nanoTime();
|
||||
timeout -= (end - start) / 1000_000;
|
||||
if (timeout <= 0) return null;
|
||||
|
@ -159,23 +131,33 @@ public class ReferenceQueue<T> {
|
|||
}
|
||||
}
|
||||
|
||||
final Reference<? extends T> remove0() throws InterruptedException { // must hold lock
|
||||
private Reference<? extends T> remove0() throws InterruptedException { // must hold lock
|
||||
for (;;) {
|
||||
var r = poll0();
|
||||
if (r != null) return r;
|
||||
await();
|
||||
lock.wait();
|
||||
}
|
||||
}
|
||||
|
||||
boolean enqueue(Reference<? extends T> r) { /* Called only by Reference class */
|
||||
lock.lock();
|
||||
try {
|
||||
synchronized (lock) {
|
||||
return enqueue0(r);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean tryDisablePreempt() {
|
||||
if (Thread.currentThread().isVirtual() && ContinuationSupport.isSupported()) {
|
||||
Continuation.pin();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void enablePreempt() {
|
||||
Continuation.unpin();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -186,13 +168,18 @@ public class ReferenceQueue<T> {
|
|||
* @see java.lang.ref.Reference#enqueue()
|
||||
*/
|
||||
public Reference<? extends T> poll() {
|
||||
if (headIsNull())
|
||||
if (head == null)
|
||||
return null;
|
||||
lock.lock();
|
||||
|
||||
// Prevent a virtual thread from being preempted as this could potentially
|
||||
// deadlock with a carrier that is polling the same reference queue.
|
||||
boolean disabled = tryDisablePreempt();
|
||||
try {
|
||||
return poll0();
|
||||
synchronized (lock) {
|
||||
return poll0();
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
if (disabled) enablePreempt();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -224,11 +211,8 @@ public class ReferenceQueue<T> {
|
|||
if (timeout == 0)
|
||||
return remove();
|
||||
|
||||
lock.lock();
|
||||
try {
|
||||
synchronized (lock) {
|
||||
return remove0(timeout);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,11 +225,8 @@ public class ReferenceQueue<T> {
|
|||
* @see java.lang.ref.Reference#enqueue()
|
||||
*/
|
||||
public Reference<? extends T> remove() throws InterruptedException {
|
||||
lock.lock();
|
||||
try {
|
||||
synchronized (lock) {
|
||||
return remove0();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2024, 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
|
||||
|
@ -50,11 +50,4 @@ public interface JavaLangRefAccess {
|
|||
* Invoked by Runtime.runFinalization()
|
||||
*/
|
||||
void runFinalization();
|
||||
|
||||
/**
|
||||
* Constructs a new NativeReferenceQueue.
|
||||
*
|
||||
* Invoked by jdk.internal.util.ReferencedKeyMap
|
||||
*/
|
||||
<T> ReferenceQueue<T> newNativeReferenceQueue();
|
||||
}
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* 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 jdk.internal.event;
|
||||
|
||||
/**
|
||||
* Event recording that a virtual thread has parked on its carrier thread.
|
||||
*/
|
||||
public class VirtualThreadPinnedEvent extends Event {
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 2024, 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
|
||||
|
@ -39,10 +39,10 @@ public class InternalLock {
|
|||
private static final boolean CAN_USE_INTERNAL_LOCK;
|
||||
static {
|
||||
String s = System.getProperty("jdk.io.useMonitors");
|
||||
if (s != null && (s.isEmpty() || s.equals("true"))) {
|
||||
CAN_USE_INTERNAL_LOCK = false;
|
||||
} else {
|
||||
if (s != null && s.equals("false")) {
|
||||
CAN_USE_INTERNAL_LOCK = true;
|
||||
} else {
|
||||
CAN_USE_INTERNAL_LOCK = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -142,29 +142,7 @@ public final class ReferencedKeyMap<K, V> implements Map<K, V> {
|
|||
*/
|
||||
public static <K, V> ReferencedKeyMap<K, V>
|
||||
create(boolean isSoft, Supplier<Map<ReferenceKey<K>, V>> supplier) {
|
||||
return create(isSoft, false, supplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ReferencedKeyMap} map.
|
||||
*
|
||||
* @param isSoft true if {@link SoftReference} keys are to
|
||||
* be used, {@link WeakReference} otherwise.
|
||||
* @param useNativeQueue true if uses NativeReferenceQueue
|
||||
* otherwise use {@link ReferenceQueue}.
|
||||
* @param supplier {@link Supplier} of the backing map
|
||||
*
|
||||
* @return a new map with {@link Reference} keys
|
||||
*
|
||||
* @param <K> the type of keys maintained by the new map
|
||||
* @param <V> the type of mapped values
|
||||
*/
|
||||
public static <K, V> ReferencedKeyMap<K, V>
|
||||
create(boolean isSoft, boolean useNativeQueue, Supplier<Map<ReferenceKey<K>, V>> supplier) {
|
||||
return new ReferencedKeyMap<K, V>(isSoft, supplier.get(),
|
||||
useNativeQueue ? SharedSecrets.getJavaLangRefAccess().newNativeReferenceQueue()
|
||||
: new ReferenceQueue<>()
|
||||
);
|
||||
return new ReferencedKeyMap<K, V>(isSoft, supplier.get(), new ReferenceQueue<>());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, 2024, 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
|
||||
|
@ -106,25 +106,7 @@ public final class ReferencedKeySet<T> extends AbstractSet<T> {
|
|||
*/
|
||||
public static <E> ReferencedKeySet<E>
|
||||
create(boolean isSoft, Supplier<Map<ReferenceKey<E>, ReferenceKey<E>>> supplier) {
|
||||
return create(isSoft, false, supplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ReferencedKeySet} elements.
|
||||
*
|
||||
* @param isSoft true if {@link SoftReference} elements are to
|
||||
* be used, {@link WeakReference} otherwise.
|
||||
* @param useNativeQueue true if uses NativeReferenceQueue
|
||||
* otherwise use {@link ReferenceQueue}.
|
||||
* @param supplier {@link Supplier} of the backing map
|
||||
*
|
||||
* @return a new set with {@link Reference} elements
|
||||
*
|
||||
* @param <E> the type of elements maintained by this set
|
||||
*/
|
||||
public static <E> ReferencedKeySet<E>
|
||||
create(boolean isSoft, boolean useNativeQueue, Supplier<Map<ReferenceKey<E>, ReferenceKey<E>>> supplier) {
|
||||
return new ReferencedKeySet<>(ReferencedKeyMap.create(isSoft, useNativeQueue, supplier));
|
||||
return new ReferencedKeySet<>(ReferencedKeyMap.create(isSoft, supplier));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -59,7 +59,9 @@ public class Continuation {
|
|||
public enum Pinned {
|
||||
/** Native frame on stack */ NATIVE,
|
||||
/** Monitor held */ MONITOR,
|
||||
/** In critical section */ CRITICAL_SECTION }
|
||||
/** In critical section */ CRITICAL_SECTION,
|
||||
/** Exception (OOME/SOE) */ EXCEPTION
|
||||
}
|
||||
|
||||
/** Preemption attempt result */
|
||||
public enum PreemptStatus {
|
||||
|
@ -85,6 +87,7 @@ public class Continuation {
|
|||
case 2 -> Pinned.CRITICAL_SECTION;
|
||||
case 3 -> Pinned.NATIVE;
|
||||
case 4 -> Pinned.MONITOR;
|
||||
case 5 -> Pinned.EXCEPTION;
|
||||
default -> throw new AssertionError("Unknown pinned reason: " + reason);
|
||||
};
|
||||
}
|
||||
|
@ -358,8 +361,6 @@ public class Continuation {
|
|||
@Hidden
|
||||
@JvmtiHideEvents
|
||||
private boolean yield0(ContinuationScope scope, Continuation child) {
|
||||
preempted = false;
|
||||
|
||||
if (scope != this.scope)
|
||||
this.yieldInfo = scope;
|
||||
int res = doYield();
|
||||
|
|
|
@ -26,7 +26,9 @@ package sun.nio.ch;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
@ -40,7 +42,7 @@ import sun.security.action.GetPropertyAction;
|
|||
* Polls file descriptors. Virtual threads invoke the poll method to park
|
||||
* until a given file descriptor is ready for I/O.
|
||||
*/
|
||||
abstract class Poller {
|
||||
public abstract class Poller {
|
||||
private static final Pollers POLLERS;
|
||||
static {
|
||||
try {
|
||||
|
@ -142,6 +144,20 @@ abstract class Poller {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parks the current thread until a Selector's file descriptor is ready.
|
||||
* @param fdVal the Selector's file descriptor
|
||||
* @param nanos the waiting time or 0 to wait indefinitely
|
||||
*/
|
||||
static void pollSelector(int fdVal, long nanos) throws IOException {
|
||||
assert nanos >= 0L;
|
||||
Poller poller = POLLERS.masterPoller();
|
||||
if (poller == null) {
|
||||
poller = POLLERS.readPoller(fdVal);
|
||||
}
|
||||
poller.poll(fdVal, nanos, () -> true);
|
||||
}
|
||||
|
||||
/**
|
||||
* If there is a thread polling the given file descriptor for the given event then
|
||||
* the thread is unparked.
|
||||
|
@ -258,6 +274,18 @@ abstract class Poller {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number I/O operations currently registered with this poller.
|
||||
*/
|
||||
public int registered() {
|
||||
return map.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Objects.toIdentityString(this) + " [registered = " + registered() + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* The Pollers used for read and write events.
|
||||
*/
|
||||
|
@ -344,6 +372,13 @@ abstract class Poller {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the master poller, or null if there is no master poller.
|
||||
*/
|
||||
Poller masterPoller() {
|
||||
return masterPoller;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the read poller for the given file descriptor.
|
||||
*/
|
||||
|
@ -360,6 +395,21 @@ abstract class Poller {
|
|||
return writePollers[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of read pollers.
|
||||
*/
|
||||
List<Poller> readPollers() {
|
||||
return List.of(readPollers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of write pollers.
|
||||
*/
|
||||
List<Poller> writePollers() {
|
||||
return List.of(writePollers);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads the given property name to get the poller count. If the property is
|
||||
* set then the value must be a power of 2. Returns 1 if the property is not
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.net.Socket;
|
||||
import java.security.*;
|
||||
import java.security.cert.*;
|
||||
|
@ -51,6 +52,15 @@ import sun.security.validator.*;
|
|||
final class X509TrustManagerImpl extends X509ExtendedTrustManager
|
||||
implements X509TrustManager {
|
||||
|
||||
static {
|
||||
// eagerly initialize to avoid pinning virtual thread during TLS handshake
|
||||
try {
|
||||
MethodHandles.lookup().ensureInitialized(AnchorCertificates.class);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new ExceptionInInitializerError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private final String validatorType;
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue