src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java
changeset 59329 289000934908
parent 59146 455612b3161a
--- a/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java	Fri Nov 29 14:11:50 2019 -0800
+++ b/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java	Sat Nov 30 16:21:19 2019 +0000
@@ -90,12 +90,12 @@
     // Our file descriptor
     private final FileDescriptor fd;
     private final int fdVal;
-    private final Cleanable cleaner;
 
-    // Cached InetAddress and port for unconnected DatagramChannels
-    // used by receive0
-    private InetAddress cachedSenderInetAddress;
-    private int cachedSenderPort;
+    // Native buffer for socket address used by receive0, protected by readLock
+    private final NativeSocketAddress socketAddress;
+
+    // Cleaner to close file descriptor and free native socket address
+    private final Cleanable cleaner;
 
     // Lock held by current reading or connecting thread
     private final ReentrantLock readLock = new ReentrantLock();
@@ -154,6 +154,9 @@
         throws IOException
     {
         super(sp);
+
+        this.socketAddress = new NativeSocketAddress();
+
         ResourceManager.beforeUdpCreate();
         try {
             this.family = Net.isIPv6Available()
@@ -163,9 +166,12 @@
             this.fdVal = IOUtil.fdVal(fd);
         } catch (IOException ioe) {
             ResourceManager.afterUdpClose();
+            socketAddress.free();
             throw ioe;
         }
-        this.cleaner = CleanerFactory.cleaner().register(this, closerFor(fd));
+
+        Runnable releaser = releaserFor(fd, socketAddress);
+        this.cleaner = CleanerFactory.cleaner().register(this, releaser);
     }
 
     public DatagramChannelImpl(SelectorProvider sp, ProtocolFamily family)
@@ -183,6 +189,8 @@
             }
         }
 
+        this.socketAddress = new NativeSocketAddress();
+
         ResourceManager.beforeUdpCreate();
         try {
             this.family = family;
@@ -190,9 +198,12 @@
             this.fdVal = IOUtil.fdVal(fd);
         } catch (IOException ioe) {
             ResourceManager.afterUdpClose();
+            socketAddress.free();
             throw ioe;
         }
-        this.cleaner = CleanerFactory.cleaner().register(this, closerFor(fd));
+
+        Runnable releaser = releaserFor(fd, socketAddress);
+        this.cleaner = CleanerFactory.cleaner().register(this, releaser);
     }
 
     public DatagramChannelImpl(SelectorProvider sp, FileDescriptor fd)
@@ -200,6 +211,13 @@
     {
         super(sp);
 
+        try {
+            this.socketAddress = new NativeSocketAddress();
+        } catch (OutOfMemoryError e) {
+            nd.close(fd);
+            throw e;
+        }
+
         // increment UDP count to match decrement when closing
         ResourceManager.beforeUdpCreate();
 
@@ -208,7 +226,10 @@
                 : StandardProtocolFamily.INET;
         this.fd = fd;
         this.fdVal = IOUtil.fdVal(fd);
-        this.cleaner = CleanerFactory.cleaner().register(this, closerFor(fd));
+
+        Runnable releaser = releaserFor(fd, socketAddress);
+        this.cleaner = CleanerFactory.cleaner().register(this, releaser);
+
         synchronized (stateLock) {
             this.localAddress = Net.localAddress(fd);
         }
@@ -450,8 +471,6 @@
         }
     }
 
