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

[GR-45893] jdk.net.ExtendedSocketOptions.TCP_KEEPIDLE not available in native-image application #6457

Closed
beckermarc opened this issue Apr 20, 2023 · 7 comments
Assignees

Comments

@beckermarc
Copy link

I am facing issues using jdk.net.ExtendedSocketOptions.TCP_KEEPIDLE in native images. The following code runs fine on a JVM, but fails in a native image:

AsynchronousSocketChannel socket = AsynchronousSocketChannel.open();
socket.setOption(jdk.net.ExtendedSocketOptions.TCP_KEEPIDLE, 100);

Error in native-image:

Exception in thread "main" java.lang.UnsupportedOperationException: 'TCP_KEEPIDLE' not supported
        at [email protected]/sun.nio.ch.AsynchronousSocketChannelImpl.setOption(AsynchronousSocketChannelImpl.java:461)
        at com.example.repro.Application.main(Application.java:15)

The class jdk.net.ExtendedSocketOptions registers some platform-dependent optionally available socket options. All of this is orchestrated by sun.net.ext.ExtendedSocketOptions, which dynamically loads jdk.net.ExtendedSocketOptions if available. jdk.net.ExtendedSocketOptions then performs initialization of these additional options in a static code block.

For some reason, this static code block is not properly executed in the native-image. I know about build-time initialization of classes in native-images, but do not fully understand if this has an impact here.

To enable dynamic lookup of jdk.net.ExtendedSocketOptions I also put the following information into a reflect-config.json:

[
    {
        "name": "jdk.net.ExtendedSocketOptions"
    }
]

Below I have provided a simple application to reproduce the issue. There I have provided additional debug output, which checks the conditions that check some boundary conditions for the exception to occur:

AsynchronousSocketChannel socket = AsynchronousSocketChannel.open();
Set<SocketOption<?>> options = socket.supportedOptions();
System.out.println("Has TCP_KEEPIDLE? " + options.contains(jdk.net.ExtendedSocketOptions.TCP_KEEPIDLE));
System.out.println("Options: " + options);

Interestingly this produces the following output in a native-image:

Has TCP_KEEPIDLE? false
Options: [TCP_NODELAY, TCP_KEEPCOUNT, TCP_KEEPINTERVAL, SO_REUSEADDR, SO_SNDBUF, SO_RCVBUF, TCP_KEEPIDLE, SO_INCOMING_NAPI_ID, TCP_QUICKACK, SO_KEEPALIVE, SO_REUSEPORT]

Notice that the contains check evaluates to false, however the stringified options DO INCLUDE TCP_KEEPIDLE. Maybe this helps in finding the issue. My guess is this has something to do with build-time class initialization and code optimization, but I don't fully understand this behaviour.

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

  1. Unpack the application: tcp-keepidle-repro.zip
  2. mvn native:compile
  3. java -jar target/tcp-keepaliveidle-0.0.1-SNAPSHOT.jar -> works
  4. target/tcp-keepaliveidle -> fails

Describe GraalVM and your environment:

  • GraalVM version: OpenJDK 64-Bit Server VM GraalVM CE 17.0.7-dev+4.1 (build 17.0.7+4-jvmci-23.0-b10, mixed mode, sharing)
  • JDK major version: 17
  • OS: Ubuntu 22.04
  • Architecture: AMD64
@beckermarc
Copy link
Author

beckermarc commented Apr 21, 2023

I added some more output to my repro application, which better explains the current behaviour and points more to a bug:

AsynchronousSocketChannel socket = AsynchronousSocketChannel.open();
Set<SocketOption<?>> options = socket.supportedOptions();

System.out.println("Has TCP_KEEPIDLE (obj)? " + options.contains(jdk.net.ExtendedSocketOptions.TCP_KEEPIDLE));
System.out.println("Has TCP_KEEPIDLE (string)? " + options.stream().anyMatch(s -> s.name().equals(jdk.net.ExtendedSocketOptions.TCP_KEEPIDLE.name())));
System.out.println("Options: " + options);

SocketOption<?> so = options.stream().filter(s -> s.name().equals(jdk.net.ExtendedSocketOptions.TCP_KEEPIDLE.name())).findFirst().get();
System.out.println("Constant identity: " + System.identityHashCode(jdk.net.ExtendedSocketOptions.TCP_KEEPIDLE));
System.out.println("Set identity: " + System.identityHashCode(so));
socket.setOption(jdk.net.ExtendedSocketOptions.TCP_KEEPIDLE, 100);

The code above produces the following output when run on the JVM:

