8284161: Implementation of Virtual Threads (Preview)

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

View file

@ -384,7 +384,7 @@ public class Exchanger<V> {
else if (spins > 0) {
h ^= h << 1; h ^= h >>> 3; h ^= h << 10; // xorshift
if (h == 0) // initialize hash
h = SPINS | (int)t.getId();
h = SPINS | (int)t.threadId();
else if (h < 0 && // approx 50% true
(--spins & ((SPINS >>> 1) - 1)) == 0)
Thread.yield(); // two yields per wait
@ -489,7 +489,7 @@ public class Exchanger<V> {
if (spins > 0) {
h ^= h << 1; h ^= h >>> 3; h ^= h << 10;
if (h == 0)
h = SPINS | (int)t.getId();
h = SPINS | (int)t.threadId();
else if (h < 0 && (--spins & ((SPINS >>> 1) - 1)) == 0)
Thread.yield();
}

View file

@ -45,6 +45,7 @@ import java.security.PrivilegedExceptionAction;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import jdk.internal.javac.PreviewFeature;
import sun.security.util.SecurityConstants;
/**
@ -237,6 +238,43 @@ public class Executors {
threadFactory);
}
/**
* Creates an Executor that starts a new Thread for each task.
* The number of threads created by the Executor is unbounded.
*
* <p> Invoking {@link Future#cancel(boolean) cancel(true)} on a {@link
* Future Future} representing the pending result of a task submitted to
* the Executor will {@link Thread#interrupt() interrupt} the thread
* executing the task.
*
* @param threadFactory the factory to use when creating new threads
* @return a new executor that creates a new Thread for each task
* @throws NullPointerException if threadFactory is null
* @since 19
*/
@PreviewFeature(feature = PreviewFeature.Feature.VIRTUAL_THREADS)
public static ExecutorService newThreadPerTaskExecutor(ThreadFactory threadFactory) {
return ThreadPerTaskExecutor.create(threadFactory);
}
/**
* Creates an Executor that starts a new virtual Thread for each task.
* The number of threads created by the Executor is unbounded.
*
* <p> This method is equivalent to invoking
* {@link #newThreadPerTaskExecutor(ThreadFactory)} with a thread factory
* that creates virtual threads.
*
* @return a new executor that creates a new virtual Thread for each task
* @throws UnsupportedOperationException if preview features are not enabled
* @since 19
*/
@PreviewFeature(feature = PreviewFeature.Feature.VIRTUAL_THREADS)
public static ExecutorService newVirtualThreadPerTaskExecutor() {
ThreadFactory factory = Thread.ofVirtual().factory();
return newThreadPerTaskExecutor(factory);
}
/**
* Creates a single-threaded executor that can schedule commands
* to run after a given delay, or to execute periodically.

View file

@ -51,8 +51,10 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;
import jdk.internal.access.JavaUtilConcurrentFJPAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.Unsafe;
//import jdk.internal.vm.SharedThreadContainer; // for loom
import jdk.internal.vm.SharedThreadContainer;
/**
* An {@link ExecutorService} for running {@link ForkJoinTask}s.
@ -1512,7 +1514,7 @@ public class ForkJoinPool extends AbstractExecutorService {
final ForkJoinWorkerThreadFactory factory;
final UncaughtExceptionHandler ueh; // per-worker UEH
final Predicate<? super ForkJoinPool> saturate;
// final SharedThreadContainer container; // for loom
final SharedThreadContainer container;
@jdk.internal.vm.annotation.Contended("fjpctl") // segregate
volatile long ctl; // main pool control
@ -1568,8 +1570,7 @@ public class ForkJoinPool extends AbstractExecutorService {
try {
if (runState >= 0 && // avoid construction if terminating
fac != null && (wt = fac.newThread(this)) != null) {
wt.start(); // replace with following line for loom
// container.start(wt);
container.start(wt);
return true;
}
} catch (Throwable rex) {
@ -2528,7 +2529,7 @@ public class ForkJoinPool extends AbstractExecutorService {
if ((cond = termination) != null)
cond.signalAll();
lock.unlock();
// container.close(); // for loom
container.close();
}
return true;
}
@ -2721,7 +2722,7 @@ public class ForkJoinPool extends AbstractExecutorService {
String pid = Integer.toString(getAndAddPoolIds(1) + 1);
String name = "ForkJoinPool-" + pid;
this.workerNamePrefix = name + "-worker-";
// this.container = SharedThreadContainer.create(name); // for loom
this.container = SharedThreadContainer.create(name);
}
/**
@ -2773,7 +2774,7 @@ public class ForkJoinPool extends AbstractExecutorService {
this.workerNamePrefix = null;
this.registrationLock = new ReentrantLock();
this.queues = new WorkQueue[size];
// this.container = SharedThreadContainer.create("ForkJoinPool.commonPool"); // for loom
this.container = SharedThreadContainer.create("ForkJoinPool.commonPool");
}
/**
@ -3662,6 +3663,32 @@ public class ForkJoinPool extends AbstractExecutorService {
}
}
/**
* Invokes tryCompensate to create or re-activate a spare thread to
* compensate for a thread that performs a blocking operation. When the
* blocking operation is done then endCompensatedBlock must be invoked
* with the value returned by this method to re-adjust the parallelism.
*/
private long beginCompensatedBlock() {
for (;;) {
int comp;
if ((comp = tryCompensate(ctl, false)) >= 0) {
return (comp == 0) ? 0L : RC_UNIT;
} else {
Thread.onSpinWait();
}
}
}
/**
* Re-adjusts parallelism after a blocking operation completes.
*/
void endCompensatedBlock(long post) {
if (post > 0) {
getAndAddCtl(post);
}
}
/** ManagedBlock for external threads */
private static void unmanagedBlock(ManagedBlocker blocker)
throws InterruptedException {
@ -3704,6 +3731,17 @@ public class ForkJoinPool extends AbstractExecutorService {
AccessController.doPrivileged(new PrivilegedAction<>() {
public ForkJoinPool run() {
return new ForkJoinPool((byte)0); }});
// allow access to non-public methods
SharedSecrets.setJavaUtilConcurrentFJPAccess(
new JavaUtilConcurrentFJPAccess() {
@Override
public long beginCompensatedBlock(ForkJoinPool pool) {
return pool.beginCompensatedBlock();
}
public void endCompensatedBlock(ForkJoinPool pool, long post) {
pool.endCompensatedBlock(post);
}
});
Class<?> dep = LockSupport.class; // ensure loaded
}
}

View file

@ -54,16 +54,19 @@ package java.util.concurrent;
* to known values before returning it.
* @since 1.5
* @author Doug Lea
* @see Thread.Builder#factory()
*/
public interface ThreadFactory {
/**
* Constructs a new {@code Thread}. Implementations may also initialize
* priority, name, daemon status, {@code ThreadGroup}, etc.
* Constructs a new unstarted {@code Thread} to run the given runnable.
*
* @param r a runnable to be executed by new thread instance
* @return constructed thread, or {@code null} if the request to
* create a thread is rejected
*
* @see <a href="../../lang/Thread.html#inheritance">Inheritance when
* creating threads</a>
*/
Thread newThread(Runnable r);
}

View file

@ -50,6 +50,8 @@ import java.util.random.RandomGenerator;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import jdk.internal.access.JavaUtilConcurrentTLRAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.util.random.RandomSupport;
import jdk.internal.util.random.RandomSupport.*;
import jdk.internal.misc.Unsafe;
@ -218,7 +220,7 @@ public final class ThreadLocalRandom extends Random {
final long nextSeed() {
Thread t; long r; // read and update per-thread seed
U.putLong(t = Thread.currentThread(), SEED,
r = U.getLong(t, SEED) + (t.getId() << 1) + GOLDEN_GAMMA);
r = U.getLong(t, SEED) + (t.threadId() << 1) + GOLDEN_GAMMA);
return r;
}
@ -399,6 +401,19 @@ public final class ThreadLocalRandom extends Random {
= new AtomicLong(RandomSupport.mixMurmur64(System.currentTimeMillis()) ^
RandomSupport.mixMurmur64(System.nanoTime()));
// used by ExtentLocal
private static class Access {
static {
SharedSecrets.setJavaUtilConcurrentTLRAccess(
new JavaUtilConcurrentTLRAccess() {
public int nextSecondaryThreadLocalRandomSeed() {
return nextSecondarySeed();
}
}
);
}
}
// at end of <clinit> to survive static initialization circularity
static {
String sec = VM.getSavedProperty("java.util.secureRandomSeed");

View file

@ -0,0 +1,601 @@
/*
* Copyright (c) 2019, 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.util.concurrent;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.security.Permission;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.locks.LockSupport;
import java.util.stream.Stream;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import jdk.internal.access.JavaLangAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.vm.ThreadContainer;
import jdk.internal.vm.ThreadContainers;
/**
* An ExecutorService that starts a new thread for each task. The number of
* threads is unbounded.
*/
class ThreadPerTaskExecutor extends ThreadContainer implements ExecutorService {
private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
private static final Permission MODIFY_THREAD = new RuntimePermission("modifyThread");
private static final VarHandle STATE;
static {
try {
MethodHandles.Lookup l = MethodHandles.lookup();
STATE = l.findVarHandle(ThreadPerTaskExecutor.class, "state", int.class);
} catch (Exception e) {
throw new InternalError(e);
}
}
private final ThreadFactory factory;
private final Set<Thread> threads = ConcurrentHashMap.newKeySet();
private final CountDownLatch terminationSignal = new CountDownLatch(1);
// states: RUNNING -> SHUTDOWN -> TERMINATED
private static final int RUNNING = 0;
private static final int SHUTDOWN = 1;
private static final int TERMINATED = 2;
private volatile int state;
// the key for this container in the registry
private volatile Object key;
private ThreadPerTaskExecutor(ThreadFactory factory) {
super(/*shared*/ true);
this.factory = Objects.requireNonNull(factory);
}
/**
* Creates a thread-per-task executor that creates threads using the given factory.
*/
static ThreadPerTaskExecutor create(ThreadFactory factory) {
var executor = new ThreadPerTaskExecutor(factory);
// register it to allow discovery by serviceability tools
executor.key = ThreadContainers.registerContainer(executor);
return executor;
}
/**
* Throws SecurityException if there is a security manager set and it denies
* RuntimePermission("modifyThread").
*/
@SuppressWarnings("removal")
private void checkPermission() {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(MODIFY_THREAD);
}
}
/**
* Throws RejectedExecutionException if the executor has been shutdown.
*/
private void ensureNotShutdown() {
if (state >= SHUTDOWN) {
// shutdown or terminated
throw new RejectedExecutionException();
}
}
/**
* Attempts to terminate if already shutdown. If this method terminates the
* executor then it signals any threads that are waiting for termination.
*/
private void tryTerminate() {
assert state >= SHUTDOWN;
if (threads.isEmpty()
&& STATE.compareAndSet(this, SHUTDOWN, TERMINATED)) {
// signal waiters
terminationSignal.countDown();
// remove from registry
ThreadContainers.deregisterContainer(key);
}
}
/**
* Attempts to shutdown and terminate the executor.
* If interruptThreads is true then all running threads are interrupted.
*/
private void tryShutdownAndTerminate(boolean interruptThreads) {
if (STATE.compareAndSet(this, RUNNING, SHUTDOWN))
tryTerminate();
if (interruptThreads) {
threads.forEach(Thread::interrupt);
}
}
@Override
public Stream<Thread> threads() {
return threads.stream().filter(Thread::isAlive);
}
@Override
public long threadCount() {
return threads.size();
}
@Override
public void shutdown() {
checkPermission();
if (!isShutdown())
tryShutdownAndTerminate(false);
}
@Override
public List<Runnable> shutdownNow() {
checkPermission();
if (!isTerminated())
tryShutdownAndTerminate(true);
return List.of();
}
@Override
public boolean isShutdown() {
return state >= SHUTDOWN;
}
@Override
public boolean isTerminated() {
return state >= TERMINATED;
}
@Override
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
Objects.requireNonNull(unit);
if (isTerminated()) {
return true;
} else {
return terminationSignal.await(timeout, unit);
}
}
/**
* Waits for executor to terminate.
*/
private void awaitTermination() {
boolean terminated = isTerminated();
if (!terminated) {
tryShutdownAndTerminate(false);
boolean interrupted = false;
while (!terminated) {
try {
terminated = awaitTermination(1L, TimeUnit.DAYS);
} catch (InterruptedException e) {
if (!interrupted) {
tryShutdownAndTerminate(true);
interrupted = true;
}
}
}
if (interrupted) {
Thread.currentThread().interrupt();
}
}
}
@Override
public void close() {
checkPermission();
awaitTermination();
}
/**
* Creates a thread to run the given task.
*/
private Thread newThread(Runnable task) {
Thread thread = factory.newThread(task);
if (thread == null)
throw new RejectedExecutionException();
return thread;
}
/**
* Notify the executor that the task executed by the given thread is complete.
* If the executor has been shutdown then this method will attempt to terminate
* the executor.
*/
private void taskComplete(Thread thread) {
boolean removed = threads.remove(thread);
assert removed;
if (state == SHUTDOWN) {
tryTerminate();
}
}
/**
* Adds a thread to the set of threads and starts it.
* @throws RejectedExecutionException
*/
private void start(Thread thread) {
assert thread.getState() == Thread.State.NEW;
threads.add(thread);
boolean started = false;
try {
if (state == RUNNING) {
JLA.start(thread, this);
started = true;
}
} finally {
if (!started) {
taskComplete(thread);
}
}
// throw REE if thread not started and no exception thrown
if (!started) {
throw new RejectedExecutionException();
}
}
/**
* Starts a thread to execute the given task.
* @throws RejectedExecutionException
*/
private Thread start(Runnable task) {
Objects.requireNonNull(task);
ensureNotShutdown();
Thread thread = newThread(new TaskRunner(this, task));
start(thread);
return thread;
}
@Override
public void execute(Runnable task) {
start(task);
}
@Override
public <T> Future<T> submit(Callable<T> task) {
Objects.requireNonNull(task);
ensureNotShutdown();
var future = new ThreadBoundFuture<>(this, task);
Thread thread = future.thread();
start(thread);
return future;
}
@Override
public Future<?> submit(Runnable task) {
return submit(Executors.callable(task));
}
@Override
public <T> Future<T> submit(Runnable task, T result) {
return submit(Executors.callable(task, result));
}
/**
* Runs a task and notifies the executor when it completes.
*/
private static class TaskRunner implements Runnable {
final ThreadPerTaskExecutor executor;
final Runnable task;
TaskRunner(ThreadPerTaskExecutor executor, Runnable task) {
this.executor = executor;
this.task = task;
}
@Override
public void run() {
try {
task.run();
} finally {
executor.taskComplete(Thread.currentThread());
}
}
}
/**
* A Future for a task that runs in its own thread. The thread is
* created (but not started) when the Future is created. The thread
* is interrupted when the future is cancelled. The executor is
* notified when the task completes.
*/
private static class ThreadBoundFuture<T>
extends CompletableFuture<T> implements Runnable {
final ThreadPerTaskExecutor executor;
final Callable<T> task;
final Thread thread;
ThreadBoundFuture(ThreadPerTaskExecutor executor, Callable<T> task) {
this.executor = executor;
this.task = task;
this.thread = executor.newThread(this);
}
Thread thread() {
return thread;
}
@Override
public void run() {
if (Thread.currentThread() != thread) {
// should not happen except where something casts this object
// to a Runnable and invokes the run method.
throw new WrongThreadException();
}
try {
T result = task.call();
complete(result);
} catch (Throwable e) {
completeExceptionally(e);
} finally {
executor.taskComplete(thread);
}
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
boolean cancelled = super.cancel(mayInterruptIfRunning);
if (cancelled && mayInterruptIfRunning)
thread.interrupt();
return cancelled;
}
}
@Override
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException {
Objects.requireNonNull(tasks);
List<Future<T>> futures = new ArrayList<>();
int j = 0;
try {
for (Callable<T> t : tasks) {
Future<T> f = submit(t);
futures.add(f);
}
for (int size = futures.size(); j < size; j++) {
Future<T> f = futures.get(j);
if (!f.isDone()) {
try {
f.get();
} catch (ExecutionException | CancellationException ignore) { }
}
}
return futures;
} finally {
cancelAll(futures, j);
}
}
@Override
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException {
Objects.requireNonNull(tasks);
long deadline = System.nanoTime() + unit.toNanos(timeout);
List<Future<T>> futures = new ArrayList<>();
int j = 0;
try {
for (Callable<T> t : tasks) {
Future<T> f = submit(t);
futures.add(f);
}
for (int size = futures.size(); j < size; j++) {
Future<T> f = futures.get(j);
if (!f.isDone()) {
try {
f.get(deadline - System.nanoTime(), NANOSECONDS);
} catch (TimeoutException e) {
break;
} catch (ExecutionException | CancellationException ignore) { }
}
}
return futures;
} finally {
cancelAll(futures, j);
}
}
private <T> void cancelAll(List<Future<T>> futures, int j) {
for (int size = futures.size(); j < size; j++)
futures.get(j).cancel(true);
}
@Override
public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException {
try {
return invokeAny(tasks, false, 0, null);
} catch (TimeoutException e) {
// should not happen
throw new InternalError(e);
}
}
@Override
public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
Objects.requireNonNull(unit);
return invokeAny(tasks, true, timeout, unit);
}
private <T> T invokeAny(Collection<? extends Callable<T>> tasks,
boolean timed,
long timeout,
TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
int size = tasks.size();
if (size == 0) {
throw new IllegalArgumentException("'tasks' is empty");
}
var holder = new AnyResultHolder<T>(Thread.currentThread());
var threadList = new ArrayList<Thread>(size);
long nanos = (timed) ? unit.toNanos(timeout) : 0;
long startNanos = (timed) ? System.nanoTime() : 0;
try {
int count = 0;
Iterator<? extends Callable<T>> iterator = tasks.iterator();
while (count < size && iterator.hasNext()) {
Callable<T> task = iterator.next();
Objects.requireNonNull(task);
Thread thread = start(() -> {
try {
T r = task.call();
holder.complete(r);
} catch (Throwable e) {
holder.completeExceptionally(e);
}
});
threadList.add(thread);
count++;
}
if (count == 0) {
throw new IllegalArgumentException("'tasks' is empty");
}
if (Thread.interrupted())
throw new InterruptedException();
T result = holder.result();
while (result == null && holder.exceptionCount() < count) {
if (timed) {
long remainingNanos = nanos - (System.nanoTime() - startNanos);
if (remainingNanos <= 0)
throw new TimeoutException();
LockSupport.parkNanos(remainingNanos);
} else {
LockSupport.park();
}
if (Thread.interrupted())
throw new InterruptedException();
result = holder.result();
}
if (result != null) {
return (result != AnyResultHolder.NULL) ? result : null;
} else {
throw new ExecutionException(holder.firstException());
}
} finally {
// interrupt any threads that are still running
for (Thread t : threadList) {
if (t.isAlive()) {
t.interrupt();
}
}
}
}
/**
* An object for use by invokeAny to hold the result of the first task
* to complete normally and/or the first exception thrown. The object
* also maintains a count of the number of tasks that attempted to
* complete up to when the first tasks completes normally.
*/
private static class AnyResultHolder<T> {
private static final VarHandle RESULT;
private static final VarHandle EXCEPTION;
private static final VarHandle EXCEPTION_COUNT;
static {
try {
MethodHandles.Lookup l = MethodHandles.lookup();
RESULT = l.findVarHandle(AnyResultHolder.class, "result", Object.class);
EXCEPTION = l.findVarHandle(AnyResultHolder.class, "exception", Throwable.class);
EXCEPTION_COUNT = l.findVarHandle(AnyResultHolder.class, "exceptionCount", int.class);
} catch (Exception e) {
throw new InternalError(e);
}
}
private static final Object NULL = new Object();
private final Thread owner;
private volatile T result;
private volatile Throwable exception;
private volatile int exceptionCount;
AnyResultHolder(Thread owner) {
this.owner = owner;
}
/**
* Complete with the given result if not already completed. The winner
* unparks the owner thread.
*/
void complete(T value) {
@SuppressWarnings("unchecked")
T v = (value != null) ? value : (T) NULL;
if (result == null && RESULT.compareAndSet(this, null, v)) {
LockSupport.unpark(owner);
}
}
/**
* Complete with the given exception. If the result is not already
* set then it unparks the owner thread.
*/
void completeExceptionally(Throwable exc) {
if (result == null) {
if (exception == null)
EXCEPTION.compareAndSet(this, null, exc);
EXCEPTION_COUNT.getAndAdd(this, 1);
LockSupport.unpark(owner);
}
}
/**
* Returns non-null if a task completed successfully. The result is
* NULL if completed with null.
*/
T result() {
return result;
}
/**
* Returns the first exception thrown if recorded by this object.
*
* @apiNote The result() method should be used to test if there is
* a result before invoking the exception method.
*/
Throwable firstException() {
return exception;
}
/**
* Returns the number of tasks that terminated with an exception before
* a task completed normally.
*/
int exceptionCount() {
return exceptionCount;
}
}
}

View file

@ -40,10 +40,12 @@ import java.util.ConcurrentModificationException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import jdk.internal.vm.SharedThreadContainer;
/**
* An {@link ExecutorService} that executes each submitted task using
@ -477,6 +479,11 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
*/
private final Condition termination = mainLock.newCondition();
/**
* The thread container for the worker threads.
*/
private final SharedThreadContainer container;
/**
* Tracks largest attained pool size. Accessed only under
* mainLock.
@ -726,6 +733,7 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
} finally {
ctl.set(ctlOf(TERMINATED, 0));
termination.signalAll();
container.close();
}
return;
}
@ -942,7 +950,7 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
mainLock.unlock();
}
if (workerAdded) {
t.start();
container.start(t);
workerStarted = true;
}
}
@ -1309,6 +1317,9 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
String name = Objects.toIdentityString(this);
this.container = SharedThreadContainer.create(name);
}
/**

View file

@ -35,6 +35,7 @@
package java.util.concurrent.locks;
import jdk.internal.misc.VirtualThreads;
import jdk.internal.misc.Unsafe;
/**
@ -173,8 +174,13 @@ public class LockSupport {
* this operation has no effect
*/
public static void unpark(Thread thread) {
if (thread != null)
U.unpark(thread);
if (thread != null) {
if (thread.isVirtual()) {
VirtualThreads.unpark(thread);
} else {
U.unpark(thread);
}
}
}
/**
@ -208,8 +214,15 @@ public class LockSupport {
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
U.park(false, 0L);
setBlocker(t, null);
try {
if (t.isVirtual()) {
VirtualThreads.park();
} else {
U.park(false, 0L);
}
} finally {
setBlocker(t, null);
}
}
/**
@ -249,8 +262,15 @@ public class LockSupport {
if (nanos > 0) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
U.park(false, nanos);
setBlocker(t, null);
try {
if (t.isVirtual()) {
VirtualThreads.park(nanos);
} else {
U.park(false, nanos);
}
} finally {
setBlocker(t, null);
}
}
}
@ -290,8 +310,15 @@ public class LockSupport {
public static void parkUntil(Object blocker, long deadline) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
U.park(true, deadline);
setBlocker(t, null);
try {
if (t.isVirtual()) {
VirtualThreads.parkUntil(deadline);
} else {
U.park(true, deadline);
}
} finally {
setBlocker(t, null);
}
}
/**
@ -338,7 +365,11 @@ public class LockSupport {
* for example, the interrupt status of the thread upon return.
*/
public static void park() {
U.park(false, 0L);
if (Thread.currentThread().isVirtual()) {
VirtualThreads.park();
} else {
U.park(false, 0L);
}
}
/**
@ -372,8 +403,13 @@ public class LockSupport {
* @param nanos the maximum number of nanoseconds to wait
*/
public static void parkNanos(long nanos) {
if (nanos > 0)
U.park(false, nanos);
if (nanos > 0) {
if (Thread.currentThread().isVirtual()) {
VirtualThreads.park(nanos);
} else {
U.park(false, nanos);
}
}
}
/**
@ -407,24 +443,23 @@ public class LockSupport {
* to wait until
*/
public static void parkUntil(long deadline) {
U.park(true, deadline);
if (Thread.currentThread().isVirtual()) {
VirtualThreads.parkUntil(deadline);
} else {
U.park(true, deadline);
}
}
/**
* Returns the thread id for the given thread. We must access
* this directly rather than via method Thread.getId() because
* getId() has been known to be overridden in ways that do not
* preserve unique mappings.
* Returns the thread id for the given thread.
*/
static final long getThreadId(Thread thread) {
return U.getLong(thread, TID);
return thread.threadId();
}
// Hotspot implementation via intrinsics API
private static final Unsafe U = Unsafe.getUnsafe();
private static final long PARKBLOCKER
= U.objectFieldOffset(Thread.class, "parkBlocker");
private static final long TID
= U.objectFieldOffset(Thread.class, "tid");
}

View file

@ -27,6 +27,7 @@ package java.util.jar;
import jdk.internal.access.SharedSecrets;
import jdk.internal.access.JavaUtilZipFileAccess;
import jdk.internal.misc.ThreadTracker;
import sun.security.action.GetPropertyAction;
import sun.security.util.ManifestEntryVerifier;
@ -44,10 +45,8 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Locale;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
@ -150,7 +149,6 @@ public class JarFile extends ZipFile {
private static final Runtime.Version RUNTIME_VERSION;
private static final boolean MULTI_RELEASE_ENABLED;
private static final boolean MULTI_RELEASE_FORCED;
private static final ThreadLocal<Boolean> isInitializing = new ThreadLocal<>();
// The maximum size of array to allocate. Some VMs reserve some header words in an array.
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
@ -1036,6 +1034,18 @@ public class JarFile extends ZipFile {
}
}
private static class ThreadTrackHolder {
static final ThreadTracker TRACKER = new ThreadTracker();
}
private static Object beginInit() {
return ThreadTrackHolder.TRACKER.begin();
}
private static void endInit(Object key) {
ThreadTrackHolder.TRACKER.end(key);
}
synchronized void ensureInitialization() {
try {
maybeInstantiateVerifier();
@ -1043,19 +1053,18 @@ public class JarFile extends ZipFile {
throw new RuntimeException(e);
}
if (jv != null && !jvInitialized) {
isInitializing.set(Boolean.TRUE);
Object key = beginInit();
try {
initializeVerifier();
jvInitialized = true;
} finally {
isInitializing.set(Boolean.FALSE);
endInit(key);
}
}
}
static boolean isInitializing() {
Boolean value = isInitializing.get();
return (value == null) ? false : value;
return ThreadTrackHolder.TRACKER.contains(Thread.currentThread());
}
/*