Skip to content

Starvation in ensureInitialize #10923

@JaroslavTulach

Description

@JaroslavTulach

Describe GraalVM and your environment :

  • GraalVM version or commit id if built from source: 21+35-jvmci-23.1-b15
  • CE or EE: CE
  • JDK version: JDK21
  • OS and OS Version: Ubuntu
  • Architecture: amd64
  • The output of java -Xinternalversion:
~/bin/graalvm-community-openjdk-21+35.1/bin/java -Xinternalversion
OpenJDK 64-Bit Server VM (21+35-jvmci-23.1-b15) for linux-amd64 JRE (21+35-jvmci-23.1-b15), built on 2023-09-05T16:45:14Z by "buildslave" with gcc 11.2.0

Have you verified this issue still happens when using the latest snapshot?
You can find snapshot builds here: https://github.com/graalvm/graalvm-ce-dev-builds/releases

Describe the issue

Enso tried to migrate to Truffle 24.2.0, but we are facing starvation when initializing TRegex. This is the stack of the starving thread:

     at java.util.concurrent.locks.LockSupport.park(java.base@21/LockSupport.java:221)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(java.base@21/AbstractQueuedSynchronizer.java:754)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(java.base@21/AbstractQueuedSynchronizer.java:1099)
        at com.oracle.truffle.api.impl.ThreadLocalHandshake$Barrier.await(ThreadLocalHandshake.java:273)
        at com.oracle.truffle.api.impl.ThreadLocalHandshake$Handshake.get(ThreadLocalHandshake.java:500)
        at com.oracle.truffle.api.impl.ThreadLocalHandshake$Handshake.get(ThreadLocalHandshake.java:295)
        at com.oracle.truffle.polyglot.PolyglotLanguageContext.lambda$ensureInitialized$1(PolyglotLanguageContext.java:862)
        at com.oracle.truffle.polyglot.PolyglotLanguageContext$$Lambda/0x00007ae5c80f7670.apply(Unknown Source)
        at com.oracle.truffle.api.impl.ThreadLocalHandshake$TruffleSafepointImpl.setBlockedBoundary(ThreadLocalHandshake.java:881)
        at com.oracle.truffle.api.impl.ThreadLocalHandshake$TruffleSafepointImpl.setBlocked(ThreadLocalHandshake.java:852)
        at com.oracle.truffle.api.TruffleSafepoint.setBlockedThreadInterruptible(TruffleSafepoint.java:338)
        at com.oracle.truffle.polyglot.PolyglotLanguageContext.ensureInitialized(PolyglotLanguageContext.java:860)
        at com.oracle.truffle.polyglot.PolyglotContextImpl.getContextInitialized(PolyglotContextImpl.java:833)

Code snippet or code repository that reproduces the issue

Author: Jaroslav Tulach <jaroslav.tulach@enso.org>
Date:   Tue Mar 25 14:52:15 2025 +0100

    Showing starvation problem introduces in Truffle 24.2.0

