mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 06:45:07 +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
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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");
|
||||
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue