Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid ThreadLocals while using VirtualThreads #39696

Open
franz1981 opened this issue Mar 26, 2024 · 35 comments
Open

Avoid ThreadLocals while using VirtualThreads #39696

franz1981 opened this issue Mar 26, 2024 · 35 comments
Labels
area/virtual-threads Issue related to Java's Virtual Threads kind/bug Something isn't working

Comments

@franz1981
Copy link
Contributor

franz1981 commented Mar 26, 2024

Similarly to FasterXML/jackson-core#919

in quarkus we have few parts which are still using thread locals

eg running the test https://github.com/franz1981/fortune-benchmark/blob/main/fortune-virtual-thread-postgresql/src/test/java/me/escoffier/fortune/virtual/reactive/postgresql/test/FortuneApiTest.java
adding the parameter -Djdk.traceVirtualThreadLocals=true

should report this stack trace (which ca be used in IDEA stack trace analyzer to fix the issues, one by one):

VirtualThread[#172,quarkus-virtual-thread-0]/runnable@ForkJoinPool-1-worker-1    java.base/java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:236)
    java.base/java.lang.ThreadLocal.get(ThreadLocal.java:194)
    java.base/java.lang.ThreadLocal.get(ThreadLocal.java:172)
    io.vertx.core.impl.VertxImpl.beginDispatch2(VertxImpl.java:1372)
    io.vertx.core.impl.VertxImpl.beginDispatch(VertxImpl.java:1366)
    io.vertx.core.impl.ContextInternal.beginDispatch(ContextInternal.java:301)
    io.quarkus.virtual.threads.ContextPreservingExecutorService$ContextPreservingRunnable.run(ContextPreservingExecutorService.java:43)
    java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314)
    java.base/java.lang.VirtualThread.run(VirtualThread.java:311)
VirtualThread[#172,quarkus-virtual-thread-0]/runnable@ForkJoinPool-1-worker-1    java.base/java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:236)
    java.base/java.lang.ThreadLocal.get(ThreadLocal.java:194)
    java.base/java.lang.ThreadLocal.get(ThreadLocal.java:172)
    jdk.zipfs/jdk.nio.zipfs.ZipFileSystem$IndexNode.keyOf(ZipFileSystem.java:2603)
    jdk.zipfs/jdk.nio.zipfs.ZipFileSystem.getInode(ZipFileSystem.java:1899)
    jdk.zipfs/jdk.nio.zipfs.ZipFileSystem.exists(ZipFileSystem.java:658)
    jdk.zipfs/jdk.nio.zipfs.ZipPath.exists(ZipPath.java:905)
    jdk.zipfs/jdk.nio.zipfs.ZipFileSystemProvider.exists(ZipFileSystemProvider.java:197)
    java.base/java.nio.file.Files.exists(Files.java:2514)
    io.quarkus.paths.DirectoryPathTree.apply(DirectoryPathTree.java:103)
    io.quarkus.paths.ArchivePathTree$OpenArchivePathTree.apply(ArchivePathTree.java:262)
    io.quarkus.paths.PathTreeWithManifest.apply(PathTreeWithManifest.java:74)
    io.quarkus.paths.SharedArchivePathTree$CallerOpenPathTree.apply(SharedArchivePathTree.java:153)
    io.quarkus.bootstrap.classloading.PathTreeClassPathElement.lambda$getResource$1(PathTreeClassPathElement.java:106)
    io.quarkus.bootstrap.classloading.PathTreeClassPathElement.apply(PathTreeClassPathElement.java:114)
    io.quarkus.bootstrap.classloading.PathTreeClassPathElement.getResource(PathTreeClassPathElement.java:106)
    io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:504)
    io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:468)
    io.vertx.core.impl.VertxImpl.beginDispatch2(VertxImpl.java:1377)
    io.vertx.core.impl.VertxImpl.beginDispatch(VertxImpl.java:1366)
    io.vertx.core.impl.ContextInternal.beginDispatch(ContextInternal.java:301)
    io.quarkus.virtual.threads.ContextPreservingExecutorService$ContextPreservingRunnable.run(ContextPreservingExecutorService.java:43)
    java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314)
    java.base/java.lang.VirtualThread.run(VirtualThread.java:311)
VirtualThread[#172,quarkus-virtual-thread-0]/runnable@ForkJoinPool-1-worker-1    java.base/java.lang.ThreadLocal.set(ThreadLocal.java:253)
    jdk.zipfs/jdk.nio.zipfs.ZipFileSystem$IndexNode.keyOf(ZipFileSystem.java:2606)
    jdk.zipfs/jdk.nio.zipfs.ZipFileSystem.getInode(ZipFileSystem.java:1899)
    jdk.zipfs/jdk.nio.zipfs.ZipFileSystem.exists(ZipFileSystem.java:658)
    jdk.zipfs/jdk.nio.zipfs.ZipPath.exists(ZipPath.java:905)
    jdk.zipfs/jdk.nio.zipfs.ZipFileSystemProvider.exists(ZipFileSystemProvider.java:197)
    java.base/java.nio.file.Files.exists(Files.java:2514)
    io.quarkus.paths.DirectoryPathTree.apply(DirectoryPathTree.java:103)
    io.quarkus.paths.ArchivePathTree$OpenArchivePathTree.apply(ArchivePathTree.java:262)
    io.quarkus.paths.PathTreeWithManifest.apply(PathTreeWithManifest.java:74)
    io.quarkus.paths.SharedArchivePathTree$CallerOpenPathTree.apply(SharedArchivePathTree.java:153)
    io.quarkus.bootstrap.classloading.PathTreeClassPathElement.lambda$getResource$1(PathTreeClassPathElement.java:106)
    io.quarkus.bootstrap.classloading.PathTreeClassPathElement.apply(PathTreeClassPathElement.java:114)
    io.quarkus.bootstrap.classloading.PathTreeClassPathElement.getResource(PathTreeClassPathElement.java:106)
    io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:504)
    io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:468)
    io.vertx.core.impl.VertxImpl.beginDispatch2(VertxImpl.java:1377)
    io.vertx.core.impl.VertxImpl.beginDispatch(VertxImpl.java:1366)
    io.vertx.core.impl.ContextInternal.beginDispatch(ContextInternal.java:301)
    io.quarkus.virtual.threads.ContextPreservingExecutorService$ContextPreservingRunnable.run(ContextPreservingExecutorService.java:43)
    java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314)
    java.base/java.lang.VirtualThread.run(VirtualThread.java:311)
