unixdomainchannels: added some unit tests unixdomainchannels
authormichaelm
Wed, 13 Nov 2019 11:06:17 +0000
branchunixdomainchannels
changeset 59052 15e9a570c6e6
parent 59048 4c3eb05c0701
child 59054 42bcf3042cd8
unixdomainchannels: added some unit tests
src/java.base/share/classes/sun/nio/ch/UnixDomainServerSocketChannelImpl.java
src/java.base/share/classes/sun/nio/ch/UnixDomainSocketChannelImpl.java
test/jdk/java/nio/channels/unixdomain/Basic.java
test/jdk/java/nio/channels/unixdomain/SocketOptions.java
--- a/src/java.base/share/classes/sun/nio/ch/UnixDomainServerSocketChannelImpl.java	Wed Nov 13 09:16:04 2019 +0000
+++ b/src/java.base/share/classes/sun/nio/ch/UnixDomainServerSocketChannelImpl.java	Wed Nov 13 11:06:17 2019 +0000
@@ -124,7 +124,6 @@
         private static Set<SocketOption<?>> defaultOptions() {
             HashSet<SocketOption<?>> set = new HashSet<>();
             set.add(StandardSocketOptions.SO_RCVBUF);
-            set.addAll(ExtendedSocketOptions.serverSocketOptions());
             return Collections.unmodifiableSet(set);
         }
     }
--- a/src/java.base/share/classes/sun/nio/ch/UnixDomainSocketChannelImpl.java	Wed Nov 13 09:16:04 2019 +0000
+++ b/src/java.base/share/classes/sun/nio/ch/UnixDomainSocketChannelImpl.java	Wed Nov 13 11:06:17 2019 +0000
@@ -149,7 +149,6 @@
             HashSet<SocketOption<?>> set = new HashSet<>();
             set.add(StandardSocketOptions.SO_SNDBUF);
             set.add(StandardSocketOptions.SO_RCVBUF);
-            set.add(StandardSocketOptions.SO_REUSEADDR); // TODO: DELETE ME
             set.add(StandardSocketOptions.SO_KEEPALIVE);
             set.add(StandardSocketOptions.SO_LINGER);
             return Collections.unmodifiableSet(set);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/nio/channels/unixdomain/Basic.java	Wed Nov 13 11:06:17 2019 +0000
