From ba715269a3de80eb304982f9ee24172036c9ceea Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Tue, 19 Aug 2025 11:54:39 +0200 Subject: [PATCH 01/12] Reduce locking in HostClassLoader Using a neat trick described in https://github.com/enso-org/enso/pull/13219#discussion_r2131542150 to avoid large synchronization blocks. --- .../enso/interpreter/epb/HostClassLoader.java | 83 +++++++++++-------- 1 file changed, 48 insertions(+), 35 deletions(-) diff --git a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/HostClassLoader.java b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/HostClassLoader.java index 969e714985a1..8057641a7294 100644 --- a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/HostClassLoader.java +++ b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/HostClassLoader.java @@ -17,8 +17,10 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; -import java.util.Map; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; import org.graalvm.polyglot.Context; /** @@ -30,7 +32,8 @@ @ExportLibrary(InteropLibrary.class) final class HostClassLoader extends URLClassLoader implements AutoCloseable, TruffleObject { - private final Map> loadedClasses = new ConcurrentHashMap<>(); + private final ConcurrentHashMap>> loadedClasses = + new ConcurrentHashMap<>(); private static final Logger logger = System.getLogger(HostClassLoader.class.getName()); // Classes from "org.graalvm" packages are loaded either by a class loader for the boot // module layer, or by a specific class loader, depending on how enso is run. For example, @@ -68,43 +71,53 @@ public Class loadClass(String name) throws ClassNotFoundException { @Override @CompilerDirectives.TruffleBoundary + @SuppressWarnings("unchecked") protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { logger.log(Logger.Level.TRACE, "Loading class {0}", name); - var l = loadedClasses.get(name); - if (l != null) { - logger.log(Logger.Level.TRACE, "Class {0} found in cache", name); - return l; + var placeholder = new CompletableFuture[1]; + var pendingClass = + loadedClasses.computeIfAbsent( + name, + t -> { + logger.log(Logger.Level.TRACE, "Class {0} found in cache", name); + var f = new CompletableFuture>(); + placeholder[0] = f; + return f; + }); + if (placeholder[0] != null) { + placeholder[0].complete(loadClassUnsafe(name, resolve)); } - synchronized (this) { - l = loadedClasses.get(name); - if (l != null) { - logger.log(Logger.Level.TRACE, "Class {0} found in cache", name); - return l; - } - if (!isRuntimeModInBootLayer && name.startsWith("org.graalvm")) { - return polyglotClassLoader.loadClass(name); - } - if (name.startsWith("org.slf4j")) { - // Delegating to system class loader ensures that log classes are not loaded again - // and do not require special setup. In other words, it is using log configuration that - // has been setup by the runner that started the process. See #11641. - return polyglotClassLoader.loadClass(name); - } - try { - l = findClass(name); - if (resolve) { - l.getMethods(); - } - logger.log(Logger.Level.TRACE, "Class {0} found, putting in cache", name); - loadedClasses.put(name, l); - return l; - } catch (ClassNotFoundException ex) { - logger.log(Logger.Level.TRACE, "Class {0} not found, delegating to super", name); - return super.loadClass(name, resolve); - } catch (Throwable e) { - logger.log(Logger.Level.TRACE, "Failure while loading a class: " + e.getMessage(), e); - throw e; + try { + return pendingClass.get(); + } catch (InterruptedException | ExecutionException e) { + throw new ClassNotFoundException("Unable to find class " + name, e); + } + } + + /** Find a class with a given name without giving any Thread-safety guarantees. */ + private Class loadClassUnsafe(String name, boolean resolve) throws ClassNotFoundException { + if (!isRuntimeModInBootLayer && name.startsWith("org.graalvm")) { + return polyglotClassLoader.loadClass(name); + } + if (name.startsWith("org.slf4j")) { + // Delegating to system class loader ensures that log classes are not loaded again + // and do not require special setup. In other words, it is using log configuration that + // has been setup by the runner that started the process. See #11641. + return polyglotClassLoader.loadClass(name); + } + try { + var l = findClass(name); + if (resolve) { + l.getMethods(); } + logger.log(Logger.Level.TRACE, "Class {0} found, putting in cache", name); + return l; + } catch (ClassNotFoundException ex) { + logger.log(Logger.Level.TRACE, "Class {0} not found, delegating to super", name); + return super.loadClass(name, resolve); + } catch (Throwable e) { + logger.log(Logger.Level.TRACE, "Failure while loading a class: " + e.getMessage(), e); + throw e; } } From 4b8d3052e9e0cde6c20c062f76de1164fdc10589 Mon Sep 17 00:00:00 2001 From: Hubert P Date: Wed, 21 Jan 2026 16:08:22 +0100 Subject: [PATCH 02/12] Properly propagate failed class loading in futures If loading a classes would fail, then the result would not be properly reported to the underlying future. As a result `future.get()` would get stuck waiting infinitely. This has happenned surprisingly a lot with nested classes, as a retry mechanism is in place for replacing `.` with `$` in class name. Also tests would hang infinitely. --- .../main/java/org/enso/interpreter/epb/HostClassLoader.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/HostClassLoader.java b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/HostClassLoader.java index c5413a0087c2..9e482d89e996 100644 --- a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/HostClassLoader.java +++ b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/HostClassLoader.java @@ -79,7 +79,11 @@ protected Class loadClass(String name, boolean resolve) throws ClassNotFoundE return f; }); if (placeholder[0] != null) { - placeholder[0].complete(loadClassUnsafe(name, resolve)); + try { + placeholder[0].complete(loadClassUnsafe(name, resolve)); + } catch (ClassNotFoundException e) { + placeholder[0].completeExceptionally(e); + } } try { return pendingClass.get(); From a424d794344fdcfe595dabdd1ef27dd7de2ef25d Mon Sep 17 00:00:00 2001 From: Hubert P Date: Wed, 21 Jan 2026 16:15:37 +0100 Subject: [PATCH 03/12] post-merge fmt --- .../enso/interpreter/epb/HostClassLoader.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/HostClassLoader.java b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/HostClassLoader.java index 98f4575669a3..904bfe7e5637 100644 --- a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/HostClassLoader.java +++ b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/HostClassLoader.java @@ -116,14 +116,14 @@ private Class loadClassUnsafe(String name, boolean resolve) throws ClassNotFo return super.loadClass(name, resolve); } catch (Throwable e) { if (isAttemptToLoadBytecodeInNI(e)) { - logger.log( - Logger.Level.TRACE, - "Attempt to load bytecode for class {0}, delegating to super" + name); - return super.loadClass(name, resolve); - } else { - logger.log(Logger.Level.TRACE, "Failure while loading a class: " + e.getMessage(), e); - throw e; - } + logger.log( + Logger.Level.TRACE, + "Attempt to load bytecode for class {0}, delegating to super" + name); + return super.loadClass(name, resolve); + } else { + logger.log(Logger.Level.TRACE, "Failure while loading a class: " + e.getMessage(), e); + throw e; + } } } From 90b880a3ddfa6736602edd36d5d78235b58a6b51 Mon Sep 17 00:00:00 2001 From: Hubert P Date: Thu, 22 Jan 2026 12:29:59 +0100 Subject: [PATCH 04/12] Test multithreaded access to classloader Added a test with mutual dependency between two classes. The test also uses two threads to evaluate two code examples. The setup should create sufficient race conditions. If the test fails at any point, then we know that our logic is at fault. --- .../org/enso/example/deps/one/DepAClass.java | 9 ++++++ .../org/enso/example/deps/two/DepBClass.java | 13 +++++++++ .../test/interop/JavaInteropTest.java | 29 +++++++++++++++++++ .../enso/interpreter/epb/HostClassLoader.java | 3 ++ 4 files changed, 54 insertions(+) create mode 100644 engine/runtime-integration-tests/src/test/java/org/enso/example/deps/one/DepAClass.java create mode 100644 engine/runtime-integration-tests/src/test/java/org/enso/example/deps/two/DepBClass.java diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/one/DepAClass.java b/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/one/DepAClass.java new file mode 100644 index 000000000000..2bb75c387e59 --- /dev/null +++ b/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/one/DepAClass.java @@ -0,0 +1,9 @@ +package org.enso.example.deps.one; + +import org.enso.example.deps.two.DepBClass; + +public class DepAClass { + public static double expToNeg(double x) { + return DepBClass.expPow(-x); + } +} diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/two/DepBClass.java b/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/two/DepBClass.java new file mode 100644 index 000000000000..1c961785faa1 --- /dev/null +++ b/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/two/DepBClass.java @@ -0,0 +1,13 @@ +package org.enso.example.deps.two; + +import org.enso.example.deps.one.DepAClass; + +public class DepBClass { + public static double sigmoid(double x) { + return 1 / (1 + DepAClass.expToNeg(x)); + } + + public static double expPow(double x) { + return Math.exp(x); + } +} diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/interop/JavaInteropTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/interop/JavaInteropTest.java index 0a1cc7c6e384..1ec8ce5e8acb 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/interop/JavaInteropTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/interop/JavaInteropTest.java @@ -5,7 +5,10 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ForkJoinPool; import org.enso.example.TestClass; import org.enso.test.utils.ContextUtils; import org.graalvm.polyglot.PolyglotException; @@ -503,6 +506,32 @@ public void catchCheckedSubExceptionThrownInJava() { assertEquals(result.asInt(), -1); } + @Test + public void multiThreadedAccess() throws Exception { + var code1 = + """ + from Standard.Base import all + polyglot java import org.enso.example.deps.one.DepAClass + + main = + DepAClass.expToNeg 2 + """; + var code2 = + """ + from Standard.Base import all + polyglot java import org.enso.example.deps.two.DepBClass + + main = + DepBClass.sigmoid 2 + """; + List> cases = new ArrayList<>(); + cases.add(() -> ctx().evalModule(code1).asDouble()); + cases.add(() -> ctx().evalModule(code2).asDouble()); + var results = ForkJoinPool.commonPool().invokeAll(cases); + assertEquals(results.get(0).get().doubleValue(), 0.1353352832366127, 0); + assertEquals(results.get(1).get().doubleValue(), 0.8807970779778823, 0); + } + private Value checkedException(int t) { var code = """ diff --git a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/HostClassLoader.java b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/HostClassLoader.java index 904bfe7e5637..9138c9ffe458 100644 --- a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/HostClassLoader.java +++ b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/HostClassLoader.java @@ -89,6 +89,9 @@ protected Class loadClass(String name, boolean resolve) throws ClassNotFoundE try { return pendingClass.get(); } catch (InterruptedException | ExecutionException e) { + if (e.getCause() instanceof ClassCastException cce) { + throw cce; + } throw new ClassNotFoundException("Unable to find class " + name, e); } } From 7e63f8277285783457bf101136694b21b09ab931 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Thu, 22 Jan 2026 20:42:04 +0100 Subject: [PATCH 05/12] Need both classes to start (and not finish) static class initialization at once --- .../org/enso/example/deps/AwaitLoading.java | 20 +++++++++++++++++++ .../org/enso/example/deps/one/DepAClass.java | 8 ++++++++ .../org/enso/example/deps/two/DepBClass.java | 8 ++++++++ 3 files changed, 36 insertions(+) create mode 100644 engine/runtime-integration-tests/src/test/java/org/enso/example/deps/AwaitLoading.java diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/AwaitLoading.java b/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/AwaitLoading.java new file mode 100644 index 000000000000..85627cf2c9c5 --- /dev/null +++ b/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/AwaitLoading.java @@ -0,0 +1,20 @@ +package org.enso.example.deps; + +import static org.junit.Assert.fail; + +import java.util.concurrent.CountDownLatch; + +public final class AwaitLoading { + private static final CountDownLatch TWO = new CountDownLatch(2); + + private AwaitLoading() {} + + public static void waitForTwo() { + TWO.countDown(); + try { + TWO.await(); + } catch (InterruptedException ex) { + fail("The test failed with " + ex); + } + } +} diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/one/DepAClass.java b/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/one/DepAClass.java index 2bb75c387e59..826ec62ea0d7 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/one/DepAClass.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/one/DepAClass.java @@ -1,8 +1,16 @@ package org.enso.example.deps.one; +import org.enso.example.deps.AwaitLoading; import org.enso.example.deps.two.DepBClass; public class DepAClass { + static { + System.err.println("Started DepAClass"); + AwaitLoading.waitForTwo(); + System.err.println("Loading DepAClass"); + System.err.println("Finished DepAClass"); + } + public static double expToNeg(double x) { return DepBClass.expPow(-x); } diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/two/DepBClass.java b/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/two/DepBClass.java index 1c961785faa1..965ca0939548 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/two/DepBClass.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/two/DepBClass.java @@ -1,8 +1,16 @@ package org.enso.example.deps.two; +import org.enso.example.deps.AwaitLoading; import org.enso.example.deps.one.DepAClass; public class DepBClass { + static { + System.err.println("Started DepBClass"); + AwaitLoading.waitForTwo(); + System.err.println("Loading DepBClass"); + System.err.println("Finished DepBClass"); + } + public static double sigmoid(double x) { return 1 / (1 + DepAClass.expToNeg(x)); } From 3a43d3fad29d468f4e22a9d30a97e3187b8294fc Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Thu, 22 Jan 2026 20:46:23 +0100 Subject: [PATCH 06/12] Initialization of the classes must depend on the other one --- .../test/java/org/enso/example/deps/one/DepAClass.java | 5 ++++- .../test/java/org/enso/example/deps/two/DepBClass.java | 9 ++++++--- .../enso/interpreter/test/interop/JavaInteropTest.java | 4 ++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/one/DepAClass.java b/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/one/DepAClass.java index 826ec62ea0d7..0e7ce2b7a642 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/one/DepAClass.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/one/DepAClass.java @@ -4,14 +4,17 @@ import org.enso.example.deps.two.DepBClass; public class DepAClass { + private static final DepBClass OTHER; + static { System.err.println("Started DepAClass"); AwaitLoading.waitForTwo(); System.err.println("Loading DepAClass"); + OTHER = new DepBClass(); System.err.println("Finished DepAClass"); } public static double expToNeg(double x) { - return DepBClass.expPow(-x); + return OTHER.expPow(-x); } } diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/two/DepBClass.java b/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/two/DepBClass.java index 965ca0939548..3313cd909edb 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/two/DepBClass.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/two/DepBClass.java @@ -4,18 +4,21 @@ import org.enso.example.deps.one.DepAClass; public class DepBClass { + private static final DepAClass OTHER; + static { System.err.println("Started DepBClass"); AwaitLoading.waitForTwo(); System.err.println("Loading DepBClass"); + OTHER = new DepAClass(); System.err.println("Finished DepBClass"); } - public static double sigmoid(double x) { - return 1 / (1 + DepAClass.expToNeg(x)); + public double sigmoid(double x) { + return 1 / (1 + OTHER.expToNeg(x)); } - public static double expPow(double x) { + public double expPow(double x) { return Math.exp(x); } } diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/interop/JavaInteropTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/interop/JavaInteropTest.java index 1ec8ce5e8acb..048517bcd839 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/interop/JavaInteropTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/interop/JavaInteropTest.java @@ -514,7 +514,7 @@ public void multiThreadedAccess() throws Exception { polyglot java import org.enso.example.deps.one.DepAClass main = - DepAClass.expToNeg 2 + DepAClass.new.expToNeg 2 """; var code2 = """ @@ -522,7 +522,7 @@ public void multiThreadedAccess() throws Exception { polyglot java import org.enso.example.deps.two.DepBClass main = - DepBClass.sigmoid 2 + DepBClass.new.sigmoid 2 """; List> cases = new ArrayList<>(); cases.add(() -> ctx().evalModule(code1).asDouble()); From 3de7b6becd3d9466e8cbd6352f59ec5fc32812b8 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Thu, 22 Jan 2026 20:55:00 +0100 Subject: [PATCH 07/12] Some timeouts to not block forever --- .../src/test/java/org/enso/example/deps/AwaitLoading.java | 3 ++- .../org/enso/interpreter/test/interop/JavaInteropTest.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/AwaitLoading.java b/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/AwaitLoading.java index 85627cf2c9c5..61b7c8830188 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/AwaitLoading.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/AwaitLoading.java @@ -3,6 +3,7 @@ import static org.junit.Assert.fail; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; public final class AwaitLoading { private static final CountDownLatch TWO = new CountDownLatch(2); @@ -12,7 +13,7 @@ private AwaitLoading() {} public static void waitForTwo() { TWO.countDown(); try { - TWO.await(); + TWO.await(1, TimeUnit.SECONDS); } catch (InterruptedException ex) { fail("The test failed with " + ex); } diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/interop/JavaInteropTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/interop/JavaInteropTest.java index 048517bcd839..7c7f98622cb6 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/interop/JavaInteropTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/interop/JavaInteropTest.java @@ -506,7 +506,7 @@ public void catchCheckedSubExceptionThrownInJava() { assertEquals(result.asInt(), -1); } - @Test + @Test(timeout = 10000) public void multiThreadedAccess() throws Exception { var code1 = """ From b711de70138e3cda2bb0a85b2e19a851c7e2c5d7 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Thu, 22 Jan 2026 21:09:49 +0100 Subject: [PATCH 08/12] Testing the same example with regular classloader --- .../example/deps/ClassLoadingWorksTest.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 engine/runtime-integration-tests/src/test/java/org/enso/example/deps/ClassLoadingWorksTest.java diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/ClassLoadingWorksTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/ClassLoadingWorksTest.java new file mode 100644 index 000000000000..470cd171509a --- /dev/null +++ b/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/ClassLoadingWorksTest.java @@ -0,0 +1,22 @@ +package org.enso.example.deps; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ForkJoinPool; +import org.enso.example.deps.one.DepAClass; +import org.enso.example.deps.two.DepBClass; +import static org.junit.Assert.assertEquals; +import org.junit.Test; + +public class ClassLoadingWorksTest { + @Test + public void depADepB() throws Exception { + List> cases = new ArrayList<>(); + cases.add(() -> new DepAClass().expToNeg(2)); + cases.add(() -> new DepBClass().sigmoid(2)); + var results = ForkJoinPool.commonPool().invokeAll(cases); + assertEquals(results.get(0).get().doubleValue(), 0.1353352832366127, 0); + assertEquals(results.get(1).get().doubleValue(), 0.8807970779778823, 0); + } +} From 5510bd0db40c211bbedc5b6fadaf9160a6ef8b80 Mon Sep 17 00:00:00 2001 From: Hubert P Date: Fri, 23 Jan 2026 13:11:04 +0100 Subject: [PATCH 09/12] Revert "Testing the same example with regular classloader" This reverts commit b711de70138e3cda2bb0a85b2e19a851c7e2c5d7. --- .../example/deps/ClassLoadingWorksTest.java | 22 ------------------- 1 file changed, 22 deletions(-) delete mode 100644 engine/runtime-integration-tests/src/test/java/org/enso/example/deps/ClassLoadingWorksTest.java diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/ClassLoadingWorksTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/ClassLoadingWorksTest.java deleted file mode 100644 index 470cd171509a..000000000000 --- a/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/ClassLoadingWorksTest.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.enso.example.deps; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.ForkJoinPool; -import org.enso.example.deps.one.DepAClass; -import org.enso.example.deps.two.DepBClass; -import static org.junit.Assert.assertEquals; -import org.junit.Test; - -public class ClassLoadingWorksTest { - @Test - public void depADepB() throws Exception { - List> cases = new ArrayList<>(); - cases.add(() -> new DepAClass().expToNeg(2)); - cases.add(() -> new DepBClass().sigmoid(2)); - var results = ForkJoinPool.commonPool().invokeAll(cases); - assertEquals(results.get(0).get().doubleValue(), 0.1353352832366127, 0); - assertEquals(results.get(1).get().doubleValue(), 0.8807970779778823, 0); - } -} From de0ebe1fc84ee598c25507be575091187efb2633 Mon Sep 17 00:00:00 2001 From: Hubert P Date: Fri, 23 Jan 2026 13:11:05 +0100 Subject: [PATCH 10/12] Revert "Some timeouts to not block forever" This reverts commit 3de7b6becd3d9466e8cbd6352f59ec5fc32812b8. --- .../src/test/java/org/enso/example/deps/AwaitLoading.java | 3 +-- .../org/enso/interpreter/test/interop/JavaInteropTest.java | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/AwaitLoading.java b/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/AwaitLoading.java index 61b7c8830188..85627cf2c9c5 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/AwaitLoading.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/AwaitLoading.java @@ -3,7 +3,6 @@ import static org.junit.Assert.fail; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; public final class AwaitLoading { private static final CountDownLatch TWO = new CountDownLatch(2); @@ -13,7 +12,7 @@ private AwaitLoading() {} public static void waitForTwo() { TWO.countDown(); try { - TWO.await(1, TimeUnit.SECONDS); + TWO.await(); } catch (InterruptedException ex) { fail("The test failed with " + ex); } diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/interop/JavaInteropTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/interop/JavaInteropTest.java index 7c7f98622cb6..048517bcd839 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/interop/JavaInteropTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/interop/JavaInteropTest.java @@ -506,7 +506,7 @@ public void catchCheckedSubExceptionThrownInJava() { assertEquals(result.asInt(), -1); } - @Test(timeout = 10000) + @Test public void multiThreadedAccess() throws Exception { var code1 = """ From 745199c0d95bcdc956ac93fe371bdef3cbea071f Mon Sep 17 00:00:00 2001 From: Hubert P Date: Fri, 23 Jan 2026 13:11:07 +0100 Subject: [PATCH 11/12] Revert "Initialization of the classes must depend on the other one" This reverts commit 3a43d3fad29d468f4e22a9d30a97e3187b8294fc. --- .../test/java/org/enso/example/deps/one/DepAClass.java | 5 +---- .../test/java/org/enso/example/deps/two/DepBClass.java | 9 +++------ .../enso/interpreter/test/interop/JavaInteropTest.java | 4 ++-- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/one/DepAClass.java b/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/one/DepAClass.java index 0e7ce2b7a642..826ec62ea0d7 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/one/DepAClass.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/one/DepAClass.java @@ -4,17 +4,14 @@ import org.enso.example.deps.two.DepBClass; public class DepAClass { - private static final DepBClass OTHER; - static { System.err.println("Started DepAClass"); AwaitLoading.waitForTwo(); System.err.println("Loading DepAClass"); - OTHER = new DepBClass(); System.err.println("Finished DepAClass"); } public static double expToNeg(double x) { - return OTHER.expPow(-x); + return DepBClass.expPow(-x); } } diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/two/DepBClass.java b/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/two/DepBClass.java index 3313cd909edb..965ca0939548 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/two/DepBClass.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/two/DepBClass.java @@ -4,21 +4,18 @@ import org.enso.example.deps.one.DepAClass; public class DepBClass { - private static final DepAClass OTHER; - static { System.err.println("Started DepBClass"); AwaitLoading.waitForTwo(); System.err.println("Loading DepBClass"); - OTHER = new DepAClass(); System.err.println("Finished DepBClass"); } - public double sigmoid(double x) { - return 1 / (1 + OTHER.expToNeg(x)); + public static double sigmoid(double x) { + return 1 / (1 + DepAClass.expToNeg(x)); } - public double expPow(double x) { + public static double expPow(double x) { return Math.exp(x); } } diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/interop/JavaInteropTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/interop/JavaInteropTest.java index 048517bcd839..1ec8ce5e8acb 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/interop/JavaInteropTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/interop/JavaInteropTest.java @@ -514,7 +514,7 @@ public void multiThreadedAccess() throws Exception { polyglot java import org.enso.example.deps.one.DepAClass main = - DepAClass.new.expToNeg 2 + DepAClass.expToNeg 2 """; var code2 = """ @@ -522,7 +522,7 @@ public void multiThreadedAccess() throws Exception { polyglot java import org.enso.example.deps.two.DepBClass main = - DepBClass.new.sigmoid 2 + DepBClass.sigmoid 2 """; List> cases = new ArrayList<>(); cases.add(() -> ctx().evalModule(code1).asDouble()); From b0050888ce0df368a9691c548e1026196ae52e2c Mon Sep 17 00:00:00 2001 From: Hubert P Date: Fri, 23 Jan 2026 13:11:08 +0100 Subject: [PATCH 12/12] Revert "Need both classes to start (and not finish) static class initialization at once" This reverts commit 7e63f8277285783457bf101136694b21b09ab931. --- .../org/enso/example/deps/AwaitLoading.java | 20 ------------------- .../org/enso/example/deps/one/DepAClass.java | 8 -------- .../org/enso/example/deps/two/DepBClass.java | 8 -------- 3 files changed, 36 deletions(-) delete mode 100644 engine/runtime-integration-tests/src/test/java/org/enso/example/deps/AwaitLoading.java diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/AwaitLoading.java b/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/AwaitLoading.java deleted file mode 100644 index 85627cf2c9c5..000000000000 --- a/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/AwaitLoading.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.enso.example.deps; - -import static org.junit.Assert.fail; - -import java.util.concurrent.CountDownLatch; - -public final class AwaitLoading { - private static final CountDownLatch TWO = new CountDownLatch(2); - - private AwaitLoading() {} - - public static void waitForTwo() { - TWO.countDown(); - try { - TWO.await(); - } catch (InterruptedException ex) { - fail("The test failed with " + ex); - } - } -} diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/one/DepAClass.java b/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/one/DepAClass.java index 826ec62ea0d7..2bb75c387e59 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/one/DepAClass.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/one/DepAClass.java @@ -1,16 +1,8 @@ package org.enso.example.deps.one; -import org.enso.example.deps.AwaitLoading; import org.enso.example.deps.two.DepBClass; public class DepAClass { - static { - System.err.println("Started DepAClass"); - AwaitLoading.waitForTwo(); - System.err.println("Loading DepAClass"); - System.err.println("Finished DepAClass"); - } - public static double expToNeg(double x) { return DepBClass.expPow(-x); } diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/two/DepBClass.java b/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/two/DepBClass.java index 965ca0939548..1c961785faa1 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/two/DepBClass.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/example/deps/two/DepBClass.java @@ -1,16 +1,8 @@ package org.enso.example.deps.two; -import org.enso.example.deps.AwaitLoading; import org.enso.example.deps.one.DepAClass; public class DepBClass { - static { - System.err.println("Started DepBClass"); - AwaitLoading.waitForTwo(); - System.err.println("Loading DepBClass"); - System.err.println("Finished DepBClass"); - } - public static double sigmoid(double x) { return 1 / (1 + DepAClass.expToNeg(x)); }