I've used to close a socket to stop a server asynchronously. This no longer works.
package com.example.bug;
import java.io.Closeable;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class SocketInvalidArgument implements Closeable {
public static void main(String[] args) throws IOException, InterruptedException {
for (int i = 0; i<1000; i++) {
try (SocketInvalidArgument serve = new SocketInvalidArgument()) {
Thread.sleep(i);
}
}
}
private final ServerSocket socket;
private final Thread acceptThread;
public SocketInvalidArgument() throws IOException {
socket = new ServerSocket(0);
acceptThread = new Thread(() -> {
try (Closeable closeable = socket) {
while (true) {
try {
try (Socket client = socket.accept()) { // Hangs indefinitely
// The client is immediately closed, not needed for this test
}
} catch (Exception e) {
if (!socket.isClosed()) {
e.printStackTrace();
}
break;
}
}
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
});
acceptThread.start();
}
@Override
public void close() throws IOException {
try {
socket.close();
socket.close();
socket.close();
} finally {
try {
acceptThread.join(); // Hangs indefinitely
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IOException(e);
}
}
}
}
It consistently hangs indefinitely on first or second iteration.
"main" #1 [44] prio=5 os_prio=0 cpu=3511.20ms elapsed=769.07s tid=0x00002aaab002a920 nid=44 in Object.wait() [0x00002aaaacc8f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait0(java.base@21.0.7/Native Method)
- waiting on <0x000000008bf7b0d0> (a java.lang.Thread)
at java.lang.Object.wait(java.base@21.0.7/Object.java:366)
at java.lang.Thread.join(java.base@21.0.7/Thread.java:2079)
- locked <0x000000008bf7b0d0> (a java.lang.Thread)
at java.lang.Thread.join(java.base@21.0.7/Thread.java:2155)
at com.example.bug.SocketInvalidArgument.close(SocketInvalidArgument.java:55)
at com.example.bug.SocketInvalidArgument.main(SocketInvalidArgument.java:15)
at java.lang.invoke.LambdaForm$DMH/0x00002aab3d030c00.invokeStatic(java.base@21.0.7/LambdaForm$DMH)
at java.lang.invoke.LambdaForm$MH/0x00002aab3d14d000.invoke(java.base@21.0.7/LambdaForm$MH)
at java.lang.invoke.Invokers$Holder.invokeExact_MT(java.base@21.0.7/Invokers$Holder)
at jdk.internal.reflect.DirectMethodHandleAccessor.invokeImpl(java.base@21.0.7/DirectMethodHandleAccessor.java:154)
at jdk.internal.reflect.DirectMethodHandleAccessor.invoke(java.base@21.0.7/DirectMethodHandleAccessor.java:103)
at java.lang.reflect.Method.invoke(java.base@21.0.7/Method.java:580)
at com.sun.tools.javac.launcher.Main.execute(jdk.compiler@21.0.7/Main.java:484)
at com.sun.tools.javac.launcher.Main.run(jdk.compiler@21.0.7/Main.java:208)
at com.sun.tools.javac.launcher.Main.main(jdk.compiler@21.0.7/Main.java:135)
"Thread-0" #20 [65] prio=5 os_prio=0 cpu=2.91ms elapsed=765.22s tid=0x00002aaab0603520 nid=65 runnable [0x00002aabefd7f000]
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.Net.accept(java.base@21.0.7/Native Method)
at sun.nio.ch.NioSocketImpl.accept(java.base@21.0.7/NioSocketImpl.java:748)
at java.net.ServerSocket.implAccept(java.base@21.0.7/ServerSocket.java:698)
at java.net.ServerSocket.platformImplAccept(java.base@21.0.7/ServerSocket.java:663)
at java.net.ServerSocket.implAccept(java.base@21.0.7/ServerSocket.java:639)
at java.net.ServerSocket.implAccept(java.base@21.0.7/ServerSocket.java:585)
at java.net.ServerSocket.accept(java.base@21.0.7/ServerSocket.java:543)
at com.example.bug.SocketInvalidArgument.lambda$new$0(SocketInvalidArgument.java:29)
at com.example.bug.SocketInvalidArgument$$Lambda/0x00002aab3d14c238.run(Unknown Source)
at java.lang.Thread.runWith(java.base@21.0.7/Thread.java:1596)
at java.lang.Thread.run(java.base@21.0.7/Thread.java:1583)
To reproduce:
docker run --rm --platform=linux/x86_64 -ti maven:3.9.9-eclipse-temurin-21 /bin/bash
cat > SocketInvalidArgument.java # paste the code
java SocketInvalidArgument.java
It works as expected (creates a bunch of servers and shuts them down after a delay) on other platforms.
For example, if instead of x86_64 architecture I use Ubuntu image for aarh64, the sample works fine.
Did you check if any "socket.close()" actually goes through and changes the socket's state? If the first one throws exception (with information that might be the clue), execution would immediately go to the finally block and the stack traces would look the same.