We are using Caffeine in Apache Camel and as part of the Quarkus effort we are working to make it possible to compile Apache Camel as native application and while working on this we have found and issue related to the usage of 'ForkJoinPool.commonPool()' in Caffeine.
To simplify things I've create a project with a minimal set-up that reproduces the problem in which the build of this simple application:
public class Application {
private static final Cache<String, String> CACHE = Caffeine.newBuilder()
.build();
public static void main(String[] args) {
CACHE.put("key", "test");
}
}
Fails because:
Warning: Aborting stand-alone image build. Detected the ForkJoinPool.commonPool() in the image heap. The common pool must be created at run time because the parallelism depends on the number of cores available at run time. Therefore the common pool used during image generation must not be reachable, e.g., via a static field that caches a copy of the common pool. The object was probably created by a class initializer and is reachable from a static field. By default, all class initialization is done during native image building.You can manually delay class initialization to image run time by using the option -H:ClassInitialization=<class-name>. Or you can write your own initialization methods and call them explicitly from your main entry point.
Detailed message:
Trace: object com.github.benmanes.caffeine.cache.UnboundedLocalCache
object com.github.benmanes.caffeine.cache.UnboundedLocalCache$UnboundedLocalManualCache
method io.github.lburgazzoli.graalvm.caffeine.Application.main(String[])
Call path from entry point to io.github.lburgazzoli.graalvm.caffeine.Application.main(String[]):
at io.github.lburgazzoli.graalvm.caffeine.Application.main(Application.java:12)
at com.oracle.svm.core.JavaMainWrapper.run(JavaMainWrapper.java:147)
at com.oracle.svm.core.code.IsolateEnterStub.JavaMainWrapper_run_5087f5482cc9a6abc971913ece43acb471d2631b(generated:0)
The application build fine if the call to the commonPool is wrapped like:
public class Application {
private static final Cache<String, String> CACHE = Caffeine.newBuilder()
.executor(task -> ForkJoinPool.commonPool().execute(task))
.build();
public static void main(String[] args) {
CACHE.put("key", "test");
}
}
I do not know it that is the best solution but it would be nice to have some support for GraalVM/SubstrateVM in Caffeine as when using 3th party libraries it may not possible to customize the executor used by Caffeine.
It seems awkward to require all usages wrap to the pool to be lazily initialized. Why not have Graal make it lazy as a compiler transformation instead? This seems like an issue we should try to workout together with them.
Yep, I did open here as I do not know what's the best option
Can you open an issue with them and cc me? I鈥檇 like to know how they want developers to write code and if leaking workarounds is the expectation. I suspect they would be willing to add lazy resolving but didn鈥檛 get to it yet.
Fixed in graal! :)
I still have the same issue in https://github.com/quarkusio/quarkus/pull/3394.
I think you should raise it with the Graal team.