@@ -0,0 +1,316 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8231358
+ * @run main/othervm Basic 32000 32000 nagle-off
+ * @run main/othervm Basic default default nagle-off
+ * @summary Basic test for Unix Domain and Inet socket and server socket channels
+ */
+
+import java.io.IOException;
+import java.net.*;
+import java.nio.ByteBuffer;
+import java.nio.channels.*;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.LinkedList;
+import java.util.List;
+
+public class Basic {
+    static int sockRxBufsize, sockTxBufsize;
+    static boolean nagle;
+
+    public static void main(String args[]) throws Exception {
+        if (args.length != 3)
+            usage();
+        sockRxBufsize = getInt(args[0]);
+        sockTxBufsize = getInt(args[1]);
+        if (args[2].equals("nagle-on"))
+            nagle = true;
+        else if (args[2].equals("nagle-off"))
+            nagle = false;
+
+        warmup();
+        test(128, 1000);
+        test(8 * 1024, 10000);
+        test(16 * 1024, 10000);
+        test(32 * 1024, 10000);
+        // repeat
+        System.out.println("---- Repeating all tests again -----");
+        test(128, 1000);
+        test(16 * 1024, 10000);
+        test(32 * 1024, 10000);
+    }
+
+    static int getInt(String s) {
+        if (s.equalsIgnoreCase("default"))
+            return -1;
+        else
+            return Integer.parseInt(s);
+    }
+
+    static void usage() {
+        System.out.println("usage: java Basic " +
+            "<kernel sock read buf size> <kernel sock send buf size> {nagle-on|nagle-off}");
+        System.out.println("nagle setting only affects TCP sockets");
+        System.exit(-1);
+    }
+
+    static void warmup() throws Exception {
+        Server server = new Server(StandardProtocolFamily.UNIX, 1024);
+        Client client = new Client(server, 128, 100);
+        server.start();
+        client.run();
+
+        server = new Server(StandardProtocolFamily.INET, 1024);
+        client = new Client(server, 128, 100);
+        server.start();
+        client.run();
+    }
+
+    static void test(int bufsize, int nbufs) throws Exception {
+        long unix = testUnix(bufsize, nbufs);
+        long inet = testInet(bufsize, nbufs);
+        // expect unix to be faster (express as percentage of inet)
+        long percent = (unix * 100) / inet;
+        System.out.printf ("Unix elapsed time is %d%% of the INET time\n\n", percent);
+    }
+
+    static long testUnix(int bufsize, int nbufs) throws Exception {
+        Server server = new Server(StandardProtocolFamily.UNIX, bufsize);
+        Client client = new Client(server, bufsize, nbufs);
+        System.out.printf("Test (family=unix bufsize=%d, nbufs=%d) ", bufsize, nbufs);
+        server.start();
+        client.run();
+        long unix = client.elapsed();
+        int sbuf = client.sendSocketBufsize();
+        int rbuf = client.receiveSocketBufsize();
+        System.out.printf("completed in %d ns (sbuf=%d, rbuf=%d)\n", unix, sbuf, rbuf);
+        System.gc();
+        return unix;
+    }
+
+    static long testInet(int bufsize, int nbufs) throws Exception {
+        Server server = new Server(StandardProtocolFamily.INET, bufsize);
+        Client client = new Client(server, bufsize, nbufs);
+        System.out.printf("Test (family=inet bufsize=%d, nbufs=%d) ", bufsize, nbufs);
+        server.start();
+        client.run();
+        long inet = client.elapsed();
+        System.gc();
+        int sbuf = client.sendSocketBufsize();
+        int rbuf = client.receiveSocketBufsize();
+        System.out.printf("completed in %d ns (sbuf=%d, rbuf=%d)\n", inet, sbuf, rbuf);
+        return inet;
+    }
+
+    static void setNagle(SocketChannel chan, boolean value) throws IOException {
+        if (chan.getRemoteAddress() instanceof InetSocketAddress) {
+            chan.setOption(StandardSocketOptions.TCP_NODELAY, value);
+        }
+    }
+
+    static void setSendBufferSize(SocketChannel chan, int bufsize) throws IOException {
+        if (bufsize < 0)
+            return;
+        chan.setOption(StandardSocketOptions.SO_SNDBUF, bufsize);
+    }
+
+    static void setRecvBufferSize(SocketChannel chan, int bufsize) throws IOException {
+        if (bufsize < 0)
+            return;
+        chan.setOption(StandardSocketOptions.SO_RCVBUF, bufsize);
+    }
+
+    static class Server extends Thread {
+
+        ServerSocketChannel server;
+        SocketAddress address;
+        SocketChannel connection;
+        SelectionKey ckey;
+        List<ByteBuffer> toSend = new LinkedList<>();
+        final int bufsize;
+
+        Server(ProtocolFamily family, int bufsize) throws IOException {
+            setDaemon(true);
+            SocketAddress addr;
+            this.bufsize = bufsize;
+            if (family == StandardProtocolFamily.UNIX) {
+                server = ServerSocketChannel.open(family);
+                Path sockfile = Path.of("server.sock");
+                Files.deleteIfExists(sockfile);
+                addr = new UnixDomainSocketAddress(sockfile);
+            } else {
+                server = ServerSocketChannel.open();
+                addr = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
+            }
+            server.bind(addr);
+            address = server.getLocalAddress();
+        }
+
+        SocketAddress getAddress() {
+            return address;
+        }
+
+        public void  run() {
+            try {
+                server.configureBlocking(false);
+                Selector sel = Selector.open();
+                server.register(sel, SelectionKey.OP_ACCEPT, server);
+
+                while (true) {
+                    int n = sel.select();
+                    var keys = sel.selectedKeys();
+                    for (SelectionKey key : keys) {
+                        if (key.isAcceptable()) {
+                            if (connection != null)
+                                throw new RuntimeException("One connection per server");
+                            ServerSocketChannel server = (ServerSocketChannel)key.attachment();
+                            connection = server.accept();
+                            connection.configureBlocking(false);
+                            setNagle(connection, nagle);
+                            setSendBufferSize(connection, sockTxBufsize);
+                            setRecvBufferSize(connection, sockRxBufsize);
+                            ckey = connection.register(sel, SelectionKey.OP_READ, connection);
+                        }
+                        if (key.isReadable()) {
+                            ByteBuffer buf = ByteBuffer.allocate(bufsize);
+                            SocketChannel channel = (SocketChannel)key.attachment();
+                            int m = channel.read(buf);
+                            if (m == -1) {
+                                channel.close();
+                                server.close();
+                                return;
+                            } else {
+                                buf.flip();
+                                // ECHO
+                                toSend.add(buf);
+                                ckey.interestOpsOr(SelectionKey.OP_WRITE);
+                            }
+                        }
+                        if (key.isWritable()) {
+                            if (toSend.isEmpty()) {
+                                ckey.interestOpsAnd(~SelectionKey.OP_WRITE);
+                            } else {
+                                ByteBuffer b = toSend.get(0);
+                                connection.write(b);
+                                if (b.remaining() == 0)
+                                    toSend.remove(0);
+                            }
+                        }
+                    }
+                    keys.clear();
+                }
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    static void fill(ByteBuffer buf) {
+        int n = buf.remaining();
+        for (int i=0; i<n; i++) {
+            buf.put((byte)(i % 127));
+        }
+    }
+
+    static void check(ByteBuffer buf, int expected, int iteration) throws Exception {
+        if (buf.remaining() != expected)
+            throw new RuntimeException();
+        for (int i=0; i<expected; i++) {
+            int b = buf.get();
+            if (b != (i % 127)) {
+                System.out.printf("b = %d , i = %d\n", b, i);
+                throw new RuntimeException("Iteration " + Integer.toString(iteration));
+            }
+        }
+    }
+
+// read until buf is full
+
+    static void readFully(SocketChannel chan, ByteBuffer buf) throws Exception {
+        int n = buf.remaining();
+        while (n > 0) {
+            int c = chan.read(buf);
+            if (c == -1)
+                throw new RuntimeException("EOF");
+            n -= c;
+        }
+    }
+
+    static class Client {
+        Server server;
+        int bufsize, nbufs, sbuf, rbuf;
+        long elapsed;
+
+        Client(Server server, int bufsize, int nbufs) {
+            this.server = server;
+            this.bufsize = bufsize;
+            this.nbufs = nbufs;
+        }
+
+        public void run() throws Exception {
+            SocketAddress remote = server.getAddress();
+            long start = System.nanoTime();
+            SocketChannel c = null;
+            String fam;
+            if (remote instanceof UnixDomainSocketAddress) {
+                c = SocketChannel.open(StandardProtocolFamily.UNIX);
+                fam = "unix";
+            } else {
+                c = SocketChannel.open();
+                fam = "inet";
+            }
+            setNagle(c, nagle);
+            c.connect(remote);
+            setSendBufferSize(c, sockTxBufsize);
+            setRecvBufferSize(c, sockRxBufsize);
+            ByteBuffer tx = ByteBuffer.allocate(bufsize);
+            ByteBuffer rx = ByteBuffer.allocate(bufsize);
+            fill(tx);
+            for (int i=0; i<nbufs; i++) {
+                tx.rewind();
+                c.write(tx);
+                rx.clear();
+                readFully(c, rx);
+                rx.flip();
+                check(rx, bufsize, i);
+            }
+            long end = System.nanoTime();
+            elapsed = end - start;
+            sbuf = c.getOption(StandardSocketOptions.SO_SNDBUF);
+            rbuf = c.getOption(StandardSocketOptions.SO_SNDBUF);
+            c.close();
+        }
+
+        long elapsed() {
+            return elapsed;
+        }
+
+        int receiveSocketBufsize() {return rbuf;}
+        int sendSocketBufsize() {return sbuf;}
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/nio/channels/unixdomain/SocketOptions.java	Wed Nov 13 11:06:17 2019 +0000
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8231358
+ * @run main SocketOptions
+ * @summary Socket option test
+ */
+
+import java.io.IOException;
+import java.net.*;
+import java.nio.ByteBuffer;
+import java.nio.channels.*;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Set;
+
+/**
+ * Check that all supported options can actually be set and got
+ */
+public class SocketOptions {
+
+    public static void main(String args[]) throws Exception {
+        test(ServerSocketChannel.open(StandardProtocolFamily.UNIX));
+        test(SocketChannel.open(StandardProtocolFamily.UNIX));
+    }
+
+    @SuppressWarnings("unchecked")
+    public static void test(NetworkChannel chan) throws IOException {
+        System.out.println("Checking: " + chan.getClass());
+        Set<SocketOption<?>> supported = chan.supportedOptions();
+        for (SocketOption<?> option : supported) {
+            String name = option.name();
+            System.out.println("Checking option " + name);
+            if (option.type() == Boolean.class) {
+                chan.setOption((SocketOption<Boolean>)option, true);
+                chan.setOption((SocketOption<Boolean>)option, false);
+                chan.getOption(option);
+            } else if (option.type() == Integer.class) {
+                chan.setOption((SocketOption<Integer>)option, 10);
+                chan.getOption(option);
+            }
+        }
+    }
+}