import java.io.IOException;
import java.net.StandardProtocolFamily;
import java.net.UnixDomainSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.CountDownLatch;


public class ExceptionBugTest {
    public static void main(String[] args) throws Exception {
        test(false);
        test(true);
    }

    private static void test(boolean delay) throws Exception {
        System.err.flush();
        System.out.println("Running with delay: " + delay);
        System.out.flush();

        Path socketPath = Files.createTempFile("bug", "test");
        Files.delete(socketPath);
        UnixDomainSocketAddress addr = UnixDomainSocketAddress.of(socketPath);
        try {
            @SuppressWarnings("resource")
            var serverChannel = ServerSocketChannel.open(StandardProtocolFamily.UNIX);
            serverChannel.bind(addr);

            CountDownLatch threadsReady = new CountDownLatch(2);
            Thread serverThread = Thread.ofVirtual().start(() -> {
                threadsReady.countDown();

                try (SocketChannel channel = serverChannel.accept()) {
                    // close immediately
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });

            try (SocketChannel channel = SocketChannel.open(StandardProtocolFamily.UNIX)) {
                channel.connect(addr);

                Thread writeThread = Thread.ofVirtual().start(() -> {
                    try {
                        threadsReady.countDown();
                        int written = channel.write(ByteBuffer.allocate(1));

                        // occasionally succeeds
                        System.out.println("write returned without exception: " + written
                                + " (please re-run test)");
                    } catch (IOException e) {
                        // delay=false:
                        // will usually fail with either ClosedChannelException, AsynchronousCloseException,
                        // or ClosedByInterruptException
                        //
                        // delay=true:
                        // will usually fail with "java.io.IOException: Broken pipe"
                        // but also sometimes with "java.io.IOException: Socket is not connected"
                        e.printStackTrace();
                    }
                });

                threadsReady.await();
                if (delay) {
                    Thread.sleep(500);
                }

                // serverThread.interrupt();
                writeThread.interrupt();
            } finally {
                try {
                    serverChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                serverThread.join();
            }

        } finally {
            Files.deleteIfExists(socketPath);
        }
    }
}
