You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by rz...@apache.org on 2026/04/02 18:51:44 UTC
(tomee) 02/02: Use default MSES delegate for scheduled async trigger loop
This is an automated email from the ASF dual-hosted git repository.
rzo1 pushed a commit to branch concurency
in repository https://gitbox.apache.org/repos/asf/tomee.git
commit 75cefe7e61cb43e192cbc2c307a0d6d74917c800
Author: Richard Zowalla <rz...@apache.org>
AuthorDate: Thu Apr 2 20:17:21 2026 +0200
Use default MSES delegate for scheduled async trigger loop
Per spec, scheduled async methods are not subject to maxAsync
constraints. Use the default MSES's thread pool for the trigger loop
instead of the referenced executor's pool. This prevents thread
starvation when maxAsync threads are busy with blocking tasks.
---
.../openejb/cdi/concurrency/AsynchronousInterceptor.java | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/container/openejb-core/src/main/java/org/apache/openejb/cdi/concurrency/AsynchronousInterceptor.java b/container/openejb-core/src/main/java/org/apache/openejb/cdi/concurrency/AsynchronousInterceptor.java
index 263e34aaa5..5fef6731e2 100644
--- a/container/openejb-core/src/main/java/org/apache/openejb/cdi/concurrency/AsynchronousInterceptor.java
+++ b/container/openejb-core/src/main/java/org/apache/openejb/cdi/concurrency/AsynchronousInterceptor.java
@@ -128,7 +128,13 @@ public class AsynchronousInterceptor {
final boolean isVoid = ctx.getMethod().getReturnType() == Void.TYPE;
final ContextServiceImpl ctxService = (ContextServiceImpl) mses.getContextService();
final ContextServiceImpl.Snapshot snapshot = ctxService.snapshot(null);
- final ScheduledExecutorService delegate = mses.getDelegate();
+
+ // Per spec, scheduled async methods are NOT subject to maxAsync constraints.
+ // Use the default MSES's delegate for the trigger loop — it is not constrained
+ // by the referenced executor's maxAsync setting.
+ final ManagedScheduledExecutorServiceImpl defaultMses =
+ ManagedScheduledExecutorServiceImplFactory.lookup("java:comp/DefaultManagedScheduledExecutorService");
+ final ScheduledExecutorService triggerDelegate = defaultMses.getDelegate();
// A single CompletableFuture represents ALL executions in the schedule.
// Per spec: "A single future represents the completion of all executions in the schedule."
@@ -140,8 +146,7 @@ public class AsynchronousInterceptor {
final AtomicReference<ScheduledFuture<?>> scheduledRef = new AtomicReference<>();
final AtomicReference<LastExecution> lastExecutionRef = new AtomicReference<>();
- // Schedule the first execution via the manual trigger loop
- scheduleNextExecution(delegate, snapshot, ctxService, trigger, outerFuture,
+ scheduleNextExecution(triggerDelegate, snapshot, ctxService, trigger, outerFuture,
ctx, isVoid, scheduledRef, lastExecutionRef);
// Cancel the underlying scheduled task when the future completes externally