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

Exception when attempting to generate heap classes concurrently #33

Closed
moore-ryan opened this issue Sep 9, 2021 · 5 comments
Closed

Comments

@moore-ryan
Copy link

Encountered on jdk11.0.8, using chronicle-values:2.22ea1 (but also reproducible on chronicle-values:2.20.80)

When attempting to fetch multiple heap classes for the first time concurrently, it is possible to hit an exception that seems to be caused by a threading issue when defining / loading classes.

There seem to be two different exceptions that are thrown, depending on the order things get executed in:
First type of exception:

Exception in thread "Thread-0" java.lang.AssertionError: java.lang.LinkageError: loader 'app' attempted duplicate class definition for org.example.Value1$$Heap. (org.example.Value1$$Heap is in unnamed module of loader 'app')
	at net.openhft.chronicle.values.CompilerUtils.defineClass(CompilerUtils.java:87)
	at net.openhft.chronicle.values.CachedCompiler.loadFromJava(CachedCompiler.java:95)
	at net.openhft.chronicle.values.ValueModel.createClass(ValueModel.java:326)
	at net.openhft.chronicle.values.ValueModel.createHeapClass(ValueModel.java:310)
	at net.openhft.chronicle.values.ValueModel.heapClass(ValueModel.java:300)
	at org.example.ValueInitTest.lambda$main$0(ValueInitTest.java:21)
	at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.LinkageError: loader 'app' attempted duplicate class definition for org.example.Value1$$Heap. (org.example.Value1$$Heap is in unnamed module of loader 'app')
	at java.base/java.lang.ClassLoader.defineClass1(Native Method)
	at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1017)
	at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:878)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at net.openhft.chronicle.values.CompilerUtils.defineClass(CompilerUtils.java:82)
	... 6 more

Second type of exception:

Exception in thread "Thread-0" Exception in thread "Thread-1" net.openhft.chronicle.values.ImplGenerationFailedException: java.lang.ClassNotFoundException: org.example.Value1$$Heap
	at net.openhft.chronicle.values.ValueModel.createClass(ValueModel.java:329)
	at net.openhft.chronicle.values.ValueModel.createHeapClass(ValueModel.java:310)
	at net.openhft.chronicle.values.ValueModel.heapClass(ValueModel.java:300)
	at org.example.ValueInitTest.lambda$main$0(ValueInitTest.java:21)
	at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.ClassNotFoundException: org.example.Value1$$Heap
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
	at net.openhft.chronicle.values.CachedCompiler.loadFromJava(CachedCompiler.java:101)
	at net.openhft.chronicle.values.ValueModel.createClass(ValueModel.java:326)
	... 4 more

Test code (using chronicle-values:2.22ea1):

package org.example

import net.openhft.chronicle.values.ValueModel;

public class ValueInitTest {

    static Class[] tests = {
            Value1.class,
            Value2.class
    };

    public static void main(String[] args) {

        // This works:
        // for(Class test : tests) {
        //   ValueModel.acquire(test).heapClass();
        // }

        // This does not usually work:
        for(Class test : tests) {
            final Thread t = new Thread(() -> ValueModel.acquire(test).heapClass());
            t.start();
        }
    }
}

The layout of the value interfaces aren't really important, so long as they need to be compiled

Value1 interface:

package org.example

public interface Value1 {
    int getValue();
    void setValue(int value);
}

Value2 interface:

package org.example

public interface Value2 {
    int getValue();
    void setValue(int value);
}
@JerryShea
Copy link
Contributor

JerryShea commented Nov 1, 2022

@moore-ryan we have fixed some concurrency bugs in Java-Runtime-Compiler. Can you reproduce with the latest code?

@moore-ryan
Copy link
Author

@JerryShea Thank you for the response.
I am still able to reproduce the issue with both chronicle-values:2.23.3 and with chronicle-values:2.24ea0

@JerryShea
Copy link
Contributor

JerryShea commented Nov 1, 2022

Quick response ;) Thanks @moore-ryan

I thought we were using Java-Runtime-Compiler (which is thread-safe) but we were not. I've made a PR to fix but will wait for review in case there was a reason not to have that dependency.

@moore-ryan
Copy link
Author

Excellent, thank you for the quick update!

@hft-team-city
Copy link
Contributor

Released in Chronicle-Values-2.24ea1, BOM-2.24ea86

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

3 participants