-    private SocketAddress sender;       // Set by receive0 (## ugh)
-
     @Override
     public SocketAddress receive(ByteBuffer dst) throws IOException {
         if (dst.isReadOnly())
@@ -459,33 +478,31 @@
         readLock.lock();
         try {
             boolean blocking = isBlocking();
-            boolean completed = false;
-            int n = 0;
+            SocketAddress sender = null;
             try {
                 SocketAddress remote = beginRead(blocking, false);
                 boolean connected = (remote != null);
                 SecurityManager sm = System.getSecurityManager();
-
                 if (connected || (sm == null)) {
                     // connected or no security manager
-                    n = receive(dst, connected);
+                    int n = receive(dst, connected);
                     if (blocking) {
                         while (IOStatus.okayToRetry(n) && isOpen()) {
                             park(Net.POLLIN);
                             n = receive(dst, connected);
                         }
                     }
+                    if (n >= 0) {
+                        // sender address is in socket address buffer
+                        sender = socketAddress.toInetSocketAddress();
+                    }
                 } else {
                     // security manager and unconnected
-                    n = untrustedReceive(dst);
+                    sender = untrustedReceive(dst);
                 }
-                if (n == IOStatus.UNAVAILABLE)
-                    return null;
-                completed = (n > 0) || (n == 0 && isOpen());
                 return sender;
             } finally {
-                endRead(blocking, completed);
-                assert IOStatus.check(n);
+                endRead(blocking, (sender != null));
             }
         } finally {
             readLock.unlock();
@@ -498,10 +515,8 @@
      * into a buffer that is not accessible to the user. The datagram is copied
      * into the user's buffer when the sender address is accepted by the security
      * manager.
-     *
-     * @return the size of the datagram or IOStatus.UNAVAILABLE
      */
-    private int untrustedReceive(ByteBuffer dst) throws IOException {
+    private SocketAddress untrustedReceive(ByteBuffer dst) throws IOException {
         SecurityManager sm = System.getSecurityManager();
         assert readLock.isHeldByCurrentThread()
                 && sm != null && remoteAddress == null;
@@ -516,18 +531,21 @@
                         park(Net.POLLIN);
                         n = receive(bb, false);
                     }
-                } else if (n == IOStatus.UNAVAILABLE) {
-                    return n;
                 }
-                InetSocketAddress isa = (InetSocketAddress) sender;
-                try {
-                    sm.checkAccept(isa.getAddress().getHostAddress(), isa.getPort());
-                    bb.flip();
-                    dst.put(bb);
-                    return n;
-                } catch (SecurityException se) {
-                    // ignore datagram
-                    bb.clear();
+                if (n >= 0) {
+                    // sender address is in socket address buffer
+                    InetSocketAddress isa = socketAddress.toInetSocketAddress();
+                    try {
+                        sm.checkAccept(isa.getAddress().getHostAddress(), isa.getPort());
+                        bb.flip();
+                        dst.put(bb);
+                        return isa;
+                    } catch (SecurityException se) {
+                        // ignore datagram
+                        bb.clear();
+                    }
+                } else {
+                    return null;
                 }
             }
         } finally {
@@ -584,21 +602,22 @@
         throws IOException
     {
         assert readLock.isHeldByCurrentThread() && isBlocking();
-        boolean completed = false;
-        int n = 0;
+        SocketAddress sender = null;
         try {
             SocketAddress remote = beginRead(true, false);
             boolean connected = (remote != null);
-            n = receive(dst, connected);
-            while (n == IOStatus.UNAVAILABLE && isOpen()) {
+            int n = receive(dst, connected);
+            while (IOStatus.okayToRetry(n) && isOpen()) {
                 park(Net.POLLIN);
                 n = receive(dst, connected);
             }
-            completed = (n > 0) || (n == 0 && isOpen());
+            if (n >= 0) {
+                // sender address is in socket address buffer
+                sender = socketAddress.toInetSocketAddress();
+            }
             return sender;
         } finally {
-            endRead(true, completed);
-            assert IOStatus.check(n);
+            endRead(true, (sender != null));
         }
     }
 
@@ -611,8 +630,7 @@
         throws IOException
     {
         assert readLock.isHeldByCurrentThread() && isBlocking();
-        boolean completed = false;
-        int n = 0;
+        SocketAddress sender = null;
         try {
             SocketAddress remote = beginRead(true, false);
             boolean connected = (remote != null);
@@ -621,7 +639,7 @@
             lockedConfigureBlocking(false);
             try {
                 long startNanos = System.nanoTime();
-                n = receive(dst, connected);
+                int n = receive(dst, connected);
                 while (n == IOStatus.UNAVAILABLE && isOpen()) {
                     long remainingNanos = nanos - (System.nanoTime() - startNanos);
                     if (remainingNanos <= 0) {
@@ -630,16 +648,17 @@
                     park(Net.POLLIN, remainingNanos);
                     n = receive(dst, connected);
                 }
-                completed = (n > 0) || (n == 0 && isOpen());
+                if (n >= 0) {
+                    // sender address is in socket address buffer
+                    sender = socketAddress.toInetSocketAddress();
+                }
                 return sender;
             } finally {
                 // restore socket to blocking mode (if channel is open)
                 tryLockedConfigureBlocking(true);
             }
-
         } finally {
-            endRead(true, completed);
-            assert IOStatus.check(n);
+            endRead(true, (sender != null));
         }
     }
 
@@ -671,7 +690,10 @@
                                         boolean connected)
         throws IOException
     {
-        int n = receive0(fd, ((DirectBuffer)bb).address() + pos, rem, connected);
+        int n = receive0(fd,
+                         ((DirectBuffer)bb).address() + pos, rem,
+                         socketAddress.address(),
+                         connected);
         if (n > 0)
             bb.position(pos + n);
         return n;
@@ -1709,38 +1731,37 @@
     }
 
     /**
-     * Returns an action to close the given file descriptor.
+     * Returns an action to release a the given file descriptor and free the
+     * given native socket address.
      */
-    private static Runnable closerFor(FileDescriptor fd) {
+    private static Runnable releaserFor(FileDescriptor fd, NativeSocketAddress sa) {
         return () -> {
             try {
                 nd.close(fd);
             } catch (IOException ioe) {
                 throw new UncheckedIOException(ioe);
             } finally {
-                // decrement
+                // decrement socket count and release memory
                 ResourceManager.afterUdpClose();
+                sa.free();
             }
         };
     }
 
     // -- Native methods --
 
-    private static native void initIDs();
-
     private static native void disconnect0(FileDescriptor fd, boolean isIPv6)
         throws IOException;
 
-    private native int receive0(FileDescriptor fd, long address, int len,
-                                boolean connected)
+    private static native int receive0(FileDescriptor fd, long address, int len,
+                                       long senderAddress, boolean connected)
         throws IOException;
 
-    private native int send0(boolean preferIPv6, FileDescriptor fd, long address,
-                             int len, InetAddress addr, int port)
+    private static native int send0(boolean preferIPv6, FileDescriptor fd,
+                                    long address, int len, InetAddress addr, int port)
         throws IOException;
 
     static {
         IOUtil.load();
-        initIDs();
     }
 }