VirtualThread[#172,quarkus-virtual-thread-0]/runnable@ForkJoinPool-1-worker-1    java.base/java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:236)
    java.base/java.lang.ThreadLocal.get(ThreadLocal.java:194)
    java.base/java.lang.ThreadLocal.getCarrierThreadLocal(ThreadLocal.java:181)
    java.base/java.lang.System$2.getCarrierThreadLocal(System.java:2597)
    java.base/jdk.internal.misc.CarrierThreadLocal.get(CarrierThreadLocal.java:39)
    java.base/jdk.internal.misc.TerminatingThreadLocal.register(TerminatingThreadLocal.java:83)
    java.base/java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:233)
    java.base/java.lang.ThreadLocal.get(ThreadLocal.java:194)
    java.base/java.lang.ThreadLocal.getCarrierThreadLocal(ThreadLocal.java:181)
    java.base/java.lang.System$2.getCarrierThreadLocal(System.java:2597)
    java.base/jdk.internal.misc.CarrierThreadLocal.get(CarrierThreadLocal.java:39)
    java.base/sun.nio.ch.Util.getTemporaryDirectBuffer(Util.java:230)
    java.base/sun.nio.ch.IOUtil.read(IOUtil.java:303)
    java.base/sun.nio.ch.IOUtil.read(IOUtil.java:283)
    java.base/sun.nio.ch.FileChannelImpl.readInternal(FileChannelImpl.java:984)
    java.base/sun.nio.ch.FileChannelImpl.read(FileChannelImpl.java:967)
    jdk.zipfs/jdk.nio.zipfs.ZipFileSystem.readFullyAt(ZipFileSystem.java:1241)
    jdk.zipfs/jdk.nio.zipfs.ZipFileSystem.readFullyAt(ZipFileSystem.java:1236)
    jdk.zipfs/jdk.nio.zipfs.ZipFileSystem$EntryInputStream.initDataPos(ZipFileSystem.java:2386)
    jdk.zipfs/jdk.nio.zipfs.ZipFileSystem$EntryInputStream.read(ZipFileSystem.java:2328)
    jdk.zipfs/jdk.nio.zipfs.ZipFileSystem$2.fill(ZipFileSystem.java:2278)
    java.base/java.util.zip.InflaterInputStream.read(InflaterInputStream.java:175)
    java.base/java.io.InputStream.readNBytes(InputStream.java:412)
    java.base/java.io.InputStream.readAllBytes(InputStream.java:349)
    jdk.zipfs/jdk.nio.zipfs.ZipFileSystem.newByteChannel(ZipFileSystem.java:977)
    jdk.zipfs/jdk.nio.zipfs.ZipPath.newByteChannel(ZipPath.java:870)
    jdk.zipfs/jdk.nio.zipfs.ZipFileSystemProvider.newByteChannel(ZipFileSystemProvider.java:247)
    java.base/java.nio.file.Files.newByteChannel(Files.java:379)
    java.base/java.nio.file.Files.newByteChannel(Files.java:431)
    java.base/java.nio.file.Files.readAllBytes(Files.java:3268)
    io.quarkus.bootstrap.classloading.PathTreeClassPathElement$Resource.getData(PathTreeClassPathElement.java:269)
    io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:506)
    io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:468)
    io.vertx.core.impl.VertxImpl.beginDispatch2(VertxImpl.java:1377)
    io.vertx.core.impl.VertxImpl.beginDispatch(VertxImpl.java:1366)
    io.vertx.core.impl.ContextInternal.beginDispatch(ContextInternal.java:301)
    io.quarkus.virtual.threads.ContextPreservingExecutorService$ContextPreservingRunnable.run(ContextPreservingExecutorService.java:43)
    java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314)
    java.base/java.lang.VirtualThread.run(VirtualThread.java:311)
VirtualThread[#172,quarkus-virtual-thread-0]/runnable@ForkJoinPool-1-worker-1    java.base/java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:236)
    java.base/java.lang.ThreadLocal.get(ThreadLocal.java:194)
    java.base/java.lang.ThreadLocal.getCarrierThreadLocal(ThreadLocal.java:181)
    java.base/java.lang.System$2.getCarrierThreadLocal(System.java:2597)
    java.base/jdk.internal.misc.CarrierThreadLocal.get(CarrierThreadLocal.java:39)
    java.base/sun.nio.ch.Util.getTemporaryDirectBuffer(Util.java:230)
    java.base/sun.nio.ch.IOUtil.read(IOUtil.java:303)
    java.base/sun.nio.ch.IOUtil.read(IOUtil.java:283)
    java.base/sun.nio.ch.FileChannelImpl.readInternal(FileChannelImpl.java:984)
    java.base/sun.nio.ch.FileChannelImpl.read(FileChannelImpl.java:967)
    jdk.zipfs/jdk.nio.zipfs.ZipFileSystem.readFullyAt(ZipFileSystem.java:1241)
    jdk.zipfs/jdk.nio.zipfs.ZipFileSystem.readFullyAt(ZipFileSystem.java:1236)
    jdk.zipfs/jdk.nio.zipfs.ZipFileSystem$EntryInputStream.initDataPos(ZipFileSystem.java:2386)
    jdk.zipfs/jdk.nio.zipfs.ZipFileSystem$EntryInputStream.read(ZipFileSystem.java:2328)
    jdk.zipfs/jdk.nio.zipfs.ZipFileSystem$2.fill(ZipFileSystem.java:2278)
    java.base/java.util.zip.InflaterInputStream.read(InflaterInputStream.java:175)
    java.base/java.io.InputStream.readNBytes(InputStream.java:412)
    java.base/java.io.InputStream.readAllBytes(InputStream.java:349)
    jdk.zipfs/jdk.nio.zipfs.ZipFileSystem.newByteChannel(ZipFileSystem.java:977)
    jdk.zipfs/jdk.nio.zipfs.ZipPath.newByteChannel(ZipPath.java:870)
    jdk.zipfs/jdk.nio.zipfs.ZipFileSystemProvider.newByteChannel(ZipFileSystemProvider.java:247)
    java.base/java.nio.file.Files.newByteChannel(Files.java:379)
    java.base/java.nio.file.Files.newByteChannel(Files.java:431)
    java.base/java.nio.file.Files.readAllBytes(Files.java:3268)
    io.quarkus.bootstrap.classloading.PathTreeClassPathElement$Resource.getData(PathTreeClassPathElement.java:269)
    io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:506)
    io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:468)
    io.vertx.core.impl.VertxImpl.beginDispatch2(VertxImpl.java:1377)
    io.vertx.core.impl.VertxImpl.beginDispatch(VertxImpl.java:1366)
    io.vertx.core.impl.ContextInternal.beginDispatch(ContextInternal.java:301)
    io.quarkus.virtual.threads.ContextPreservingExecutorService$ContextPreservingRunnable.run(ContextPreservingExecutorService.java:43)
    java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314)
    java.base/java.lang.VirtualThread.run(VirtualThread.java:311)
VirtualThread[#172,quarkus-virtual-thread-0]/runnable@ForkJoinPool-1-worker-1    java.base/java.lang.ThreadLocal.set(ThreadLocal.java:253)
    io.vertx.core.impl.VertxImpl.beginDispatch2(VertxImpl.java:1378)
    io.vertx.core.impl.VertxImpl.beginDispatch(VertxImpl.java:1366)
    io.vertx.core.impl.ContextInternal.beginDispatch(ContextInternal.java:301)
    io.quarkus.virtual.threads.ContextPreservingExecutorService$ContextPreservingRunnable.run(ContextPreservingExecutorService.java:43)
    java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314)
    java.base/java.lang.VirtualThread.run(VirtualThread.java:311)
VirtualThread[#172,quarkus-virtual-thread-0]/runnable@ForkJoinPool-1-worker-1    java.base/java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:236)
    java.base/java.lang.ThreadLocal.get(ThreadLocal.java:194)
    java.base/java.lang.ThreadLocal.get(ThreadLocal.java:172)
    io.quarkus.arc.impl.InjectionPointProvider.set(InjectionPointProvider.java:29)
    io.quarkus.arc.impl.ArcContainerImpl.beanInstanceHandle(ArcContainerImpl.java:556)
    io.quarkus.arc.impl.ArcContainerImpl.beanInstanceHandle(ArcContainerImpl.java:539)
    io.quarkus.arc.impl.ArcContainerImpl.beanInstanceHandle(ArcContainerImpl.java:572)
    io.quarkus.arc.impl.ArcContainerImpl$3.get(ArcContainerImpl.java:331)
    io.quarkus.arc.impl.ArcContainerImpl$3.get(ArcContainerImpl.java:328)
    io.quarkus.arc.runtime.BeanContainerImpl$1.create(BeanContainerImpl.java:58)
    io.quarkus.resteasy.reactive.common.runtime.ArcBeanFactory.createInstance(ArcBeanFactory.java:27)
    org.jboss.resteasy.reactive.server.handlers.InstanceHandler.handle(InstanceHandler.java:26)
    io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:139)
    org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
    io.quarkus.virtual.threads.ContextPreservingExecutorService$ContextPreservingRunnable.run(ContextPreservingExecutorService.java:45)
    java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314)
    java.base/java.lang.VirtualThread.run(VirtualThread.java:311)
VirtualThread[#172,quarkus-virtual-thread-0]/runnable@ForkJoinPool-1-worker-1    java.base/java.lang.ThreadLocal.set(ThreadLocal.java:253)
    io.quarkus.arc.impl.InjectionPointProvider.set(InjectionPointProvider.java:33)
    io.quarkus.arc.impl.ArcContainerImpl.beanInstanceHandle(ArcContainerImpl.java:556)
    io.quarkus.arc.impl.ArcContainerImpl.beanInstanceHandle(ArcContainerImpl.java:539)
    io.quarkus.arc.impl.ArcContainerImpl.beanInstanceHandle(ArcContainerImpl.java:572)
    io.quarkus.arc.impl.ArcContainerImpl$3.get(ArcContainerImpl.java:331)
    io.quarkus.arc.impl.ArcContainerImpl$3.get(ArcContainerImpl.java:328)
    io.quarkus.arc.runtime.BeanContainerImpl$1.create(BeanContainerImpl.java:58)
    io.quarkus.resteasy.reactive.common.runtime.ArcBeanFactory.createInstance(ArcBeanFactory.java:27)
    org.jboss.resteasy.reactive.server.handlers.InstanceHandler.handle(InstanceHandler.java:26)
    io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:139)
    org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
    io.quarkus.virtual.threads.ContextPreservingExecutorService$ContextPreservingRunnable.run(ContextPreservingExecutorService.java:45)
    java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314)
    java.base/java.lang.VirtualThread.run(VirtualThread.java:311)
VirtualThread[#172,quarkus-virtual-thread-0]/runnable@ForkJoinPool-1-worker-1    java.base/java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:236)
    java.base/java.lang.ThreadLocal.get(ThreadLocal.java:194)
    java.base/java.lang.ThreadLocal.get(ThreadLocal.java:172)
    io.smallrye.context.SmallRyeThreadContext.getCurrentThreadContext(SmallRyeThreadContext.java:194)
    io.smallrye.context.SmallRyeThreadContext.getCurrentThreadContextOrDefaultContexts(SmallRyeThreadContext.java:160)
    io.smallrye.mutiny.context.DefaultContextPropagationInterceptor.getThreadContext(DefaultContextPropagationInterceptor.java:12)
    io.smallrye.mutiny.context.BaseContextPropagationInterceptor.decorate(BaseContextPropagationInterceptor.java:33)
    io.smallrye.mutiny.infrastructure.Infrastructure.decorate(Infrastructure.java:152)
    io.smallrye.mutiny.vertx.AsyncResultUni.<init>(AsyncResultUni.java:21)
    io.smallrye.mutiny.vertx.AsyncResultUni.toUni(AsyncResultUni.java:17)
    io.vertx.mutiny.sqlclient.Pool.getConnection(Pool.java:115)
    io.vertx.mutiny.pgclient.PgPool_Ly1M_jXJtinTvrqFDvbMm7TSXGc_Synthetic_ClientProxy.getConnection(Unknown Source)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneRepository.listAll(FortuneRepository.java:53)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneRepository_ClientProxy.listAll(Unknown Source)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneResource.getAllFortunes(FortuneResource.java:22)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneResource$quarkusrestinvoker$getAllFortunes_17630c6d41f1fd48ab8b75db07f293217742deee.invoke(Unknown Source)
    org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
    io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141)
    org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
    io.quarkus.virtual.threads.ContextPreservingExecutorService$ContextPreservingRunnable.run(ContextPreservingExecutorService.java:45)
    java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314)
    java.base/java.lang.VirtualThread.run(VirtualThread.java:311)
VirtualThread[#172,quarkus-virtual-thread-0]/runnable@ForkJoinPool-1-worker-1    java.base/java.lang.ThreadLocal.set(ThreadLocal.java:253)
    io.smallrye.context.SmallRyeThreadContext.withThreadContext(SmallRyeThreadContext.java:93)
    io.smallrye.context.impl.SlowActiveContextState.<init>(SlowActiveContextState.java:31)
    io.smallrye.context.impl.SlowCapturedContextState.begin(SlowCapturedContextState.java:34)
    io.smallrye.context.impl.SlowCapturedContextState.begin(SlowCapturedContextState.java:13)
    io.smallrye.context.impl.wrappers.SlowContextualConsumer.accept(SlowContextualConsumer.java:20)
    io.smallrye.mutiny.vertx.AsyncResultUni.subscribe(AsyncResultUni.java:31)
    io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
    io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni.subscribe(UniOnItemTransformToUni.java:25)
    io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
    io.smallrye.mutiny.operators.uni.UniBlockingAwait.await(UniBlockingAwait.java:60)
    io.smallrye.mutiny.groups.UniAwait.atMost(UniAwait.java:65)
    io.smallrye.mutiny.groups.UniAwait.indefinitely(UniAwait.java:46)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneRepository.listAll(FortuneRepository.java:62)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneRepository_ClientProxy.listAll(Unknown Source)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneResource.getAllFortunes(FortuneResource.java:22)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneResource$quarkusrestinvoker$getAllFortunes_17630c6d41f1fd48ab8b75db07f293217742deee.invoke(Unknown Source)
    org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
    io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141)
    org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
    io.quarkus.virtual.threads.ContextPreservingExecutorService$ContextPreservingRunnable.run(ContextPreservingExecutorService.java:45)
    java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314)
    java.base/java.lang.VirtualThread.run(VirtualThread.java:311)
VirtualThread[#172,quarkus-virtual-thread-0]/runnable@ForkJoinPool-1-worker-1    java.base/java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:236)
    java.base/java.lang.ThreadLocal.get(ThreadLocal.java:194)
    java.base/java.lang.ThreadLocal.get(ThreadLocal.java:172)
    io.netty.util.internal.InternalThreadLocalMap.slowGet(InternalThreadLocalMap.java:130)
    io.netty.util.internal.InternalThreadLocalMap.get(InternalThreadLocalMap.java:117)
    io.netty.util.concurrent.FastThreadLocal.get(FastThreadLocal.java:136)
    io.vertx.core.net.impl.pool.CombinerExecutor.submit(CombinerExecutor.java:85)
    io.vertx.core.net.impl.pool.SimpleConnectionPool.execute(SimpleConnectionPool.java:244)
    io.vertx.core.net.impl.pool.SimpleConnectionPool.acquire(SimpleConnectionPool.java:639)
    io.vertx.sqlclient.impl.pool.SqlConnectionPool.acquire(SqlConnectionPool.java:239)
    io.vertx.sqlclient.impl.PoolImpl.acquire(PoolImpl.java:176)
    io.vertx.sqlclient.impl.PoolImpl.getConnection(PoolImpl.java:139)
    io.vertx.sqlclient.impl.PoolImpl.getConnection(PoolImpl.java:126)
    io.vertx.sqlclient.impl.PoolBase.getConnection(PoolBase.java:56)
    io.vertx.pgclient.PgPool_7weXgYHcaRck_k2l7dGKfXZMdD8_Synthetic_ClientProxy.getConnection(Unknown Source)
    io.vertx.mutiny.sqlclient.Pool.lambda$getConnection$11(Pool.java:116)
    io.smallrye.context.impl.wrappers.SlowContextualConsumer.accept(SlowContextualConsumer.java:21)
    io.smallrye.mutiny.vertx.AsyncResultUni.subscribe(AsyncResultUni.java:31)
    io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
    io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni.subscribe(UniOnItemTransformToUni.java:25)
    io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
    io.smallrye.mutiny.operators.uni.UniBlockingAwait.await(UniBlockingAwait.java:60)
    io.smallrye.mutiny.groups.UniAwait.atMost(UniAwait.java:65)
    io.smallrye.mutiny.groups.UniAwait.indefinitely(UniAwait.java:46)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneRepository.listAll(FortuneRepository.java:62)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneRepository_ClientProxy.listAll(Unknown Source)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneResource.getAllFortunes(FortuneResource.java:22)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneResource$quarkusrestinvoker$getAllFortunes_17630c6d41f1fd48ab8b75db07f293217742deee.invoke(Unknown Source)
    org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
    io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141)
    org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
    io.quarkus.virtual.threads.ContextPreservingExecutorService$ContextPreservingRunnable.run(ContextPreservingExecutorService.java:45)
    java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314)
    java.base/java.lang.VirtualThread.run(VirtualThread.java:311)
VirtualThread[#172,quarkus-virtual-thread-0]/runnable@ForkJoinPool-1-worker-1    java.base/java.lang.ThreadLocal.set(ThreadLocal.java:253)
    io.netty.util.internal.InternalThreadLocalMap.slowGet(InternalThreadLocalMap.java:133)
    io.netty.util.internal.InternalThreadLocalMap.get(InternalThreadLocalMap.java:117)
    io.netty.util.concurrent.FastThreadLocal.get(FastThreadLocal.java:136)
    io.vertx.core.net.impl.pool.CombinerExecutor.submit(CombinerExecutor.java:85)
    io.vertx.core.net.impl.pool.SimpleConnectionPool.execute(SimpleConnectionPool.java:244)
    io.vertx.core.net.impl.pool.SimpleConnectionPool.acquire(SimpleConnectionPool.java:639)
    io.vertx.sqlclient.impl.pool.SqlConnectionPool.acquire(SqlConnectionPool.java:239)
    io.vertx.sqlclient.impl.PoolImpl.acquire(PoolImpl.java:176)
    io.vertx.sqlclient.impl.PoolImpl.getConnection(PoolImpl.java:139)
    io.vertx.sqlclient.impl.PoolImpl.getConnection(PoolImpl.java:126)
    io.vertx.sqlclient.impl.PoolBase.getConnection(PoolBase.java:56)
    io.vertx.pgclient.PgPool_7weXgYHcaRck_k2l7dGKfXZMdD8_Synthetic_ClientProxy.getConnection(Unknown Source)
    io.vertx.mutiny.sqlclient.Pool.lambda$getConnection$11(Pool.java:116)
    io.smallrye.context.impl.wrappers.SlowContextualConsumer.accept(SlowContextualConsumer.java:21)
    io.smallrye.mutiny.vertx.AsyncResultUni.subscribe(AsyncResultUni.java:31)
    io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
    io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni.subscribe(UniOnItemTransformToUni.java:25)
    io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
    io.smallrye.mutiny.operators.uni.UniBlockingAwait.await(UniBlockingAwait.java:60)
    io.smallrye.mutiny.groups.UniAwait.atMost(UniAwait.java:65)
    io.smallrye.mutiny.groups.UniAwait.indefinitely(UniAwait.java:46)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneRepository.listAll(FortuneRepository.java:62)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneRepository_ClientProxy.listAll(Unknown Source)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneResource.getAllFortunes(FortuneResource.java:22)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneResource$quarkusrestinvoker$getAllFortunes_17630c6d41f1fd48ab8b75db07f293217742deee.invoke(Unknown Source)
    org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
    io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141)
    org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
    io.quarkus.virtual.threads.ContextPreservingExecutorService$ContextPreservingRunnable.run(ContextPreservingExecutorService.java:45)
    java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314)
    java.base/java.lang.VirtualThread.run(VirtualThread.java:311)
VirtualThread[#172,quarkus-virtual-thread-0]/runnable@ForkJoinPool-1-worker-1    java.base/java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:236)
    java.base/java.lang.ThreadLocal.get(ThreadLocal.java:194)
    java.base/java.lang.ThreadLocal.get(ThreadLocal.java:172)
    com.intellij.rt.debugger.agent.CaptureStorage.capture(CaptureStorage.java:43)
    io.netty.util.concurrent.SingleThreadEventExecutor.execute0(SingleThreadEventExecutor.java)
    io.netty.util.concurrent.SingleThreadEventExecutor.execute(SingleThreadEventExecutor.java:817)
    io.netty.util.concurrent.AbstractScheduledEventExecutor.schedule(AbstractScheduledEventExecutor.java:290)
    io.netty.util.concurrent.AbstractScheduledEventExecutor.schedule(AbstractScheduledEventExecutor.java:205)
    io.vertx.core.impl.VertxImpl.scheduleTimeout(VertxImpl.java:667)
    io.vertx.core.impl.VertxImpl.scheduleTimeout(VertxImpl.java:678)
    io.vertx.core.impl.ContextInternal.setTimer(ContextInternal.java:478)
    io.vertx.sqlclient.impl.pool.SqlConnectionPool$1PoolRequest.onEnqueue(SqlConnectionPool.java:219)
    io.vertx.sqlclient.impl.pool.SqlConnectionPool$1PoolRequest.onConnect(SqlConnectionPool.java:235)
    io.vertx.core.net.impl.pool.SimpleConnectionPool$Acquire$3.run(SimpleConnectionPool.java:593)
    io.vertx.core.net.impl.pool.Task.runNextTasks(Task.java:43)
    io.vertx.core.net.impl.pool.CombinerExecutor.submit(CombinerExecutor.java:91)
    io.vertx.core.net.impl.pool.SimpleConnectionPool.execute(SimpleConnectionPool.java:244)
    io.vertx.core.net.impl.pool.SimpleConnectionPool.acquire(SimpleConnectionPool.java:639)
    io.vertx.sqlclient.impl.pool.SqlConnectionPool.acquire(SqlConnectionPool.java:239)
    io.vertx.sqlclient.impl.PoolImpl.acquire(PoolImpl.java:176)
    io.vertx.sqlclient.impl.PoolImpl.getConnection(PoolImpl.java:139)
    io.vertx.sqlclient.impl.PoolImpl.getConnection(PoolImpl.java:126)
    io.vertx.sqlclient.impl.PoolBase.getConnection(PoolBase.java:56)
    io.vertx.pgclient.PgPool_7weXgYHcaRck_k2l7dGKfXZMdD8_Synthetic_ClientProxy.getConnection(Unknown Source)
    io.vertx.mutiny.sqlclient.Pool.lambda$getConnection$11(Pool.java:116)
    io.smallrye.context.impl.wrappers.SlowContextualConsumer.accept(SlowContextualConsumer.java:21)
    io.smallrye.mutiny.vertx.AsyncResultUni.subscribe(AsyncResultUni.java:31)
    io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
    io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni.subscribe(UniOnItemTransformToUni.java:25)
    io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
    io.smallrye.mutiny.operators.uni.UniBlockingAwait.await(UniBlockingAwait.java:60)
    io.smallrye.mutiny.groups.UniAwait.atMost(UniAwait.java:65)
    io.smallrye.mutiny.groups.UniAwait.indefinitely(UniAwait.java:46)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneRepository.listAll(FortuneRepository.java:62)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneRepository_ClientProxy.listAll(Unknown Source)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneResource.getAllFortunes(FortuneResource.java:22)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneResource$quarkusrestinvoker$getAllFortunes_17630c6d41f1fd48ab8b75db07f293217742deee.invoke(Unknown Source)
    org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
    io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141)
    org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
    io.quarkus.virtual.threads.ContextPreservingExecutorService$ContextPreservingRunnable.run(ContextPreservingExecutorService.java:45)
    java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314)
    java.base/java.lang.VirtualThread.run(VirtualThread.java:311)

I think JDK 21 have a BUG on the mentioned property, because the stacks which contains

java.base/java.lang.ThreadLocal.getCarrierThreadLocal(ThreadLocal.java:181)

are absolutely fine.
This is a sign that most users didn't used this feature to make sure are not using thread locals on virtual thread(s) OR that loom is not that used (yet, which makes sense) OR that thread local(s) is not widely used (which is unlikely, really).

Instead, https://github.com/openjdk/jdk/blob/b9c76dedf4aa2248a5e561a535c9e3e181f7836a/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java#L2799 shows that OpenJDK still have to fix its own ThreadLocal usages as well, although, is not a big deal per-se, given that the number of class-loading operations should not keep on increasing during a run, in theory.

@franz1981 franz1981 added the kind/bug Something isn't working label Mar 26, 2024
@franz1981
Copy link
Contributor Author

franz1981 commented Mar 26, 2024

adding @vietj and @jponge because some of these are on both Mutiny and Vertx.

Adding @mkouba because Arc seems affected the same.

@franz1981
Copy link
Contributor Author

This is the reduced list, ignoring things which belong to OpenJDK instead, and we have not much control of:

VirtualThread[#172,quarkus-virtual-thread-0]/runnable@ForkJoinPool-1-worker-1
    java.base/java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:236)
    java.base/java.lang.ThreadLocal.get(ThreadLocal.java:194)
    java.base/java.lang.ThreadLocal.get(ThreadLocal.java:172)
    io.vertx.core.impl.VertxImpl.beginDispatch2(VertxImpl.java:1372)
    io.vertx.core.impl.VertxImpl.beginDispatch(VertxImpl.java:1366)
    io.vertx.core.impl.ContextInternal.beginDispatch(ContextInternal.java:301)
    io.quarkus.virtual.threads.ContextPreservingExecutorService$ContextPreservingRunnable.run(ContextPreservingExecutorService.java:43)
    java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314)
    java.base/java.lang.VirtualThread.run(VirtualThread.java:311)