diff --git pom.xml pom.xml
new file mode 100644
index 0000000..1c471ec
--- /dev/null
+++ pom.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.apidesign.demo</groupId>
+    <artifactId>StarvationOfTruffle</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <packaging>jar</packaging>
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <maven.compiler.release>21</maven.compiler.release>
+        <exec.mainClass>org.apidesign.demo.starvationoftruffle.StarvationOfTruffle</exec.mainClass>
+        <graalvm.version>24.2.0</graalvm.version>
+        <exec.vmArgs>-ea</exec.vmArgs>
+        <exec.args>${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs}</exec.args>
+        <exec.appArgs>Hi Hello Hola Hey Ciao Ahoj</exec.appArgs>
+        <exec.mainClass>org.apidesign.demo.starvationoftruffle.StarvationOfTruffle</exec.mainClass>
+        <exec.executable>${java.home}/bin/java</exec.executable>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>org.graalvm.truffle</groupId>
+            <artifactId>truffle-api</artifactId>
+            <version>${graalvm.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.graalvm.truffle</groupId>
+            <artifactId>truffle-dsl-processor</artifactId>
+            <version>${graalvm.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.graalvm.regex</groupId>
+            <artifactId>regex</artifactId>
+            <version>${graalvm.version}</version>
+            <scope>runtime</scope>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <version>3.5.0</version>
+            </plugin>
+        </plugins>
+    </build>
+</project>
\ No newline at end of file
diff --git src/main/java/org/apidesign/demo/starvationoftruffle/ManyThreadsLanguage.java src/main/java/org/apidesign/demo/starvationoftruffle/ManyThreadsLanguage.java
new file mode 100644
index 0000000..2302a55
--- /dev/null
+++ src/main/java/org/apidesign/demo/starvationoftruffle/ManyThreadsLanguage.java
@@ -0,0 +1,67 @@
+package org.apidesign.demo.starvationoftruffle;
+
+import com.oracle.truffle.api.CallTarget;
+import com.oracle.truffle.api.TruffleLanguage;
+import com.oracle.truffle.api.source.Source;
+import java.util.concurrent.CountDownLatch;
+
+@TruffleLanguage.Registration(id = "many")
+public final class ManyThreadsLanguage extends TruffleLanguage<TruffleLanguage.Env>
+        implements Runnable {
+
+    private static final ContextReference<Env> REF = ContextReference.create(ManyThreadsLanguage.class);
+    private final CountDownLatch all = new CountDownLatch(1);
+    private final Thread[] threads = new Thread[10];
+
+    @Override
+    protected boolean isThreadAccessAllowed(Thread thread, boolean singleThreaded) {
+        return true;
+    }
+
+    @Override
+    protected Env createContext(Env env) {
+        for (var i = 0; i < threads.length; i++) {
+            var t = env.newTruffleThreadBuilder(this).build();
+            t.start();
+            threads[i] = t;
+        }
+        return env;
+    }
+
+    @Override
+    protected CallTarget parse(ParsingRequest request) throws Exception {
+        var orig = request.getSource();
+        var env = REF.get(null);
+        var src = Source.newBuilder("regex", "Flavor=ECMAScript/" + orig.getCharacters() + "/", orig.getName())
+                .mimeType("application/tregex")
+                .internal(true)
+                .build();
+        return env.parseInternal(src);
+    }
+
+
+
+    @Override
+    protected void finalizeContext(Env context) {
+        all.countDown();
+        for (var t : threads) {
+            try {
+                t.join();
+            } catch (InterruptedException ex) {
+                ex.printStackTrace();
+            }
+        }
+    }
+
+    @Override
+    public void run() {
+        while (true) {
+            try {
+                all.await();
+                return;
+            } catch (InterruptedException ex) {
+            }
+        }
+    }
+
+}
diff --git src/main/java/org/apidesign/demo/starvationoftruffle/StarvationOfTruffle.java src/main/java/org/apidesign/demo/starvationoftruffle/StarvationOfTruffle.java
new file mode 100644
index 0000000..05f1f90
--- /dev/null
+++ src/main/java/org/apidesign/demo/starvationoftruffle/StarvationOfTruffle.java
@@ -0,0 +1,18 @@
+package org.apidesign.demo.starvationoftruffle;
+
+import org.graalvm.polyglot.Context;
+
+public class StarvationOfTruffle {
+
+    public static void main(String[] args) {
+        try (var ctx = Context.newBuilder("many", "regex")
+                .allowCreateThread(true)
+                .build()) {
+            var regex = ctx.eval("many", ".*[Hh].*");
+            for (var word : args) {
+                var match = regex.invokeMember("exec", word, 0).getMember("isMatch").asBoolean();
+                System.out.println("word: " + word + " match: " + match);
+            }
+        }
+    }
+}

Save the diff into a file X.diff. Then:

$ patch -p0 <X.diff

to create pom.xml, ManyThreadsLanguage.java and StarvationOfTruffle.java.

Steps to reproduce the issue

After preparing the sources with patch as described above just:

$ JAVA_HOME=~/bin/graalvm-community-openjdk-21+35.1/ mvn -q clean install exec:exec

the Java process is started, but never finished. Ctrl+\ shows the starvation in PolyglotLanguageContext.ensureInitialized.

Expected behavior

Just run the same with Truffle 24.0.0 and everything works:

$ JAVA_HOME=~/bin/graalvm-community-openjdk-21+35.1/ mvn -q clean install exec:exec -Dgraalvm.version=24.0.0
word: Hi match: true
word: Hello match: true
word: Hola match: true
word: Hey match: true
word: Ciao match: false
word: Ahoj match: true

Additional context

Metadata

Metadata

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions