ThreadUtil.java
/*
* junixsocket
*
* Copyright 2009-2024 Christian Kohlschütter
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.newsclub.net.unix;
import java.io.InterruptedIOException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.LockSupport;
import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement;
/**
* Helper class to support certain Thread-specific features.
*
* @author Christian Kohlschütter
*/
@IgnoreJRERequirement // see src/main/java20
public final class ThreadUtil {
private static final ThreadLocal<Boolean> TREAT_AS_VIRTUAL_THREAD = new ThreadLocal<>();
private ThreadUtil() {
throw new IllegalStateException("No instances");
}
/**
* Checks if the current platform Thread is treated as a virtual one.
*
* @return {@code true} if so.
*/
private static boolean isTreatAsVirtualThread() {
return Boolean.TRUE.equals(TREAT_AS_VIRTUAL_THREAD.get());
}
/**
* Marks the current platform {@link Thread} to be treated as a virtual thread, if possible. Has
* no effect if the current Thread already is a virtual thread.
*
* @param b {@code true} to enable treatment of a platform thread as a virtual thread.
*/
public static void setTreatAsVirtualThread(boolean b) {
if (isVirtualThread()) {
return;
}
TREAT_AS_VIRTUAL_THREAD.set(b);
}
/**
* Checks if the current {@link Thread} is to be considered a virtual thread.
*
* @return {@code true} if so.
*/
public static boolean isVirtualThread() {
return Thread.currentThread().isVirtual() || isTreatAsVirtualThread();
}
/**
* Checks if virtual threads are considered to be supported (and therefore if special support
* should be enabled).
*
* @return {@code true} if so.
*/
public static boolean isVirtualThreadSupported() {
return true;
}
/**
* Returns a new "virtual thread per task executor". If virtual threads are not supported by this
* JVM, a new platform thread are created for each task, and such threads are marked to be treated
* as virtual threads.
*
* @return The new executor service.
*/
public static ExecutorService newVirtualThreadPerTaskExecutor() {
return Executors.newVirtualThreadPerTaskExecutor();
}
/**
* Checks if the current Thread has been interrupted, without clearing the flag; if interrupted,
* an {@link InterruptedIOException} is thrown, otherwise {@code true} is returned.
*
* @return {@code true}.
* @throws InterruptedIOException if interrupted.
*/
public static boolean checkNotInterruptedOrThrow() throws InterruptedIOException {
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedIOException();
}
return true;
}
/**
* Starts a new daemon thread.
*
* @param virtual If {@code true}, try to start a virtual Thread instead of a platform Thread (or
* at least pretend it's a virtual thread if they're not supported natively); if
* {@code false}, a "daemon" platform thread is started.
* @param run The runnable.
* @return The thread.
*/
public static Thread startNewDaemonThread(boolean virtual, Runnable run) {
if (virtual) {
return Thread.ofVirtual().start(run);
} else {
return Thread.ofPlatform().daemon(true).start(run);
}
}
/**
* Ensures that the given operation is being executed on a system thread. If the current thread is
* a virtual thread, the operation is executed <em>synchronously</em> via
* {@link CompletableFuture#runAsync(Runnable)}: the virtual thread is suspended during that
* operation, and subsequently resumed.
*
* @param op The operation to run.
* @throws InterruptedException on interrupt.
*/
public static void runOnSystemThread(Runnable op) throws InterruptedException {
if (isVirtualThread()) {
Thread vt = Thread.currentThread();
CompletableFuture<Void> cf = CompletableFuture.runAsync(() -> {
try {
op.run();
} finally {
LockSupport.unpark(vt);
}
});
LockSupport.park();
try {
cf.get();
} catch (ExecutionException e) {
Throwable t = e.getCause();
if (t instanceof Error) {
throw (Error) t; // NOPMD.PreserveStackTrace
} else if (t instanceof RuntimeException) {
throw (RuntimeException) t; // NOPMD.PreserveStackTrace
} else {
throw new IllegalStateException(e);
}
}
} else {
boolean treatAsVirtual = isTreatAsVirtualThread();
if (treatAsVirtual) {
setTreatAsVirtualThread(false);
}
try {
op.run();
} finally {
if (treatAsVirtual) {
setTreatAsVirtualThread(treatAsVirtual);
}
}
}
}
}