22
22
23
23
import com .google .common .base .Preconditions ;
24
24
import io .temporal .internal .logging .LoggerTag ;
25
+ import io .temporal .internal .task .VirtualThreadDelegate ;
25
26
import java .util .Objects ;
26
27
import java .util .concurrent .*;
28
+ import java .util .concurrent .atomic .AtomicInteger ;
27
29
import javax .annotation .Nonnull ;
28
30
import org .slf4j .MDC ;
29
31
@@ -41,7 +43,7 @@ public interface TaskHandler<TT> {
41
43
private final TaskHandler <T > handler ;
42
44
private final PollerOptions pollerOptions ;
43
45
44
- private final ThreadPoolExecutor taskExecutor ;
46
+ private final ExecutorService taskExecutor ;
45
47
private final String pollThreadNamePrefix ;
46
48
47
49
PollTaskExecutor (
@@ -51,35 +53,46 @@ public interface TaskHandler<TT> {
51
53
@ Nonnull TaskHandler <T > handler ,
52
54
@ Nonnull PollerOptions pollerOptions ,
53
55
int workerTaskSlots ,
54
- boolean synchronousQueue ) {
56
+ boolean synchronousQueue ,
57
+ boolean useVirtualThreads ) {
55
58
this .namespace = Objects .requireNonNull (namespace );
56
59
this .taskQueue = Objects .requireNonNull (taskQueue );
57
60
this .identity = Objects .requireNonNull (identity );
58
61
this .handler = Objects .requireNonNull (handler );
59
62
this .pollerOptions = Objects .requireNonNull (pollerOptions );
60
63
61
- this .taskExecutor =
62
- new ThreadPoolExecutor (
63
- // for SynchronousQueue we can afford to set it to 0, because the queue is always full
64
- // or empty
65
- // for LinkedBlockingQueue we have to set slots to workerTaskSlots to avoid situation
66
- // when the queue grows, but the amount of threads is not, because the queue is not (and
67
- // never) full
68
- synchronousQueue ? 0 : workerTaskSlots ,
69
- workerTaskSlots ,
70
- 10 ,
71
- TimeUnit .SECONDS ,
72
- synchronousQueue ? new SynchronousQueue <>() : new LinkedBlockingQueue <>());
73
- this .taskExecutor .allowCoreThreadTimeOut (true );
74
-
75
64
this .pollThreadNamePrefix =
76
65
pollerOptions .getPollThreadNamePrefix ().replaceFirst ("Poller" , "Executor" );
77
-
78
- this .taskExecutor .setThreadFactory (
79
- new ExecutorThreadFactory (
80
- pollerOptions .getPollThreadNamePrefix ().replaceFirst ("Poller" , "Executor" ),
81
- pollerOptions .getUncaughtExceptionHandler ()));
82
- this .taskExecutor .setRejectedExecutionHandler (new BlockCallerPolicy ());
66
+ // If virtual threads are enabled, we use a virtual thread executor.
67
+ if (useVirtualThreads ) {
68
+ AtomicInteger threadIndex = new AtomicInteger ();
69
+ this .taskExecutor =
70
+ VirtualThreadDelegate .newVirtualThreadExecutor (
71
+ (t ) -> {
72
+ t .setName (this .pollThreadNamePrefix + ": " + threadIndex .incrementAndGet ());
73
+ t .setUncaughtExceptionHandler (pollerOptions .getUncaughtExceptionHandler ());
74
+ });
75
+ } else {
76
+ ThreadPoolExecutor threadPoolTaskExecutor =
77
+ new ThreadPoolExecutor (
78
+ // for SynchronousQueue we can afford to set it to 0, because the queue is always full
79
+ // or empty
80
+ // for LinkedBlockingQueue we have to set slots to workerTaskSlots to avoid situation
81
+ // when the queue grows, but the amount of threads is not, because the queue is not
82
+ // (and
83
+ // never) full
84
+ synchronousQueue ? 0 : workerTaskSlots ,
85
+ workerTaskSlots ,
86
+ 10 ,
87
+ TimeUnit .SECONDS ,
88
+ synchronousQueue ? new SynchronousQueue <>() : new LinkedBlockingQueue <>());
89
+ threadPoolTaskExecutor .allowCoreThreadTimeOut (true );
90
+ threadPoolTaskExecutor .setThreadFactory (
91
+ new ExecutorThreadFactory (
92
+ this .pollThreadNamePrefix , pollerOptions .getUncaughtExceptionHandler ()));
93
+ threadPoolTaskExecutor .setRejectedExecutionHandler (new BlockCallerPolicy ());
94
+ this .taskExecutor = threadPoolTaskExecutor ;
95
+ }
83
96
}
84
97
85
98
@ Override
0 commit comments