VirtualThread[#172,quarkus-virtual-thread-0]/runnable@ForkJoinPool-1-worker-1
    java.base/java.lang.ThreadLocal.set(ThreadLocal.java:253)
    io.vertx.core.impl.VertxImpl.beginDispatch2(VertxImpl.java:1378)
    io.vertx.core.impl.VertxImpl.beginDispatch(VertxImpl.java:1366)
    io.vertx.core.impl.ContextInternal.beginDispatch(ContextInternal.java:301)
    io.quarkus.virtual.threads.ContextPreservingExecutorService$ContextPreservingRunnable.run(ContextPreservingExecutorService.java:43)
    java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314)
    java.base/java.lang.VirtualThread.run(VirtualThread.java:311)
VirtualThread[#172,quarkus-virtual-thread-0]/runnable@ForkJoinPool-1-worker-1
    java.base/java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:236)
    java.base/java.lang.ThreadLocal.get(ThreadLocal.java:194)
    java.base/java.lang.ThreadLocal.get(ThreadLocal.java:172)
    io.quarkus.arc.impl.InjectionPointProvider.set(InjectionPointProvider.java:29)
    io.quarkus.arc.impl.ArcContainerImpl.beanInstanceHandle(ArcContainerImpl.java:556)
    io.quarkus.arc.impl.ArcContainerImpl.beanInstanceHandle(ArcContainerImpl.java:539)
    io.quarkus.arc.impl.ArcContainerImpl.beanInstanceHandle(ArcContainerImpl.java:572)
    io.quarkus.arc.impl.ArcContainerImpl$3.get(ArcContainerImpl.java:331)
    io.quarkus.arc.impl.ArcContainerImpl$3.get(ArcContainerImpl.java:328)
    io.quarkus.arc.runtime.BeanContainerImpl$1.create(BeanContainerImpl.java:58)
    io.quarkus.resteasy.reactive.common.runtime.ArcBeanFactory.createInstance(ArcBeanFactory.java:27)
    org.jboss.resteasy.reactive.server.handlers.InstanceHandler.handle(InstanceHandler.java:26)
    io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:139)
    org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
    io.quarkus.virtual.threads.ContextPreservingExecutorService$ContextPreservingRunnable.run(ContextPreservingExecutorService.java:45)
    java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314)
    java.base/java.lang.VirtualThread.run(VirtualThread.java:311)
VirtualThread[#172,quarkus-virtual-thread-0]/runnable@ForkJoinPool-1-worker-1
    java.base/java.lang.ThreadLocal.set(ThreadLocal.java:253)
    io.quarkus.arc.impl.InjectionPointProvider.set(InjectionPointProvider.java:33)
    io.quarkus.arc.impl.ArcContainerImpl.beanInstanceHandle(ArcContainerImpl.java:556)
    io.quarkus.arc.impl.ArcContainerImpl.beanInstanceHandle(ArcContainerImpl.java:539)
    io.quarkus.arc.impl.ArcContainerImpl.beanInstanceHandle(ArcContainerImpl.java:572)
    io.quarkus.arc.impl.ArcContainerImpl$3.get(ArcContainerImpl.java:331)
    io.quarkus.arc.impl.ArcContainerImpl$3.get(ArcContainerImpl.java:328)
    io.quarkus.arc.runtime.BeanContainerImpl$1.create(BeanContainerImpl.java:58)
    io.quarkus.resteasy.reactive.common.runtime.ArcBeanFactory.createInstance(ArcBeanFactory.java:27)
    org.jboss.resteasy.reactive.server.handlers.InstanceHandler.handle(InstanceHandler.java:26)
    io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:139)
    org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
    io.quarkus.virtual.threads.ContextPreservingExecutorService$ContextPreservingRunnable.run(ContextPreservingExecutorService.java:45)
    java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314)
    java.base/java.lang.VirtualThread.run(VirtualThread.java:311)
VirtualThread[#172,quarkus-virtual-thread-0]/runnable@ForkJoinPool-1-worker-1
    java.base/java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:236)
    java.base/java.lang.ThreadLocal.get(ThreadLocal.java:194)
    java.base/java.lang.ThreadLocal.get(ThreadLocal.java:172)
    io.smallrye.context.SmallRyeThreadContext.getCurrentThreadContext(SmallRyeThreadContext.java:194)
    io.smallrye.context.SmallRyeThreadContext.getCurrentThreadContextOrDefaultContexts(SmallRyeThreadContext.java:160)
    io.smallrye.mutiny.context.DefaultContextPropagationInterceptor.getThreadContext(DefaultContextPropagationInterceptor.java:12)
    io.smallrye.mutiny.context.BaseContextPropagationInterceptor.decorate(BaseContextPropagationInterceptor.java:33)
    io.smallrye.mutiny.infrastructure.Infrastructure.decorate(Infrastructure.java:152)
    io.smallrye.mutiny.vertx.AsyncResultUni.<init>(AsyncResultUni.java:21)
    io.smallrye.mutiny.vertx.AsyncResultUni.toUni(AsyncResultUni.java:17)
    io.vertx.mutiny.sqlclient.Pool.getConnection(Pool.java:115)
    io.vertx.mutiny.pgclient.PgPool_Ly1M_jXJtinTvrqFDvbMm7TSXGc_Synthetic_ClientProxy.getConnection(Unknown Source)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneRepository.listAll(FortuneRepository.java:53)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneRepository_ClientProxy.listAll(Unknown Source)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneResource.getAllFortunes(FortuneResource.java:22)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneResource$quarkusrestinvoker$getAllFortunes_17630c6d41f1fd48ab8b75db07f293217742deee.invoke(Unknown Source)
    org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
    io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141)
    org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
    io.quarkus.virtual.threads.ContextPreservingExecutorService$ContextPreservingRunnable.run(ContextPreservingExecutorService.java:45)
    java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314)
    java.base/java.lang.VirtualThread.run(VirtualThread.java:311)
VirtualThread[#172,quarkus-virtual-thread-0]/runnable@ForkJoinPool-1-worker-1
    java.base/java.lang.ThreadLocal.set(ThreadLocal.java:253)
    io.smallrye.context.SmallRyeThreadContext.withThreadContext(SmallRyeThreadContext.java:93)
    io.smallrye.context.impl.SlowActiveContextState.<init>(SlowActiveContextState.java:31)
    io.smallrye.context.impl.SlowCapturedContextState.begin(SlowCapturedContextState.java:34)
    io.smallrye.context.impl.SlowCapturedContextState.begin(SlowCapturedContextState.java:13)
    io.smallrye.context.impl.wrappers.SlowContextualConsumer.accept(SlowContextualConsumer.java:20)
    io.smallrye.mutiny.vertx.AsyncResultUni.subscribe(AsyncResultUni.java:31)
    io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
    io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni.subscribe(UniOnItemTransformToUni.java:25)
    io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
    io.smallrye.mutiny.operators.uni.UniBlockingAwait.await(UniBlockingAwait.java:60)
    io.smallrye.mutiny.groups.UniAwait.atMost(UniAwait.java:65)
    io.smallrye.mutiny.groups.UniAwait.indefinitely(UniAwait.java:46)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneRepository.listAll(FortuneRepository.java:62)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneRepository_ClientProxy.listAll(Unknown Source)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneResource.getAllFortunes(FortuneResource.java:22)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneResource$quarkusrestinvoker$getAllFortunes_17630c6d41f1fd48ab8b75db07f293217742deee.invoke(Unknown Source)
    org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
    io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141)
    org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
    io.quarkus.virtual.threads.ContextPreservingExecutorService$ContextPreservingRunnable.run(ContextPreservingExecutorService.java:45)
    java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314)
    java.base/java.lang.VirtualThread.run(VirtualThread.java:311)
