Skip to content

Commit 034aaee

Browse files
committed
add support for Virtual threads
Signed-off-by: Ceki Gulcu <[email protected]>
1 parent d6294e5 commit 034aaee

File tree

9 files changed

+109
-46
lines changed

9 files changed

+109
-46
lines changed

logback-core/src/main/java/ch/qos/logback/core/Context.java

+7
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,13 @@ public interface Context extends PropertyContainer {
132132
*/
133133
ExecutorService getExecutorService();
134134

135+
/**
136+
* Return an alternate {@link ExecutorService} used for one task per thread execution.
137+
* @return ExecutorService
138+
*/
139+
default ExecutorService getAlternateExecutorService() {
140+
return getExecutorService();
141+
}
135142

136143
/**
137144
* Register a component that participates in the context's life cycle.

logback-core/src/main/java/ch/qos/logback/core/ContextBase.java

+10-1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ public class ContextBase implements Context, LifeCycle {
5454
private ScheduledExecutorService scheduledExecutorService;
5555

5656
private ThreadPoolExecutor threadPoolExecutor;
57+
private ExecutorService alternateExecutorService;
58+
5759

5860
protected List<ScheduledFuture<?>> scheduledFutures = new ArrayList<ScheduledFuture<?>>(1);
5961
private LifeCycleManager lifeCycleManager;
@@ -225,8 +227,15 @@ public synchronized ExecutorService getExecutorService() {
225227
return threadPoolExecutor;
226228
}
227229

228-
229230
@Override
231+
public synchronized ExecutorService getAlternateExecutorService() {
232+
if(alternateExecutorService == null) {
233+
alternateExecutorService = ExecutorServiceUtil.newAlternateThreadPoolExecutor();
234+
}
235+
return alternateExecutorService;
236+
}
237+
238+
@Override
230239
public synchronized ScheduledExecutorService getScheduledExecutorService() {
231240
if (scheduledExecutorService == null) {
232241
scheduledExecutorService = ExecutorServiceUtil.newScheduledExecutorService();

logback-core/src/main/java/ch/qos/logback/core/CoreConstants.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,14 @@ public class CoreConstants {
2929
*/
3030
public static final int CORE_POOL_SIZE = 0;
3131

32-
public static final int SCHEDULED_EXECUTOR_POOL_SIZE = 2;
32+
// In Java 21 and later the actual threads are assumed to be virtual
33+
public static final int SCHEDULED_EXECUTOR_POOL_SIZE = 4;
3334

3435
/**
3536
* Maximum number of threads to allow in a context's executor service.
3637
*/
37-
// if you need a different MAX_POOL_SIZE, please file create a jira issue
38-
// asking to make MAX_POOL_SIZE a parameter.
38+
// if you need a different MAX_POOL_SIZE, please file create a github issue
39+
// asking for a larger MAX_POOL_SIZE parameter.
3940
public static final int MAX_POOL_SIZE = 32;
4041

4142
// Note that the line.separator property can be looked up even by

logback-core/src/main/java/ch/qos/logback/core/rolling/helper/TimeBasedArchiveRemover.java

+13-6
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,18 @@ public TimeBasedArchiveRemover(FileNamePattern fileNamePattern, RollingCalendar
4747

4848
int callCount = 0;
4949

50+
public Future<?> cleanAsynchronously(Instant now) {
51+
ArhiveRemoverRunnable runnable = new ArhiveRemoverRunnable(now);
52+
ExecutorService alternateExecutorService = context.getAlternateExecutorService();
53+
Future<?> future = alternateExecutorService.submit(runnable);
54+
return future;
55+
}
56+
57+
/**
58+
* Called from the cleaning thread.
59+
*
60+
* @param now
61+
*/
5062
@Override
5163
public void clean(Instant now) {
5264

@@ -233,12 +245,7 @@ public String toString() {
233245
return "c.q.l.core.rolling.helper.TimeBasedArchiveRemover";
234246
}
235247

236-
public Future<?> cleanAsynchronously(Instant now) {
237-
ArhiveRemoverRunnable runnable = new ArhiveRemoverRunnable(now);
238-
ExecutorService executorService = context.getExecutorService();
239-
Future<?> future = executorService.submit(runnable);
240-
return future;
241-
}
248+
242249

243250
public class ArhiveRemoverRunnable implements Runnable {
244251
Instant now;

logback-core/src/main/java/ch/qos/logback/core/util/AlternateExecutorServiceUtil.java

-24
This file was deleted.

logback-core/src/main/java/ch/qos/logback/core/util/EnvUtil.java

+8
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,14 @@ static public boolean isJDK18OrHigher() {
131131
return isJDK_N_OrHigher(18);
132132
}
133133

134+
/**
135+
* @since logback 1.3.12/1.4.12
136+
* @return true if runtime JDK is version 21 or higher
137+
*/
138+
static public boolean isJDK21OrHigher() {
139+
return isJDK_N_OrHigher(21);
140+
}
141+
134142
static public boolean isJaninoAvailable() {
135143
ClassLoader classLoader = EnvUtil.class.getClassLoader();
136144
try {

logback-core/src/main/java/ch/qos/logback/core/util/ExecutorServiceUtil.java

+54-4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
*/
1414
package ch.qos.logback.core.util;
1515

16+
import java.lang.reflect.InvocationTargetException;
17+
import java.lang.reflect.Method;
1618
import java.util.concurrent.ExecutorService;
1719
import java.util.concurrent.Executors;
1820
import java.util.concurrent.ScheduledExecutorService;
@@ -33,11 +35,39 @@
3335
*/
3436
public class ExecutorServiceUtil {
3537

36-
private static final ThreadFactory THREAD_FACTORY = new ThreadFactory() {
38+
static private final String NEW_VIRTUAL_TPT_METHOD_NAME = "newVirtualThreadPerTaskExecutor";
39+
40+
private static final ThreadFactory THREAD_FACTORY_FOR_SCHEDULED_EXECUTION_SERVICE = new ThreadFactory() {
3741

38-
private final ThreadFactory defaultFactory = Executors.defaultThreadFactory();
3942
private final AtomicInteger threadNumber = new AtomicInteger(1);
4043

44+
45+
private final ThreadFactory defaultFactory = makeThreadFactory();
46+
47+
/**
48+
* A thread factory which may be a virtual thread factory if available.
49+
*
50+
* @return
51+
*/
52+
private ThreadFactory makeThreadFactory() {
53+
if(EnvUtil.isJDK21OrHigher()) {
54+
try {
55+
Method ofVirtualMethod = Thread.class.getMethod("ofVirtual");
56+
Object threadBuilderOfVirtual = ofVirtualMethod.invoke(null);
57+
Method factoryMethod = threadBuilderOfVirtual.getClass().getMethod("factory");
58+
System.out.println("virtual THREAD FACTORY");
59+
return (ThreadFactory) factoryMethod.invoke(threadBuilderOfVirtual);
60+
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
61+
return Executors.defaultThreadFactory();
62+
}
63+
64+
} else {
65+
System.out.println("default THREAD FACTORY");
66+
return Executors.defaultThreadFactory();
67+
}
68+
}
69+
70+
@Override
4171
public Thread newThread(Runnable r) {
4272
Thread thread = defaultFactory.newThread(r);
4373
if (!thread.isDaemon()) {
@@ -49,7 +79,8 @@ public Thread newThread(Runnable r) {
4979
};
5080

5181
static public ScheduledExecutorService newScheduledExecutorService() {
52-
return new ScheduledThreadPoolExecutor(CoreConstants.SCHEDULED_EXECUTOR_POOL_SIZE, THREAD_FACTORY);
82+
return new ScheduledThreadPoolExecutor(CoreConstants.SCHEDULED_EXECUTOR_POOL_SIZE,
83+
THREAD_FACTORY_FOR_SCHEDULED_EXECUTION_SERVICE);
5384
}
5485

5586
/**
@@ -68,7 +99,7 @@ static public ExecutorService newExecutorService() {
6899
*/
69100
static public ThreadPoolExecutor newThreadPoolExecutor() {
70101
return new ThreadPoolExecutor(CoreConstants.CORE_POOL_SIZE, CoreConstants.MAX_POOL_SIZE, 0L,
71-
TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>(), THREAD_FACTORY);
102+
TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>(), THREAD_FACTORY_FOR_SCHEDULED_EXECUTION_SERVICE);
72103
}
73104

74105
/**
@@ -83,4 +114,23 @@ static public void shutdown(ExecutorService executorService) {
83114
}
84115
}
85116

117+
/**
118+
* An alternate implementation of {@linl #newThreadPoolExecutor} which returns a virtual thread per task executor when
119+
* available.
120+
*
121+
* @since 1.3.12/1.4.12
122+
*/
123+
static public ExecutorService newAlternateThreadPoolExecutor() {
124+
125+
if(EnvUtil.isJDK21OrHigher()) {
126+
try {
127+
Method newVirtualTPTMethod = Executors.class.getMethod(NEW_VIRTUAL_TPT_METHOD_NAME);
128+
return (ExecutorService) newVirtualTPTMethod.invoke(null);
129+
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
130+
return newThreadPoolExecutor();
131+
}
132+
} else {
133+
return newThreadPoolExecutor();
134+
}
135+
}
86136
}

logback-core/src/test/java/ch/qos/logback/core/rolling/TimeBasedRollingWithArchiveRemoval_Test.java

+1-4
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import java.time.Instant;
2323
import java.time.ZoneId;
2424
import java.time.ZonedDateTime;
25-
import java.time.temporal.ChronoUnit;
2625
import java.util.ArrayList;
2726
import java.util.Calendar;
2827
import java.util.Collections;
@@ -33,10 +32,8 @@
3332
import java.util.Set;
3433
import java.util.regex.Matcher;
3534
import java.util.regex.Pattern;
35+
import java.time.temporal.ChronoUnit;
3636

37-
//import org.joda.time.DateTimeZone;
38-
//import org.joda.time.Days;
39-
//import org.joda.time.LocalDate;
4037
import org.junit.jupiter.api.BeforeEach;
4138
import org.junit.jupiter.api.Disabled;
4239
import org.junit.jupiter.api.Test;

pom.xml

+12-4
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,13 @@
4949

5050
<properties>
5151
<!-- yyyy-MM-dd'T'HH:mm:ss'Z' -->
52-
<project.build.outputTimestamp>2023-08-09T19:42:13Z</project.build.outputTimestamp>
52+
<project.build.outputTimestamp>2023-08-09T19:42:13Z</project.build.outputTimestamp>
53+
54+
<!-- minimal JDK version at runtime -->
5355
<jdk.version>8</jdk.version>
54-
<maven.compiler.source>${jdk.version}</maven.compiler.source>
55-
<maven.compiler.target>${jdk.version}</maven.compiler.target>
56+
<maven.compiler.release>${jdk.version}</maven.compiler.release>
5657
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
57-
<junit.version>4.13.1</junit.version>
58+
5859
<junit-jupiter-api.version>5.9.1</junit-jupiter-api.version>
5960
<junit-vintage-engine.version>5.9.1</junit-vintage-engine.version>
6061
<junit-jupiter-params.version>5.9.1</junit-jupiter-params.version>
@@ -380,6 +381,7 @@
380381
<plugin>
381382
<groupId>org.apache.maven.plugins</groupId>
382383
<artifactId>maven-compiler-plugin</artifactId>
384+
<<<<<<< HEAD
383385

384386
<executions>
385387
<execution>
@@ -408,6 +410,12 @@
408410
</configuration>
409411
</execution>
410412
</executions>
413+
=======
414+
415+
<configuration>
416+
<release>${jdk.version}</release>
417+
</configuration>
418+
>>>>>>> 9a1fc44f8 (add support for Virtual threads)
411419
</plugin>
412420

413421
<plugin>

0 commit comments

Comments
 (0)