Has TCP_KEEPIDLE (obj)? true
Has TCP_KEEPIDLE (string)? true
Options: [TCP_KEEPCOUNT, TCP_QUICKACK, SO_KEEPALIVE, SO_REUSEADDR, SO_INCOMING_NAPI_ID, TCP_KEEPIDLE, SO_SNDBUF, SO_RCVBUF, SO_REUSEPORT, TCP_NODELAY, TCP_KEEPINTERVAL]
Constant identity: 186276003
Set identity: 186276003

In a native-image it produces the following output:

Has TCP_KEEPIDLE (obj)? false
Has TCP_KEEPIDLE (string)? true
Options: [SO_KEEPALIVE, TCP_NODELAY, TCP_KEEPINTERVAL, SO_REUSEADDR, SO_REUSEPORT, TCP_QUICKACK, SO_RCVBUF, TCP_KEEPCOUNT, SO_SNDBUF, SO_INCOMING_NAPI_ID, TCP_KEEPIDLE]
Constant identity: 1710198919
Set identity: 95685867
Exception in thread "main" java.lang.UnsupportedOperationException: 'TCP_KEEPIDLE' not supported
        at [email protected]/sun.nio.ch.AsynchronousSocketChannelImpl.setOption(AsynchronousSocketChannelImpl.java:461)
        at com.example.repro.Application.main(Application.java:29)

As you can see there exist two object instances for TCP_KEEPIDLE in the native-image. However the JDK source code relies on object identity comparison (the relevant class doesn't implement hashCode and equals) and uses the public static final SocketOption<Integer> TCP_KEEPINTERVAL declared object everywhere. Can you explain why there exist two different instances in the native-image?

@oubidar-Abderrahim oubidar-Abderrahim self-assigned this Apr 27, 2023
@oubidar-Abderrahim
Copy link
Member

Thank you for reporting this, This is tracked internally on GR 45893

@beckermarc
Copy link
Author

Hey, just wanted to ask if there is any update on this issue?
Have you been able to identify any clues what is causing this issue (and maybe potential workarounds for the time being)?
Thanks!

@fniephaus fniephaus changed the title jdk.net.ExtendedSocketOptions.TCP_KEEPIDLE not available in native-image application [GR-45893] jdk.net.ExtendedSocketOptions.TCP_KEEPIDLE not available in native-image application Sep 13, 2023
graalvmbot pushed a commit that referenced this issue Sep 18, 2023
@fniephaus
Copy link
Member

Can you explain why there exist two different instances in the native-image?

Because one instance was created at build-time, and the other at run-time. More specifically, it is sun.nio.ch.AsynchronousSocketChannelImpl$DefaultOptionsHolder that is initialized at build-time and then incorrectly persisted as part of the executable. b6654d0 makes sure its class initializer runs again at run-time, so that the set is reinitialized with the correct values. With my patch, I now get:

tcp-keepidle-repro$ ./target/tcp-keepaliveidle 
Is Linux? true
Has TCP_KEEPIDLE? true
Options: [TCP_QUICKACK, SO_RCVBUF, SO_KEEPALIVE, TCP_NODELAY, TCP_KEEPCOUNT, SO_SNDBUF, TCP_KEEPIDLE, SO_REUSEPORT, SO_INCOMING_NAPI_ID, TCP_KEEPINTERVAL, SO_REUSEADDR]
Constant identity: 164063280
Set identity: 164063280

If you want to try this out now, you need to build Native Image from source with my patch. Soon after #7440 is merged, the latest dev build will ship the fix.

Hope this helps!

-- Fabio

@beckermarc
Copy link
Author

Awesome! Thanks for looking into this :)
Can this reinitialization also be configured somehow in native-image.properties, as a workaround for now?

@fniephaus
Copy link
Member

Yes, I believe you can use --initialize-at-run-time=sun.nio.ch.AsynchronousSocketChannelImpl$DefaultOptionsHolder. You can add the flag via your pom.xml file like so:

<buildArgs>
	<buildArg>--initialize-at-run-time=sun.nio.ch.AsynchronousSocketChannelImpl$DefaultOptionsHolder</buildArg>
</buildArgs>

beckermarc added a commit to SAP-samples/cap-sflight that referenced this issue Sep 20, 2023
Removes previous workaround and replaces it with workaround suggested in oracle/graal#6457
@beckermarc
Copy link
Author

Thanks, yes this works!

beckermarc added a commit to SAP-samples/cap-sflight that referenced this issue Sep 20, 2023
Removes previous workaround and replaces it with workaround suggested in oracle/graal#6457
graalvmbot pushed a commit that referenced this issue Oct 20, 2023
c-kobo pushed a commit to c-kobo/cap-sflight that referenced this issue Nov 10, 2023
Removes previous workaround and replaces it with workaround suggested in oracle/graal#6457
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

3 participants