VirtualThread[#172,quarkus-virtual-thread-0]/runnable@ForkJoinPool-1-worker-1
    java.base/java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:236)
    java.base/java.lang.ThreadLocal.get(ThreadLocal.java:194)
    java.base/java.lang.ThreadLocal.get(ThreadLocal.java:172)
    io.netty.util.internal.InternalThreadLocalMap.slowGet(InternalThreadLocalMap.java:130)
    io.netty.util.internal.InternalThreadLocalMap.get(InternalThreadLocalMap.java:117)
    io.netty.util.concurrent.FastThreadLocal.get(FastThreadLocal.java:136)
    io.vertx.core.net.impl.pool.CombinerExecutor.submit(CombinerExecutor.java:85)
    io.vertx.core.net.impl.pool.SimpleConnectionPool.execute(SimpleConnectionPool.java:244)
    io.vertx.core.net.impl.pool.SimpleConnectionPool.acquire(SimpleConnectionPool.java:639)
    io.vertx.sqlclient.impl.pool.SqlConnectionPool.acquire(SqlConnectionPool.java:239)
    io.vertx.sqlclient.impl.PoolImpl.acquire(PoolImpl.java:176)
    io.vertx.sqlclient.impl.PoolImpl.getConnection(PoolImpl.java:139)
    io.vertx.sqlclient.impl.PoolImpl.getConnection(PoolImpl.java:126)
    io.vertx.sqlclient.impl.PoolBase.getConnection(PoolBase.java:56)
    io.vertx.pgclient.PgPool_7weXgYHcaRck_k2l7dGKfXZMdD8_Synthetic_ClientProxy.getConnection(Unknown Source)
    io.vertx.mutiny.sqlclient.Pool.lambda$getConnection$11(Pool.java:116)
    io.smallrye.context.impl.wrappers.SlowContextualConsumer.accept(SlowContextualConsumer.java:21)
    io.smallrye.mutiny.vertx.AsyncResultUni.subscribe(AsyncResultUni.java:31)
    io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
    io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni.subscribe(UniOnItemTransformToUni.java:25)
    io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
    io.smallrye.mutiny.operators.uni.UniBlockingAwait.await(UniBlockingAwait.java:60)
    io.smallrye.mutiny.groups.UniAwait.atMost(UniAwait.java:65)
    io.smallrye.mutiny.groups.UniAwait.indefinitely(UniAwait.java:46)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneRepository.listAll(FortuneRepository.java:62)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneRepository_ClientProxy.listAll(Unknown Source)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneResource.getAllFortunes(FortuneResource.java:22)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneResource$quarkusrestinvoker$getAllFortunes_17630c6d41f1fd48ab8b75db07f293217742deee.invoke(Unknown Source)
    org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
    io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141)
    org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
    io.quarkus.virtual.threads.ContextPreservingExecutorService$ContextPreservingRunnable.run(ContextPreservingExecutorService.java:45)
    java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314)
    java.base/java.lang.VirtualThread.run(VirtualThread.java:311)
VirtualThread[#172,quarkus-virtual-thread-0]/runnable@ForkJoinPool-1-worker-1
    java.base/java.lang.ThreadLocal.set(ThreadLocal.java:253)
    io.netty.util.internal.InternalThreadLocalMap.slowGet(InternalThreadLocalMap.java:133)
    io.netty.util.internal.InternalThreadLocalMap.get(InternalThreadLocalMap.java:117)
    io.netty.util.concurrent.FastThreadLocal.get(FastThreadLocal.java:136)
    io.vertx.core.net.impl.pool.CombinerExecutor.submit(CombinerExecutor.java:85)
    io.vertx.core.net.impl.pool.SimpleConnectionPool.execute(SimpleConnectionPool.java:244)
    io.vertx.core.net.impl.pool.SimpleConnectionPool.acquire(SimpleConnectionPool.java:639)
    io.vertx.sqlclient.impl.pool.SqlConnectionPool.acquire(SqlConnectionPool.java:239)
    io.vertx.sqlclient.impl.PoolImpl.acquire(PoolImpl.java:176)
    io.vertx.sqlclient.impl.PoolImpl.getConnection(PoolImpl.java:139)
    io.vertx.sqlclient.impl.PoolImpl.getConnection(PoolImpl.java:126)
    io.vertx.sqlclient.impl.PoolBase.getConnection(PoolBase.java:56)
    io.vertx.pgclient.PgPool_7weXgYHcaRck_k2l7dGKfXZMdD8_Synthetic_ClientProxy.getConnection(Unknown Source)
    io.vertx.mutiny.sqlclient.Pool.lambda$getConnection$11(Pool.java:116)
    io.smallrye.context.impl.wrappers.SlowContextualConsumer.accept(SlowContextualConsumer.java:21)
    io.smallrye.mutiny.vertx.AsyncResultUni.subscribe(AsyncResultUni.java:31)
    io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
    io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni.subscribe(UniOnItemTransformToUni.java:25)
    io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
    io.smallrye.mutiny.operators.uni.UniBlockingAwait.await(UniBlockingAwait.java:60)
    io.smallrye.mutiny.groups.UniAwait.atMost(UniAwait.java:65)
    io.smallrye.mutiny.groups.UniAwait.indefinitely(UniAwait.java:46)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneRepository.listAll(FortuneRepository.java:62)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneRepository_ClientProxy.listAll(Unknown Source)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneResource.getAllFortunes(FortuneResource.java:22)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneResource$quarkusrestinvoker$getAllFortunes_17630c6d41f1fd48ab8b75db07f293217742deee.invoke(Unknown Source)
    org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
    io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141)
    org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
    io.quarkus.virtual.threads.ContextPreservingExecutorService$ContextPreservingRunnable.run(ContextPreservingExecutorService.java:45)
    java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314)
    java.base/java.lang.VirtualThread.run(VirtualThread.java:311)
VirtualThread[#172,quarkus-virtual-thread-0]/runnable@ForkJoinPool-1-worker-1
    java.base/java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:236)
    java.base/java.lang.ThreadLocal.get(ThreadLocal.java:194)
    java.base/java.lang.ThreadLocal.get(ThreadLocal.java:172)
    com.intellij.rt.debugger.agent.CaptureStorage.capture(CaptureStorage.java:43)
    io.netty.util.concurrent.SingleThreadEventExecutor.execute0(SingleThreadEventExecutor.java)
    io.netty.util.concurrent.SingleThreadEventExecutor.execute(SingleThreadEventExecutor.java:817)
    io.netty.util.concurrent.AbstractScheduledEventExecutor.schedule(AbstractScheduledEventExecutor.java:290)
    io.netty.util.concurrent.AbstractScheduledEventExecutor.schedule(AbstractScheduledEventExecutor.java:205)
    io.vertx.core.impl.VertxImpl.scheduleTimeout(VertxImpl.java:667)
    io.vertx.core.impl.VertxImpl.scheduleTimeout(VertxImpl.java:678)
    io.vertx.core.impl.ContextInternal.setTimer(ContextInternal.java:478)
    io.vertx.sqlclient.impl.pool.SqlConnectionPool$1PoolRequest.onEnqueue(SqlConnectionPool.java:219)
    io.vertx.sqlclient.impl.pool.SqlConnectionPool$1PoolRequest.onConnect(SqlConnectionPool.java:235)
    io.vertx.core.net.impl.pool.SimpleConnectionPool$Acquire$3.run(SimpleConnectionPool.java:593)
    io.vertx.core.net.impl.pool.Task.runNextTasks(Task.java:43)
    io.vertx.core.net.impl.pool.CombinerExecutor.submit(CombinerExecutor.java:91)
    io.vertx.core.net.impl.pool.SimpleConnectionPool.execute(SimpleConnectionPool.java:244)
    io.vertx.core.net.impl.pool.SimpleConnectionPool.acquire(SimpleConnectionPool.java:639)
    io.vertx.sqlclient.impl.pool.SqlConnectionPool.acquire(SqlConnectionPool.java:239)
    io.vertx.sqlclient.impl.PoolImpl.acquire(PoolImpl.java:176)
    io.vertx.sqlclient.impl.PoolImpl.getConnection(PoolImpl.java:139)
    io.vertx.sqlclient.impl.PoolImpl.getConnection(PoolImpl.java:126)
    io.vertx.sqlclient.impl.PoolBase.getConnection(PoolBase.java:56)
    io.vertx.pgclient.PgPool_7weXgYHcaRck_k2l7dGKfXZMdD8_Synthetic_ClientProxy.getConnection(Unknown Source)
    io.vertx.mutiny.sqlclient.Pool.lambda$getConnection$11(Pool.java:116)
    io.smallrye.context.impl.wrappers.SlowContextualConsumer.accept(SlowContextualConsumer.java:21)
    io.smallrye.mutiny.vertx.AsyncResultUni.subscribe(AsyncResultUni.java:31)
    io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
    io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni.subscribe(UniOnItemTransformToUni.java:25)
    io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
    io.smallrye.mutiny.operators.uni.UniBlockingAwait.await(UniBlockingAwait.java:60)
    io.smallrye.mutiny.groups.UniAwait.atMost(UniAwait.java:65)
    io.smallrye.mutiny.groups.UniAwait.indefinitely(UniAwait.java:46)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneRepository.listAll(FortuneRepository.java:62)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneRepository_ClientProxy.listAll(Unknown Source)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneResource.getAllFortunes(FortuneResource.java:22)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneResource$quarkusrestinvoker$getAllFortunes_17630c6d41f1fd48ab8b75db07f293217742deee.invoke(Unknown Source)
    org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
    io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141)
    org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
    io.quarkus.virtual.threads.ContextPreservingExecutorService$ContextPreservingRunnable.run(ContextPreservingExecutorService.java:45)
    java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314)
    java.base/java.lang.VirtualThread.run(VirtualThread.java:311)

