diff --git a/extensions/virtual-threads/runtime/src/main/java/io/quarkus/virtual/threads/VirtualThreadsRecorder.java b/extensions/virtual-threads/runtime/src/main/java/io/quarkus/virtual/threads/VirtualThreadsRecorder.java index 16336d8ec8fc9..1f2387837c566 100644 --- a/extensions/virtual-threads/runtime/src/main/java/io/quarkus/virtual/threads/VirtualThreadsRecorder.java +++ b/extensions/virtual-threads/runtime/src/main/java/io/quarkus/virtual/threads/VirtualThreadsRecorder.java @@ -4,7 +4,6 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.Optional; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; @@ -107,8 +106,20 @@ static ExecutorService newVirtualThreadPerTaskExecutorWithName(String prefix) Method ofVirtual = Thread.class.getMethod("ofVirtual"); Object vtb = ofVirtual.invoke(VirtualThreadsRecorder.class); Class vtbClass = Class.forName("java.lang.Thread$Builder$OfVirtual"); - Method name = vtbClass.getMethod("name", String.class, long.class); - vtb = name.invoke(vtb, prefix, 0); + // .name() + if (prefix != null) { + Method name = vtbClass.getMethod("name", String.class, long.class); + vtb = name.invoke(vtb, prefix, 0); + } + // .uncaughtExceptionHandler() + Method uncaughtHandler = vtbClass.getMethod("uncaughtExceptionHandler", Thread.UncaughtExceptionHandler.class); + vtb = uncaughtHandler.invoke(vtb, new Thread.UncaughtExceptionHandler() { + @Override + public void uncaughtException(Thread t, Throwable e) { + logger.errorf(e, "Thread %s threw an uncaught exception:", t); + } + }); + // .factory() Method factory = vtbClass.getMethod("factory"); ThreadFactory tf = (ThreadFactory) factory.invoke(vtb); @@ -116,25 +127,6 @@ static ExecutorService newVirtualThreadPerTaskExecutorWithName(String prefix) .invoke(VirtualThreadsRecorder.class, tf); } - static ExecutorService newVirtualThreadPerTaskExecutor() - throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { - return (ExecutorService) Executors.class.getMethod("newVirtualThreadPerTaskExecutor") - .invoke(VirtualThreadsRecorder.class); - } - - static ExecutorService newVirtualThreadExecutor() - throws InvocationTargetException, NoSuchMethodException, IllegalAccessException { - try { - Optional namePrefix = config.namePrefix; - return namePrefix.isPresent() ? newVirtualThreadPerTaskExecutorWithName(namePrefix.get()) - : newVirtualThreadPerTaskExecutor(); - } catch (ClassNotFoundException e) { - logger.warn("Unable to invoke java.util.concurrent.Executors#newThreadPerTaskExecutor" + - " with VirtualThreadFactory, falling back to unnamed virtual threads", e); - return newVirtualThreadPerTaskExecutor(); - } - } - /** * This method uses reflection in order to allow developers to quickly test quarkus-loom without needing to * change --release, --source, --target flags and to enable previews. @@ -144,8 +136,9 @@ static ExecutorService newVirtualThreadExecutor() private static ExecutorService createExecutor() { if (config.enabled) { try { - return new ContextPreservingExecutorService(newVirtualThreadExecutor()); - } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { + String prefix = config.namePrefix.orElse(null); + return new ContextPreservingExecutorService(newVirtualThreadPerTaskExecutorWithName(prefix)); + } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException | ClassNotFoundException e) { logger.debug("Unable to invoke java.util.concurrent.Executors#newVirtualThreadPerTaskExecutor", e); //quite ugly but works logger.warn("You weren't able to create an executor that spawns virtual threads, the default" + diff --git a/extensions/virtual-threads/runtime/src/test/java/io/quarkus/virtual/threads/VirtualThreadExecutorSupplierTest.java b/extensions/virtual-threads/runtime/src/test/java/io/quarkus/virtual/threads/VirtualThreadExecutorSupplierTest.java index e4cb74d37d117..04cc1219d35a6 100644 --- a/extensions/virtual-threads/runtime/src/test/java/io/quarkus/virtual/threads/VirtualThreadExecutorSupplierTest.java +++ b/extensions/virtual-threads/runtime/src/test/java/io/quarkus/virtual/threads/VirtualThreadExecutorSupplierTest.java @@ -33,8 +33,8 @@ void virtualThreadCustomScheduler() @Test @EnabledForJreRange(min = JRE.JAVA_20, disabledReason = "Virtual Threads are a preview feature starting from Java 20") - void execute() throws InvocationTargetException, NoSuchMethodException, IllegalAccessException { - Executor executor = VirtualThreadsRecorder.newVirtualThreadPerTaskExecutor(); + void execute() throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException { + Executor executor = VirtualThreadsRecorder.newVirtualThreadPerTaskExecutorWithName(null); var assertSubscriber = Uni.createFrom().emitter(e -> { assertThat(Thread.currentThread().getName()).isEmpty(); assertThatItRunsOnVirtualThread();