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

Impossibile to build Undertow based native image due to unsafe.staticFieldBase() on static volatile int fails with UnsupportedFeatureError #3020

Closed
ujibang opened this issue Nov 24, 2020 · 12 comments
Assignees

Comments

@ujibang
Copy link

ujibang commented Nov 24, 2020

Describe the issue

the native image of a project that uses undertow, fails at runtime with following error:

Exception in thread "main" com.oracle.svm.core.jdk.UnsupportedFeatureError: Unsupported method of Unsafe
	at com.oracle.svm.core.util.VMError.unsupportedFeature(VMError.java:87)
	at jdk.internal.misc.Unsafe.staticFieldBase(Unsafe.java:236)
	at sun.misc.Unsafe.staticFieldBase(Unsafe.java:677)
	at org.jboss.threads.EnhancedQueueExecutor.<clinit>(EnhancedQueueExecutor.java:295)
	at com.oracle.svm.core.classinitialization.ClassInitializationInfo.invokeClassInitializer(ClassInitializationInfo.java:351)
	at com.oracle.svm.core.classinitialization.ClassInitializationInfo.initialize(ClassInitializationInfo.java:271)
	at org.xnio.XnioWorker.<init>(XnioWorker.java:139)
	at org.xnio.nio.NioXnioWorker.<init>(NioXnioWorker.java:84)
	at org.xnio.nio.NioXnio.build(NioXnio.java:233)
	at org.xnio.XnioWorker$Builder.build(XnioWorker.java:1191)
	at org.xnio.Xnio.createWorker(Xnio.java:481)
	at org.xnio.Xnio.createWorker(Xnio.java:463)
	at org.xnio.Xnio.createWorker(Xnio.java:450)
	at io.undertow.Undertow.start(Undertow.java:122)
	at com.softinstigate.App.main(App.java:25)

The problem comes from EnhancedQueueExecutor class of jboss-threads that uses

static volatile int sequence = 1;

...

sequence  = unsafe.staticFieldBase(EnhancedQueueExecutor.class.getDeclaredField("sequence"));

Steps to reproduce the issue
Please include both build steps as well as run steps

  1. git clone --depth 1 https://github.com/SoftInstigate/graalvm-undertow-issue.git
  2. cd graalvm-undertow-issue
  3. mvn clean package -Pnative
  4. ./target/graalvm-undertow-issue-native

Describe GraalVM and your environment:

  • GraalVM version: CE 20.3.0
  • JDK major version: 11
$ java -version
openjdk version "11.0.9" 2020-10-20
OpenJDK Runtime Environment GraalVM CE 20.3.0 (build 11.0.9+10-jvmci-20.3-b06)
OpenJDK 64-Bit Server VM GraalVM CE 20.3.0 (build 11.0.9+10-jvmci-20.3-b06, mixed mode, sharing)
  • OS: macOS Big Sur, also reproduced on Linux Debian
  • Architecture: AMD64

More details
native-image succeeds. the error is at runtime with reported exception.

Workaroud

Downgrading xnio library to version 3.5.9.Final removes transitive dependency to joss-threads:2.3.0.Beta2 that introduces failing EnhancedQueueExecutor class

 <dependency>
      <groupId>org.jboss.xnio</groupId>
      <artifactId>xnio-api</artifactId>
      <version>3.5.9.Final</version>
 </dependency>
 <dependency>
      <groupId>org.jboss.xnio</groupId>
      <artifactId>xnio-nio</artifactId>
      <version>3.5.9.Final</version>
 </dependency>
@ujibang
Copy link
Author

ujibang commented Jan 23, 2021

I tested it again with GraalVM 21.0.0, and the issue has been resolved.

sorry my mistake, the issue persists with 21.0.0 as well!

@warrenc5
Copy link

warrenc5 commented Jan 23, 2021

Firstly thanks so much for serialization support in 21.0.0 @ziyilin

I'm also seeing this issues problem with the jboss river code below. It's doing some heavy reflection.

I'd be happy to experiment with @Delete or @Alias trying to short circuit it somehow. but on Unsafe or SerializableClass? Any suggestions?

https://github.com/jboss-remoting/jboss-marshalling/blob/a1d1c0a26e9eed04de624281d5c5a2852bc0e962/api/src/main/java/org/jboss/marshalling/reflect/SerializableClass.java

