Skip to content

[Virtual Threads] Possible Thread Pinning in PartTreeJpaQuery.QueryPreparer#createQuery() #3505

@kzander91

Description

@kzander91

Version: Spring Data JPA 3.3.0.

The method uses a synchronized block, causing thread pinning:

synchronized (this.cachedCriteriaQuery) {
return getEntityManager().createQuery(criteriaQuery);
}

In my application, I'm intercepting calls to EntityManager#createQuery and perform blocking operations in that interceptor. Due to the synchronized block, my virtual thread cannot dismount from the platform thread, therefore pinning it. Running with -Djdk.tracePinnedThreads=full produces stack traces that point to the snippet above:

Thread[#55,ForkJoinPool-1-worker-2,5,CarrierThreads]
    java.base/java.lang.VirtualThread$VThreadContinuation.onPinned(Unknown Source)
    java.base/jdk.internal.vm.Continuation.onPinned0(Unknown Source)
    java.base/java.lang.VirtualThread.park(Unknown Source)
    java.base/java.lang.System$2.parkVirtualThread(Unknown Source)
    java.base/jdk.internal.misc.VirtualThreads.park(Unknown Source)
    java.base/java.util.concurrent.locks.LockSupport.park(Unknown Source)
    java.base/java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(Unknown Source)
    java.base/java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(Unknown Source)
    java.base/java.util.concurrent.CountDownLatch.await(Unknown Source)
    reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:90)
    reactor.core.publisher.Mono.block(Mono.java:1728)
    my.company.MyInterceptor.blockingCall() //---------------  <---- My code here
    java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source)
    java.base/java.lang.reflect.Method.invoke(Unknown Source)
    org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:637)
    org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:620)
    org.springframework.aop.aspectj.AspectJMethodBeforeAdvice.before(AspectJMethodBeforeAdvice.java:44)
    org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:57)
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
    org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
    org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:220)
    jdk.proxy2/jdk.proxy2.$Proxy266.createQuery(Unknown Source)
    org.springframework.data.jpa.repository.query.PartTreeJpaQuery$QueryPreparer.createQuery(PartTreeJpaQuery.java:297) <== monitors:1
    org.springframework.data.jpa.repository.query.PartTreeJpaQuery$QueryPreparer.createQuery(PartTreeJpaQuery.java:242)
    org.springframework.data.jpa.repository.query.PartTreeJpaQuery.doCreateQuery(PartTreeJpaQuery.java:113)
    org.springframework.data.jpa.repository.query.AbstractJpaQuery.createQuery(AbstractJpaQuery.java:239)
    org.springframework.data.jpa.repository.query.JpaQueryExecution$SingleEntityExecution.doExecute(JpaQueryExecution.java:223)
    org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:92)
    org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:149)
    org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:137)
    org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:170)
    org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:158)
    org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:164)
    org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:143)

Please consider replacing this (and other synchronized blocks/methods) with ReentrantLock to avoid thread pinning.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions