Skip to content

Virtual thread pinning in DeploymentResolver due to blocking reload inside synchronized cache lock #996

Description

@nightknight77

Hi, we observed Java virtual-thread pinning in production when using SAP AI SDK Java 1.22.0.

The JVM was started with:

-Djdk.tracePinnedThreads=full

The pinned-thread trace points to DeploymentResolver.resolveDeployment(...):

VirtualThread[#4188,quarkus-virtual-thread-3623]/runnable@ForkJoinPool-1-worker-5 reason:MONITOR
...
java.base/java.lang.VirtualThread$VThreadContinuation.onPinned
...
java.base/java.util.concurrent.FutureTask.get
...
com.sap.ai.sdk.core.DeploymentResolver.resolveDeployment(DeploymentResolver.java:122) <== monitors:1
com.sap.ai.sdk.core.DeploymentResolver.reloadDeployments(DeploymentResolver.java:50)
com.sap.ai.sdk.core.client.DeploymentApi.query(DeploymentApi.java:754)
org.apache.hc.client5.http.impl.classic.CloseableHttpClient.execute(...)

The relevant code appears to perform a blocking deployment reload while holding a monitor:

synchronized (cache) {
  deployment = getCachedDeployment(resourceGroup, predicate);
  if (deployment.isPresent()) {
    return deployment;
  }
  reloadDeployments(resourceGroup);
  return getCachedDeployment(resourceGroup, predicate);
}

reloadDeployments(...) calls:

val deployments = new HashSet<>(apiClient.query(resourceGroup).getResources());

This means a virtual thread can block on network I/O while inside synchronized (cache), causing reason:MONITOR pinning.

Could this cache reload be changed to avoid blocking I/O while holding the monitor, for example by coordinating reload ownership under lock, releasing the lock during DeploymentApi.query(...), and reacquiring only to update the cache?

Thanks.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions