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

Make Javalin more GraalVM friendly #236

Open
pompiuses opened this issue Jun 25, 2023 · 5 comments
Open

Make Javalin more GraalVM friendly #236

pompiuses opened this issue Jun 25, 2023 · 5 comments

Comments

@pompiuses
Copy link

pompiuses commented Jun 25, 2023

Is your feature request related to a problem? Please describe.
I've been looking into having Javalin run on GraalVM. In that process I ran into a bug in GraalVM as described here. This bug prevents you from running Javalin with virtual threads in a native image.

The main problem is that GraalVM fails to correctly handle a reflective call in ConcurrencyUtil.kt when creating a new virtual thread.

Even though this isn't Javalins fault it will still block anyone who wants to use GraalVM and Javalin. I also suspect this bug wont get fixed for a while at GraalVMs side. Since the official release of virtual threads in Java 21 is just around the corner I think it'd be beneficial if this problem could get fixed at Javalins side before Java 21 is released.

Describe the solution you'd like
The fix would mean rewriting ConcurrencyUtil.kt to either not use reflection, or refactor it in such a way that it works with GraalVM.

Additional context
As a sidenote I have managed to create a temporary workaround as described in this thread in the GraalVM slack chat.

@tipsy
Copy link
Member

tipsy commented Jun 25, 2023

The fix would mean rewriting ConcurrencyUtil.kt to either not use reflection, or refactor it in such a way that it works with GraalVM.

How can we achieve that while still targeting Java 17?

@pompiuses
Copy link
Author

Not sure, that would have to be looked into. The first thing that comes to mind is that it's maybe possible to refactor ReflectiveVirtualThreadBuilder so that the "unstarted" method gets correctly invoked by GraalVMs native-image compiler. One would have to experiment with different solutions until one finds something that works. How does the other frameworks solve this problem (Spring, Micronaut etc)?

@pompiuses
Copy link
Author

pompiuses commented Jun 26, 2023

One possible solution could be to create a single instance of a virtual thread factory (Thread.ofVirtual().factory()) to create new threads with instead of Thread.Builder.OfVirtual in ReflectiveVirtualThreadBuilder. Then you won't have to call the unstarted method which GraalVM has problems with. It'd probably also be a slightly more efficient implementation than instantiating a new builder for each thread.

@pompiuses
Copy link
Author

I got around this issue when upgrading to Javalin 6. I can now set the Jetty thread pool directly myself.

        Javalin javalin = Javalin.create(config -> {
            // Using a custom thread pool for virtual threads instead of the default Javalin LoomUtil.LoomThreadPool.
            // This is because it uses reflection to create new threads which causes an error in GraalVM.
            config.jetty.threadPool = new ThreadPool() {
                @Override
                public void join() { /*no-op*/ }

                @Override
                public int getThreads() {
                    return 1;
                }

                @Override
                public int getIdleThreads() {
                    return 1;
                }

                @Override
                public boolean isLowOnThreads() {
                    return false;
                }

                @Override
                public void execute(@NonNull Runnable command) {
                    AppConfig.virtualThreadFactory.newThread(command).start();
                }
            };
        });

For reference I previously used the GraalVM SDK to override the use of reflection in Javalin's NamedVirtualThreadFactory. This has now been removed.

import com.oracle.svm.core.annotate.Alias;
import com.oracle.svm.core.annotate.Substitute;
import com.oracle.svm.core.annotate.TargetClass;
import io.javalin.util.NamedVirtualThreadFactory;

import java.util.concurrent.atomic.AtomicInteger;

@TargetClass(NamedVirtualThreadFactory.class)
public final class Target_io_javalin_util_NamedVirtualThreadFactory {
    @Alias
    AtomicInteger threadCount;

    @Alias
    String prefix;

    @Substitute
    public Thread newThread(Runnable runnable) {
        return Thread.ofVirtual()
                .name(prefix + "-Virtual-" + threadCount.getAndIncrement())
                .unstarted(runnable);
    }
}

@pompiuses
Copy link
Author

I assume this issue can be closed. Maybe the Javalin documentation can be updated with the code above in case someone else tries to use Javalin with GraalVM?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants