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 <[email protected]>
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

Reply via email to