@geoand geoand added area/virtual-threads Issue related to Java's Virtual Threads and removed triage/needs-triage labels Mar 26, 2024
Copy link

quarkus-bot bot commented Mar 26, 2024

/cc @cescoffier (virtual-threads), @ozangunalp (virtual-threads)

@mkouba
Copy link
Contributor

mkouba commented Mar 26, 2024

Hm, so we use ThreadLocal in the InjectionPointProvider and in the DecoratorDelegateProvider. We would either need to create a new abstraction or piggyback on the CurrentContextFactory. Which would be a misuse but on the other hand it's a perfect fit 🤷.

CC @manovotn @Ladicek

@vietj
Copy link

vietj commented Mar 26, 2024

The vertx one seems to be triggered by ContextPreservingExecutorService, perhaps ContextPreservingExecutorService should check whether it is a vertx thread or not before using vertx api.

@franz1981
Copy link
Contributor Author

franz1981 commented Mar 26, 2024

@vietj I see that despite starting with ContextPreservingExecutorService it will end up hitting the combiner too

    java.base/java.lang.ThreadLocal.get(ThreadLocal.java:194)
    java.base/java.lang.ThreadLocal.get(ThreadLocal.java:172)
    io.netty.util.internal.InternalThreadLocalMap.slowGet(InternalThreadLocalMap.java:130)
    io.netty.util.internal.InternalThreadLocalMap.get(InternalThreadLocalMap.java:117)
    io.netty.util.concurrent.FastThreadLocal.get(FastThreadLocal.java:136)
    io.vertx.core.net.impl.pool.CombinerExecutor.submit(CombinerExecutor.java:85)
    io.vertx.core.net.impl.pool.SimpleConnectionPool.execute(SimpleConnectionPool.java:244)
    io.vertx.core.net.impl.pool.SimpleConnectionPool.acquire(SimpleConnectionPool.java:639)
    io.vertx.sqlclient.impl.pool.SqlConnectionPool.acquire(SqlConnectionPool.java:239)
    io.vertx.sqlclient.impl.PoolImpl.acquire(PoolImpl.java:176)
    io.vertx.sqlclient.impl.PoolImpl.getConnection(PoolImpl.java:139)
    io.vertx.sqlclient.impl.PoolImpl.getConnection(PoolImpl.java:126)
    io.vertx.sqlclient.impl.PoolBase.getConnection(PoolBase.java:56)

your solution at

perhaps ContextPreservingExecutorService should check whether it is a vertx thread or not before using vertx api.

makes sense to me: the good thing is that on virtual threads (and the blocking thread pool as well) we can always await while asking this to be executed on the "right" event loop. I believe is better for both cases, but let's hear @cescoffier because maybe I'm missing something there

@Ladicek
Copy link
Contributor

Ladicek commented Mar 26, 2024

Specifically for the ArC part, we don't do anything similar to Jackson. That is, we don't use thread locals as a terrible replacement for object pooling.

@mkouba We could also put these objects into the CreationalContextImpl. I remember I already showed this to you a long time ago for the DecoratorDelegateProvider, it was very terribly built, but the principle seems sound to me. I unfortunately don't have the code anymore, but I remember it wasn't too hard to build.

@vietj
Copy link

vietj commented Mar 26, 2024

I will try to think about the pool, but I think we should not use the vertx pool like this from a virtual thread too :-)

@franz1981
Copy link
Contributor Author

@vietj yep I think the same: we could just enqueue a task to the right I/O thread (or - better - the one from which the request was coming). IIRC Virtual Threads on Quarkus already have a limited concurrency (using a Semaphore), so there shouldn't have any problem of backpressure as well.

@vietj
Copy link

vietj commented Mar 26, 2024

@franz1981 in pool impl:

  public Future<SqlConnection> getConnection() {
    ContextInternal current = vertx.getOrCreateContext();
    if (pipelined) {
      return current.failedFuture("Cannot acquire a connection on a pipelined pool");
    }
    Promise<SqlConnectionPool.PooledConnection> promise = current.promise();
    acquire(current, connectionTimeout, promise);
    return promise.future().map(conn -> {
      SqlConnectionInternal wrapper = driver.wrapConnection(current, conn.factory(), conn);
      conn.init(wrapper);
      return wrapper;
    });
  }

we could check here whether it's vertx thread or not

@vietj
Copy link

vietj commented Mar 26, 2024

but TBH we should discuss the use case here, because I am not sure to undrstand :-)

@vietj
Copy link

vietj commented Mar 26, 2024

@franz1981 we could perhaps also change CombinerExecutor to use a volatile Thread field and later on compare it against the current thread instead of a thread local, piggy backing on the CombinerExecutor state itself

@franz1981
Copy link
Contributor Author

franz1981 commented Mar 26, 2024

@vietj

IDK, https://github.com/eclipse-vertx/vert.x/blob/adbe97600cc6215f15ce2fac629c89f93618ca8f/src/main/java/io/vertx/core/net/impl/pool/CombinerExecutor.java#L85 is already pretty complex.
To me, issuing an asynchronous call from a virtual thread, which has been designed to wait, is not a good use of it, especially if the upper mutiny layers will wait for that acquire to happen - whcih means that the pooled connection acquisition is key to make progress there.

@vietj
Copy link

vietj commented Mar 26, 2024

the combiner is designed to be usable from any thread, it's not tied to netty, it just needs a thread to operate, removing the thread local here will not make the code more complex I think.

so perhaps it should be done before entering the pool and that is the code to question I think.

@franz1981
Copy link
Contributor Author

franz1981 commented Mar 26, 2024

We cannot, because of reentrant actions: if we don't use a ConcurrentHashMap<Thread..., > we have no way to bring the context of where we are, around. And CHM is super slow for the other cases.
I would focus instead into the fact that doesn't look a legit use case: if we allow to have 100000 threads we cannot have a single structure which hold all of them, really....is not safe. The scale of the problems we have now is not the same as the Jboss blocking thread pool

@mkouba
Copy link
Contributor

mkouba commented Mar 27, 2024

@mkouba We could also put these objects into the CreationalContextImpl. I remember I already showed this to you a long time ago for the DecoratorDelegateProvider, it was very terribly built, but the principle seems sound to me. I unfortunately don't have the code anymore, but I remember it wasn't too hard to build.

@Ladicek Well, things related to CreationalContext are usually not easy and straightforward but I agree that it would be a perfect fit for this kind of information. Feel free to add this issue to your TODO list then ;-).

@vietj
Copy link

vietj commented Mar 27, 2024

We cannot, because of reentrant actions: if we don't use a ConcurrentHashMap<Thread..., > we have no way to bring the context of where we are, around. And CHM is super slow for the other cases. I would focus instead into the fact that doesn't look a legit use case: if we allow to have 100000 threads we cannot have a single structure which hold all of them, really....is not safe. The scale of the problems we have now is not the same as the Jboss blocking thread pool

we need to understand when this pool is not accessed from event loop and the use cases

@franz1981
Copy link
Contributor Author

we need to understand when this pool is not accessed from event loop and the use cases

The stack trace is