Unsupported method of Unsafe
at com.oracle.svm.core.util.VMError.unsupportedFeature(VMError.java:87)
at jdk.internal.misc.Unsafe.staticFieldBase(Unsafe.java:236)
at sun.misc.Unsafe.staticFieldBase(Unsafe.java:677)
at org.jboss.marshalling.reflect.SerializableClass.getDeclaredSerialPersistentFields(SerializableClass.java:143)
at org.jboss.marshalling.reflect.SerializableClass.getSerializableFields(SerializableClass.java:96)
at org.jboss.marshalling.reflect.SerializableClass.(SerializableClass.java:88)
at org.jboss.marshalling.reflect.SerializableClassRegistry$1.computeValue(SerializableClassRegistry.java:62)
at org.jboss.marshalling.reflect.SerializableClassRegistry$1.computeValue(SerializableClassRegistry.java:59)
at java.lang.ClassValue.get(JavaLangSubstitutions.java:602)
at org.jboss.marshalling.reflect.SerializableClassRegistry.lookup(SerializableClassRegistry.java:83)
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:178)

btw @ujibang I found this interesting read http://java-performance.info/updating-final-and-static-final-fields/

@ziyilin
Copy link
Collaborator

ziyilin commented Jan 25, 2021

but on Unsafe or SerializableClass?

I think on SerializableClass is easier. You can substitute org.jboss.marshalling.reflect.SerializableClass .getDeclaredSerialPersistentFields by using reflection instead of unsafe to get the static field value.

@warrenc5
Copy link

warrenc5 commented Jan 25, 2021

Thanks. I've started with this ... to avoid the problem. And it seems to progress.

import com.oracle.svm.core.annotate.Substitute;
import com.oracle.svm.core.annotate.TargetClass;
import java.io.ObjectStreamField;

@TargetClass(org.jboss.marshalling.reflect.SerializableClass.class)
@SuppressWarnings({"unused"})
final class Target_org_jboss_marshalling_reflect_SerializableClass {

    @Substitute(polymorphicSignature = false)
    public static ObjectStreamField[] getDeclaredSerialPersistentFields(Class<?> clazz) {

        return new ObjectStreamField[0];
    }

}

However https://github.com/jbossas/jboss-threads/blob/3.2/src/main/java/org/jboss/threads/EnhancedQueueExecutor.java

Uses static initializers (cinit) to do this
terminationWaitersOffset = unsafe.objectFieldOffset(EnhancedQueueExecutor.class.getDeclaredField("terminationWaiters"));

Where unsafe is a com.sun.misc.Unsafe static field in

https://github.com/jbossas/jboss-threads/blob/2.4/src/main/java/org/jboss/threads/JBossExecutors.java

So maybe @ujibang this will need more thought and advice.

Btw I also notice ... which make me think there is some hope :) Maybe @baranowb from Redhat can comment? :)

static final boolean DISABLE_MBEAN = readBooleanPropertyPrefixed("disable-mbean", readProperty("org.graalvm.nativeimage.imagecode", null) != null);

@warrenc5
Copy link

Ps @ziyilin I'm guessing serialization-config.json looks like ... but I haven't seen one anywhere?

[
    {
        "name": "javax.management.ObjectName"
    },
    {
        "name": "java.util.AbstractMap"
    }
]

@ziyilin
Copy link
Collaborator

ziyilin commented Jan 25, 2021

Native-image-agent monitors ObjectStreamClass.<init>(Ljava/lang/Class;) to record any serialization/deserialization when it happens. If you didn't find ObjectName and AbstractMap in the configuration file, it probably because the code of their serialization is not hit in the agent monitoring run.

@warrenc5
Copy link

Ok - that's clever. Thank you.
I found https://medium.com/graalvm/graalvm-21-0-introducing-a-new-way-to-run-java-df894256de28
saying
JAVA_OPTS="-agentlib:native-image-agent=config-output-dir=config/META-INF/native-image"

@warrenc5
Copy link

@ujibang

Yes! you can disable the XNIO from using the Jboss ThreadExecutor

  System.setProperty("jboss.threads.eqe.disable", Boolean.toString(true));

However EnhancedQueueExecutor is using unsafe in its static initializer.

So we have to find a way to delete/replace that or alias unsafe field

import static org.jboss.threads.JBossExecutors.unsafe;

@warrenc5
Copy link

warrenc5 commented Jan 27, 2021

Ok basically just removed these calls. See PR below

package svm;

import com.oracle.svm.core.annotate.Substitute;
import com.oracle.svm.core.annotate.TargetClass;
import java.lang.reflect.Field;

@TargetClass(value = sun.misc.Unsafe.class)
final class Target_sun_misc_Unsafe {

    @Substitute
    public Object staticFieldBase(Field f) {
        return null;
    }

    @Substitute
    public long staticFieldOffset(Field f) {
        return -1;
    }

}

SoftInstigate/graalvm-undertow-issue#1

@warrenc5
Copy link

Can probably close this issue now. @ujibang @ziyilin

@ujibang
Copy link
Author

ujibang commented Jan 27, 2021

I confirm this fixes the issue. Great job @warrenc5 and @ziyilin!

@ujibang ujibang closed this as completed Jan 27, 2021
@cstancu
Copy link
Member

cstancu commented Apr 7, 2021

f97bdb5 adds the missing substitutions.

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

No branches or pull requests

5 participants