quarkusio / quarkus

Quarkus: Supersonic Subatomic Java.
https://quarkus.io
Apache License 2.0
13.37k stars 2.56k forks source link

Avoid ThreadLocals while using VirtualThreads #39696

Open franz1981 opened 3 months ago

franz1981 commented 3 months ago

Similarly to https://github.com/FasterXML/jackson-core/issues/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 commented 3 months ago

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

Adding @mkouba because Arc seems affected the same.

franz1981 commented 3 months ago

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)
quarkus-bot[bot] commented 3 months ago

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

mkouba commented 3 months ago

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 :shrug:.

CC @manovotn @Ladicek

vietj commented 3 months ago

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 commented 3 months ago

@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 commented 3 months ago

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 commented 3 months ago

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 commented 3 months ago

@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 commented 3 months ago

@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 commented 3 months ago

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

vietj commented 3 months ago

@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 commented 3 months ago

@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 commented 3 months ago

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 commented 3 months ago

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 commented 3 months ago

@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 commented 3 months ago

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 commented 3 months ago

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 commented 3 months ago

@FroMage I've taken a quick look at the Virtual Thread stacks and the ThreadLocal usage reported at https://github.com/smallrye/smallrye-context-propagation/issues/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 commented 3 months ago

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

franz1981 commented 3 months ago

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 commented 3 months ago

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

franz1981 commented 3 months ago

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

franz1981 commented 3 months ago

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

Ladicek commented 3 months ago

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

franz1981 commented 3 months ago

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 commented 3 months ago

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 commented 3 months ago

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 commented 3 months ago

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

mkouba commented 3 months ago

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 commented 3 months ago

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 commented 3 months ago

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 commented 2 months ago

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

and @vietj ?

Ladicek commented 2 months ago

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 commented 2 months ago

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...