VirtualThread[#172,quarkus-virtual-thread-0]/runnable@ForkJoinPool-1-worker-1
    java.base/java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:236)
    java.base/java.lang.ThreadLocal.get(ThreadLocal.java:194)
    java.base/java.lang.ThreadLocal.get(ThreadLocal.java:172)
    com.intellij.rt.debugger.agent.CaptureStorage.capture(CaptureStorage.java:43)
    io.netty.util.concurrent.SingleThreadEventExecutor.execute0(SingleThreadEventExecutor.java)
    io.netty.util.concurrent.SingleThreadEventExecutor.execute(SingleThreadEventExecutor.java:817)
    io.netty.util.concurrent.AbstractScheduledEventExecutor.schedule(AbstractScheduledEventExecutor.java:290)
    io.netty.util.concurrent.AbstractScheduledEventExecutor.schedule(AbstractScheduledEventExecutor.java:205)
    io.vertx.core.impl.VertxImpl.scheduleTimeout(VertxImpl.java:667)
    io.vertx.core.impl.VertxImpl.scheduleTimeout(VertxImpl.java:678)
    io.vertx.core.impl.ContextInternal.setTimer(ContextInternal.java:478)
    io.vertx.sqlclient.impl.pool.SqlConnectionPool$1PoolRequest.onEnqueue(SqlConnectionPool.java:219)
    io.vertx.sqlclient.impl.pool.SqlConnectionPool$1PoolRequest.onConnect(SqlConnectionPool.java:235)
    io.vertx.core.net.impl.pool.SimpleConnectionPool$Acquire$3.run(SimpleConnectionPool.java:593)
    io.vertx.core.net.impl.pool.Task.runNextTasks(Task.java:43)
    io.vertx.core.net.impl.pool.CombinerExecutor.submit(CombinerExecutor.java:91)
    io.vertx.core.net.impl.pool.SimpleConnectionPool.execute(SimpleConnectionPool.java:244)
    io.vertx.core.net.impl.pool.SimpleConnectionPool.acquire(SimpleConnectionPool.java:639)
    io.vertx.sqlclient.impl.pool.SqlConnectionPool.acquire(SqlConnectionPool.java:239)
    io.vertx.sqlclient.impl.PoolImpl.acquire(PoolImpl.java:176)
    io.vertx.sqlclient.impl.PoolImpl.getConnection(PoolImpl.java:139)
    io.vertx.sqlclient.impl.PoolImpl.getConnection(PoolImpl.java:126)
    io.vertx.sqlclient.impl.PoolBase.getConnection(PoolBase.java:56)
    io.vertx.pgclient.PgPool_7weXgYHcaRck_k2l7dGKfXZMdD8_Synthetic_ClientProxy.getConnection(Unknown Source)
    io.vertx.mutiny.sqlclient.Pool.lambda$getConnection$11(Pool.java:116)
    io.smallrye.context.impl.wrappers.SlowContextualConsumer.accept(SlowContextualConsumer.java:21)
    io.smallrye.mutiny.vertx.AsyncResultUni.subscribe(AsyncResultUni.java:31)
    io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
    io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni.subscribe(UniOnItemTransformToUni.java:25)
    io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
    io.smallrye.mutiny.operators.uni.UniBlockingAwait.await(UniBlockingAwait.java:60)
    io.smallrye.mutiny.groups.UniAwait.atMost(UniAwait.java:65)
    io.smallrye.mutiny.groups.UniAwait.indefinitely(UniAwait.java:46)
    me.escoffier.fortune.virtual.reactive.postgresql.FortuneRepository.listAll(FortuneRepository.java:62)

which is triggered by https://github.com/franz1981/fortune-benchmark/blob/fix_new_quarkus/fortune-virtual-thread-postgresql/src/main/java/me/escoffier/fortune/virtual/reactive/postgresql/FortuneRepository.java#L51-L62

    public List<Fortune> listAll() {
        List<Fortune> list = new ArrayList<>();
        return datasource.getConnection().flatMap(connection ->
                connection.query(Statements.SELECT_ALL_FORTUNES).execute()
                        .map(rs -> {
                            for (Row row : rs) {
                                list.add(new Fortune(row.getInteger(0), row.getString(1)));
                            }
                            return list;
                        })
                        .onTermination().call(connection::close)
        ).await().indefinitely();
    }

where datasource.getConnection will end up calling the vertx pool

@franz1981
Copy link
Contributor Author

franz1981 commented Mar 27, 2024

@FroMage I've taken a quick look at the Virtual Thread stacks and the ThreadLocal usage reported at smallrye/smallrye-context-propagation#424 is particularly bad here as well (mostly because ThreadLocal(s) are bad with virtual threads

image

Adding @mariofusco here as well, which was working on that issue IIRC

In short, not just the remove (which is not nice), but the ThreadLocal itself is not good here

@FroMage
Copy link
Member

FroMage commented Mar 27, 2024

I'm all for removing ThreadLocal uses, but any new strategy tested needs benchmarking validation 🤷

@franz1981
Copy link
Contributor Author

franz1981 commented Mar 27, 2024

That's not a problem, I can help with that one @FroMage - or we can have (given that virtual thread(s) are not the most common usage yet) the option of using an hybrid data-structure
ie using a ConcurrentHashMap<Thread, Whatever> to emulate a thread-local storage for VT and still using ThreadLocal(s) for the rest of the world. Clearly it requires periodically to verify if all the thread(s) are still alive, because no concurrent weak ref map exists in java AFAIK.
Or - we can think to use https://openjdk.org/jeps/464 for virtual thread(s) only: I'm opened to all the possibilities here, but probably worth opening the issue on the ctx prog side, wdyt?

@FroMage
Copy link
Member

FroMage commented Mar 27, 2024

Perfect topic to discuss at the f2f, much easier in person, unless this is urgent?

@franz1981
Copy link
Contributor Author

+100 for F2F discussion on this, great idea (almost forgot we have the F2F :D )

@franz1981
Copy link
Contributor Author

@Ladicek @mkouba I can just let this issue opened or you want to track your ones separately?

@Ladicek
Copy link
Contributor

Ladicek commented Apr 2, 2024

@franz1981 I submitted a PR for ArC (#39837), so feel free to do whatever you wish with this issue.

@franz1981
Copy link
Contributor Author

Many thanks @Ladicek let me know if I need to take a look there for some reason, and I will try (likely in the next days)

@Ladicek
Copy link
Contributor

Ladicek commented Apr 2, 2024

Actually I might have been a bit too fast, I'll let you know :-) From performance perspective, I'm adding a field (one at the moment, but may expand to two) into CreationalContextImpl, which is used super often, so it might be good to check. But let me test first, if that even makes sense.

@mkouba
Copy link
Contributor

mkouba commented Apr 4, 2024

Actually I might have been a bit too fast, I'll let you know :-) From performance perspective, I'm adding a field (one at the moment, but may expand to two) into CreationalContextImpl, which is used super often, so it might be good to check. But let me test first, if that even makes sense.

That field is not initialized in most cases, or?

@Ladicek
Copy link
Contributor

Ladicek commented Apr 4, 2024

That is correct, but it makes all the CreationalContextImpl objects bigger, which might be a concern.

@mkouba
Copy link
Contributor

mkouba commented Apr 4, 2024

That is correct, but it makes all the CreationalContextImpl objects bigger, which might be a concern.

Hm, "bigger" in the sense that you'll need roughly 4 more bytes for each instance? I don't think you'll be able to spot the difference (except for microbenchmarks where you create millions of beans ofc ;-).

@Ladicek
Copy link
Contributor

Ladicek commented Apr 4, 2024

Yeah, it is 4 bytes for each CC. It isn't much, but indeed, it is 4 more bytes for each created bean. Probably not too big of a deal.

@Ladicek
Copy link
Contributor

Ladicek commented Apr 10, 2024

I ended up making that 2 fields for the CC, so it's 8 bytes more for each created bean. Otherwise, #39837 seems OK, so @franz1981 if you think that PR needs some care, please take a look. Thanks!

@franz1981
Copy link
Contributor Author

What's the state of this on your side @Ladicek @mkouba ?

and @vietj ?

@Ladicek
Copy link
Contributor

Ladicek commented Apr 30, 2024

#39837 was merged, so there are no thread-locals used in ArC anymore (with the exception of thread-local contexts, but by default, those are only used on non-Vert.x threads).

@mkouba
Copy link
Contributor

mkouba commented Apr 30, 2024

What's the state of this on your side @Ladicek @mkouba ?

Ladislav's PR was merged so ArC should not use thread locals at all...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/virtual-threads Issue related to Java's Virtual Threads kind/bug Something isn't working
Projects
None yet
Development

No branches or pull requests

6 participants