diff --git a/src/java.base/share/classes/java/net/DatagramSocket.java b/src/java.base/share/classes/java/net/DatagramSocket.java index 2cef22b58c5..b37f179a23a 100644 --- a/src/java.base/share/classes/java/net/DatagramSocket.java +++ b/src/java.base/share/classes/java/net/DatagramSocket.java @@ -606,7 +606,6 @@ class DatagramSocket implements java.io.Closeable { * @see #bind(SocketAddress) * @since 1.4 */ - public SocketAddress getLocalSocketAddress() { if (isClosed()) return null; @@ -853,7 +852,7 @@ class DatagramSocket implements java.io.Closeable { public InetAddress getLocalAddress() { if (isClosed()) return null; - InetAddress in = null; + InetAddress in; try { in = (InetAddress) getImpl().getOption(SocketOptions.SO_BINDADDR); if (in.isAnyLocalAddress()) { @@ -874,8 +873,8 @@ class DatagramSocket implements java.io.Closeable { * is bound. * * @return the port number on the local host to which this socket is bound, - {@code -1} if the socket is closed, or - {@code 0} if it is not bound yet. + * {@code -1} if the socket is closed, or + * {@code 0} if it is not bound yet. */ public int getLocalPort() { if (isClosed()) @@ -887,15 +886,16 @@ class DatagramSocket implements java.io.Closeable { } } - /** Enable/disable SO_TIMEOUT with the specified timeout, in - * milliseconds. With this option set to a positive timeout value, - * a call to receive() for this DatagramSocket - * will block for only this amount of time. If the timeout expires, - * a <B>java.net.SocketTimeoutException</B> is raised, though the - * DatagramSocket is still valid. A timeout of zero is interpreted - * as an infinite timeout. - * The option <B>must</B> be enabled prior to entering the blocking - * operation to have effect. + /** + * Enable/disable SO_TIMEOUT with the specified timeout, in + * milliseconds. With this option set to a positive timeout value, + * a call to receive() for this DatagramSocket + * will block for only this amount of time. If the timeout expires, + * a <B>java.net.SocketTimeoutException</B> is raised, though the + * DatagramSocket is still valid. A timeout of zero is interpreted + * as an infinite timeout. + * The option <B>must</B> be enabled prior to entering the blocking + * operation to have effect. * * @param timeout the specified timeout in milliseconds. * @throws SocketException if there is an error in the underlying protocol, such as an UDP error. @@ -963,8 +963,7 @@ class DatagramSocket implements java.io.Closeable { * negative. * @see #getSendBufferSize() */ - public synchronized void setSendBufferSize(int size) - throws SocketException{ + public synchronized void setSendBufferSize(int size) throws SocketException { if (!(size > 0)) { throw new IllegalArgumentException("negative send size"); } @@ -1021,8 +1020,7 @@ class DatagramSocket implements java.io.Closeable { * negative. * @see #getReceiveBufferSize() */ - public synchronized void setReceiveBufferSize(int size) - throws SocketException{ + public synchronized void setReceiveBufferSize(int size) throws SocketException { if (size <= 0) { throw new IllegalArgumentException("invalid receive size"); } @@ -1039,8 +1037,7 @@ class DatagramSocket implements java.io.Closeable { * @throws SocketException if there is an error in the underlying protocol, such as an UDP error. * @see #setReceiveBufferSize(int) */ - public synchronized int getReceiveBufferSize() - throws SocketException{ + public synchronized int getReceiveBufferSize() throws SocketException { if (isClosed()) throw new SocketException("Socket is closed"); int result = 0; diff --git a/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java b/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java index 45379f4c372..73ded62d171 100644 --- a/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java @@ -28,6 +28,8 @@ package sun.nio.ch; import java.io.FileDescriptor; import java.io.IOException; import java.io.UncheckedIOException; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.lang.ref.Cleaner.Cleanable; import java.net.DatagramSocket; import java.net.Inet4Address; @@ -39,6 +41,7 @@ import java.net.PortUnreachableException; import java.net.ProtocolFamily; import java.net.SocketAddress; import java.net.SocketOption; +import java.net.SocketTimeoutException; import java.net.StandardProtocolFamily; import java.net.StandardSocketOptions; import java.nio.ByteBuffer; @@ -47,6 +50,7 @@ import java.nio.channels.AlreadyConnectedException; import java.nio.channels.AsynchronousCloseException; import java.nio.channels.ClosedChannelException; import java.nio.channels.DatagramChannel; +import java.nio.channels.IllegalBlockingModeException; import java.nio.channels.MembershipKey; import java.nio.channels.NotYetConnectedException; import java.nio.channels.SelectionKey; @@ -113,8 +117,17 @@ class DatagramChannelImpl private InetSocketAddress localAddress; private InetSocketAddress remoteAddress; - // Our socket adaptor, if any - private DatagramSocket socket; + // Socket adaptor, created lazily + private static final VarHandle SOCKET; + static { + try { + MethodHandles.Lookup l = MethodHandles.lookup(); + SOCKET = l.findVarHandle(DatagramChannelImpl.class, "socket", DatagramSocket.class); + } catch (Exception e) { + throw new InternalError(e); + } + } + private volatile DatagramSocket socket; // Multicast support private MembershipRegistry registry; @@ -199,11 +212,14 @@ class DatagramChannelImpl @Override public DatagramSocket socket() { - synchronized (stateLock) { - if (socket == null) - socket = DatagramSocketAdaptor.create(this); - return socket; + DatagramSocket socket = this.socket; + if (socket == null) { + socket = DatagramSocketAdaptor.create(this); + if (!SOCKET.compareAndSet(this, null, socket)) { + socket = this.socket; + } } + return socket; } @Override @@ -408,62 +424,35 @@ class DatagramChannelImpl public SocketAddress receive(ByteBuffer dst) throws IOException { if (dst.isReadOnly()) throw new IllegalArgumentException("Read-only buffer"); - readLock.lock(); try { boolean blocking = isBlocking(); + boolean completed = false; int n = 0; - ByteBuffer bb = 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(fd, dst, connected); + n = receive(dst, connected); if (blocking) { while (IOStatus.okayToRetry(n) && isOpen()) { park(Net.POLLIN); - n = receive(fd, dst, connected); + n = receive(dst, connected); } - } else if (n == IOStatus.UNAVAILABLE) { - return null; } } else { - // Cannot receive into user's buffer when running with a - // security manager and not connected - bb = Util.getTemporaryDirectBuffer(dst.remaining()); - for (;;) { - n = receive(fd, bb, connected); - if (blocking) { - while (IOStatus.okayToRetry(n) && isOpen()) { - park(Net.POLLIN); - n = receive(fd, bb, connected); - } - } else if (n == IOStatus.UNAVAILABLE) { - return null; - } - InetSocketAddress isa = (InetSocketAddress)sender; - try { - sm.checkAccept(isa.getAddress().getHostAddress(), - isa.getPort()); - } catch (SecurityException se) { - // Ignore packet - bb.clear(); - n = 0; - continue; - } - bb.flip(); - dst.put(bb); - break; - } + // security manager and unconnected + n = untrustedReceive(dst); } - assert sender != null; + if (n == IOStatus.UNAVAILABLE) + return null; + completed = (n > 0) || (n == 0 && isOpen()); return sender; } finally { - if (bb != null) - Util.releaseTemporaryDirectBuffer(bb); - endRead(blocking, n > 0); + endRead(blocking, completed); assert IOStatus.check(n); } } finally { @@ -471,15 +460,164 @@ class DatagramChannelImpl } } - private int receive(FileDescriptor fd, ByteBuffer dst, boolean connected) + /** + * Receives a datagram into an untrusted buffer. When there is a security + * manager set, and the socket is not connected, datagrams have to be received + * 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 { + SecurityManager sm = System.getSecurityManager(); + assert readLock.isHeldByCurrentThread() + && sm != null && remoteAddress == null; + + ByteBuffer bb = Util.getTemporaryDirectBuffer(dst.remaining()); + try { + boolean blocking = isBlocking(); + for (;;) { + int n = receive(bb, false); + if (blocking) { + while (IOStatus.okayToRetry(n) && isOpen()) { + 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(); + } + } + } finally { + Util.releaseTemporaryDirectBuffer(bb); + } + } + + /** + * Receives a datagram into the given buffer. + * + * @apiNote This method is for use by the socket adaptor. The buffer is + * assumed to be trusted, meaning it is not accessible to user code. + * + * @throws IllegalBlockingModeException if the channel is non-blocking + * @throws SocketTimeoutException if the timeout elapses + */ + SocketAddress blockingReceive(ByteBuffer dst, long nanos) throws IOException { + readLock.lock(); + try { + ensureOpen(); + if (!isBlocking()) + throw new IllegalBlockingModeException(); + SecurityManager sm = System.getSecurityManager(); + boolean connected = isConnected(); + SocketAddress sender; + do { + if (nanos > 0) { + sender = trustedBlockingReceive(dst, nanos); + } else { + sender = trustedBlockingReceive(dst); + } + // check sender when security manager set and not connected + if (sm != null && !connected) { + InetSocketAddress isa = (InetSocketAddress) sender; + try { + sm.checkAccept(isa.getAddress().getHostAddress(), isa.getPort()); + } catch (SecurityException e) { + sender = null; + } + } + } while (sender == null); + return sender; + } finally { + readLock.unlock(); + } + } + + /** + * Receives a datagram into given buffer. This method is used to support + * the socket adaptor. The buffer is assumed to be trusted. + * @throws SocketTimeoutException if the timeout elapses + */ + private SocketAddress trustedBlockingReceive(ByteBuffer dst) throws IOException { + assert readLock.isHeldByCurrentThread() && isBlocking(); + boolean completed = false; + int n = 0; + try { + SocketAddress remote = beginRead(true, false); + boolean connected = (remote != null); + n = receive(dst, connected); + while (n == IOStatus.UNAVAILABLE && isOpen()) { + park(Net.POLLIN); + n = receive(dst, connected); + } + completed = (n > 0) || (n == 0 && isOpen()); + return sender; + } finally { + endRead(true, completed); + assert IOStatus.check(n); + } + } + + /** + * Receives a datagram into given buffer with a timeout. This method is + * used to support the socket adaptor. The buffer is assumed to be trusted. + * @throws SocketTimeoutException if the timeout elapses + */ + private SocketAddress trustedBlockingReceive(ByteBuffer dst, long nanos) + throws IOException + { + assert readLock.isHeldByCurrentThread() && isBlocking(); + boolean completed = false; + int n = 0; + try { + SocketAddress remote = beginRead(true, false); + boolean connected = (remote != null); + + // change socket to non-blocking + lockedConfigureBlocking(false); + try { + long startNanos = System.nanoTime(); + n = receive(dst, connected); + while (n == IOStatus.UNAVAILABLE && isOpen()) { + long remainingNanos = nanos - (System.nanoTime() - startNanos); + if (remainingNanos <= 0) { + throw new SocketTimeoutException("Receive timed out"); + } + park(Net.POLLIN, remainingNanos); + n = receive(dst, connected); + } + completed = (n > 0) || (n == 0 && isOpen()); + return sender; + } finally { + // restore socket to blocking mode (if channel is open) + tryLockedConfigureBlocking(true); + } + + } finally { + endRead(true, completed); + assert IOStatus.check(n); + } + } + + private int receive(ByteBuffer dst, boolean connected) throws IOException { int pos = dst.position(); int lim = dst.limit(); assert (pos <= lim); int rem = (pos <= lim ? lim - pos : 0); if (dst instanceof DirectBuffer && rem > 0) - return receiveIntoNativeBuffer(fd, dst, rem, pos, connected); + return receiveIntoNativeBuffer(dst, rem, pos, connected); // Substitute a native buffer. If the supplied buffer is empty // we must instead use a nonempty buffer, otherwise the call @@ -487,7 +625,7 @@ class DatagramChannelImpl int newSize = Math.max(rem, 1); ByteBuffer bb = Util.getTemporaryDirectBuffer(newSize); try { - int n = receiveIntoNativeBuffer(fd, bb, newSize, 0, connected); + int n = receiveIntoNativeBuffer(bb, newSize, 0, connected); bb.flip(); if (n > 0 && rem > 0) dst.put(bb); @@ -497,8 +635,8 @@ class DatagramChannelImpl } } - private int receiveIntoNativeBuffer(FileDescriptor fd, ByteBuffer bb, - int rem, int pos, boolean connected) + private int receiveIntoNativeBuffer(ByteBuffer bb, int rem, int pos, + boolean connected) throws IOException { int n = receive0(fd, ((DirectBuffer)bb).address() + pos, rem, connected); @@ -563,6 +701,25 @@ class DatagramChannelImpl } } + /** + * Sends a datagram from the bytes in given buffer. + * + * @apiNote This method is for use by the socket adaptor. + * + * @throws IllegalBlockingModeException if the channel is non-blocking + */ + void blockingSend(ByteBuffer src, SocketAddress target) throws IOException { + writeLock.lock(); + try { + ensureOpen(); + if (!isBlocking()) + throw new IllegalBlockingModeException(); + send(src, target); + } finally { + writeLock.unlock(); + } + } + private int send(FileDescriptor fd, ByteBuffer src, InetSocketAddress target) throws IOException { @@ -785,10 +942,7 @@ class DatagramChannelImpl try { writeLock.lock(); try { - synchronized (stateLock) { - ensureOpen(); - IOUtil.configureBlocking(fd, block); - } + lockedConfigureBlocking(block); } finally { writeLock.unlock(); } @@ -797,6 +951,36 @@ class DatagramChannelImpl } } + /** + * Adjusts the blocking mode. readLock or writeLock must already be held. + */ + private void lockedConfigureBlocking(boolean block) throws IOException { + assert readLock.isHeldByCurrentThread() || writeLock.isHeldByCurrentThread(); + synchronized (stateLock) { + ensureOpen(); + IOUtil.configureBlocking(fd, block); + } + } + + /** + * Adjusts the blocking mode if the channel is open. readLock or writeLock + * must already be held. + * + * @return {@code true} if the blocking mode was adjusted, {@code false} if + * the blocking mode was not adjusted because the channel is closed + */ + private boolean tryLockedConfigureBlocking(boolean block) throws IOException { + assert readLock.isHeldByCurrentThread() || writeLock.isHeldByCurrentThread(); + synchronized (stateLock) { + if (isOpen()) { + IOUtil.configureBlocking(fd, block); + return true; + } else { + return false; + } + } + } + InetSocketAddress localAddress() { synchronized (stateLock) { return localAddress; @@ -861,6 +1045,16 @@ class DatagramChannelImpl @Override public DatagramChannel connect(SocketAddress sa) throws IOException { + return connect(sa, true); + } + + /** + * Connects the channel's socket. + * + * @param sa the remote address to which this channel is to be connected + * @param check true to check if the channel is already connected. + */ + DatagramChannel connect(SocketAddress sa, boolean check) throws IOException { InetSocketAddress isa = Net.checkAddress(sa, family); SecurityManager sm = System.getSecurityManager(); if (sm != null) { @@ -879,7 +1073,7 @@ class DatagramChannelImpl try { synchronized (stateLock) { ensureOpen(); - if (state == ST_CONNECTED) + if (check && state == ST_CONNECTED) throw new AlreadyConnectedException(); // ensure that the socket is bound @@ -908,7 +1102,7 @@ class DatagramChannelImpl } try { ByteBuffer buf = ByteBuffer.allocate(100); - while (receive(fd, buf, false) > 0) { + while (receive(buf, false) >= 0) { buf.clear(); } } finally { @@ -1331,30 +1525,6 @@ class DatagramChannelImpl return translateReadyOps(ops, 0, ski); } - /** - * Poll this channel's socket for reading up to the given timeout. - * @return {@code true} if the socket is polled - */ - boolean pollRead(long timeout) throws IOException { - boolean blocking = isBlocking(); - assert Thread.holdsLock(blockingLock()) && blocking; - - readLock.lock(); - try { - boolean polled = false; - try { - beginRead(blocking, false); - int events = Net.poll(fd, Net.POLLIN, timeout); - polled = (events != 0); - } finally { - endRead(blocking, polled); - } - return polled; - } finally { - readLock.unlock(); - } - } - /** * Translates an interest operation set into a native poll event set */ diff --git a/src/java.base/share/classes/sun/nio/ch/DatagramSocketAdaptor.java b/src/java.base/share/classes/sun/nio/ch/DatagramSocketAdaptor.java index dab7d96249a..01444d3e02d 100644 --- a/src/java.base/share/classes/sun/nio/ch/DatagramSocketAdaptor.java +++ b/src/java.base/share/classes/sun/nio/ch/DatagramSocketAdaptor.java @@ -26,6 +26,9 @@ package sun.nio.ch; import java.io.IOException; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.invoke.VarHandle; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.DatagramSocketImpl; @@ -35,15 +38,16 @@ import java.net.NetworkInterface; import java.net.SocketAddress; import java.net.SocketException; import java.net.SocketOption; -import java.net.SocketTimeoutException; import java.net.StandardSocketOptions; import java.nio.ByteBuffer; +import java.nio.channels.AlreadyConnectedException; import java.nio.channels.ClosedChannelException; import java.nio.channels.DatagramChannel; -import java.nio.channels.IllegalBlockingModeException; -import java.util.Objects; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.Set; +import static java.util.concurrent.TimeUnit.MILLISECONDS; // Make a datagram-socket channel look like a datagram socket. // @@ -61,13 +65,9 @@ class DatagramSocketAdaptor // Timeout "option" value for receives private volatile int timeout; - // ## super will create a useless impl + // create DatagramSocket with useless impl private DatagramSocketAdaptor(DatagramChannelImpl dc) { - // Invoke the DatagramSocketAdaptor(SocketAddress) constructor, - // passing a dummy DatagramSocketImpl object to avoid any native - // resource allocation in super class and invoking our bind method - // before the dc field is initialized. - super(dummyDatagramSocket); + super(new DummyDatagramSocketImpl()); this.dc = dc; } @@ -75,17 +75,9 @@ class DatagramSocketAdaptor return new DatagramSocketAdaptor(dc); } - private void connectInternal(SocketAddress remote) - throws SocketException - { - InetSocketAddress isa = Net.asInetSocketAddress(remote); - int port = isa.getPort(); - if (port < 0 || port > 0xFFFF) - throw new IllegalArgumentException("connect: " + port); - if (remote == null) - throw new IllegalArgumentException("connect: null address"); + private void connectInternal(SocketAddress remote) throws SocketException { try { - dc.connect(remote); + dc.connect(remote, false); // skips check for already connected } catch (ClosedChannelException e) { // ignore } catch (Exception x) { @@ -95,9 +87,12 @@ class DatagramSocketAdaptor @Override public void bind(SocketAddress local) throws SocketException { + if (local != null) { + local = Net.asInetSocketAddress(local); + } else { + local = new InetSocketAddress(0); + } try { - if (local == null) - local = new InetSocketAddress(0); dc.bind(local); } catch (Exception x) { Net.translateToSocketException(x); @@ -106,17 +101,20 @@ class DatagramSocketAdaptor @Override public void connect(InetAddress address, int port) { + if (address == null) + throw new IllegalArgumentException("Address can't be null"); try { connectInternal(new InetSocketAddress(address, port)); } catch (SocketException x) { - // Yes, j.n.DatagramSocket really does this + throw new Error(x); } } @Override public void connect(SocketAddress remote) throws SocketException { - Objects.requireNonNull(remote, "Address can't be null"); - connectInternal(remote); + if (remote == null) + throw new IllegalArgumentException("Address can't be null"); + connectInternal(Net.asInetSocketAddress(remote)); } @Override @@ -157,80 +155,84 @@ class DatagramSocketAdaptor @Override public SocketAddress getLocalSocketAddress() { - return dc.localAddress(); + try { + return dc.getLocalAddress(); + } catch (ClosedChannelException e) { + return null; + } catch (Exception x) { + throw new Error(x); + } } @Override public void send(DatagramPacket p) throws IOException { - synchronized (dc.blockingLock()) { - if (!dc.isBlocking()) - throw new IllegalBlockingModeException(); - try { - synchronized (p) { - ByteBuffer bb = ByteBuffer.wrap(p.getData(), - p.getOffset(), - p.getLength()); - if (dc.isConnected()) { - if (p.getAddress() == null) { - // Legacy DatagramSocket will send in this case - // and set address and port of the packet - InetSocketAddress isa = dc.remoteAddress(); - p.setPort(isa.getPort()); - p.setAddress(isa.getAddress()); - dc.write(bb); - } else { - // Target address may not match connected address - dc.send(bb, p.getSocketAddress()); - } - } else { - // Not connected so address must be valid or throw - dc.send(bb, p.getSocketAddress()); + ByteBuffer bb = null; + try { + InetSocketAddress target; + synchronized (p) { + // copy bytes to temporary direct buffer + int len = p.getLength(); + bb = Util.getTemporaryDirectBuffer(len); + bb.put(p.getData(), p.getOffset(), len); + bb.flip(); + + // target address + if (p.getAddress() == null) { + InetSocketAddress remote = dc.remoteAddress(); + if (remote == null) { + // not specified by DatagramSocket + throw new IllegalArgumentException("Address not set"); } + // set address/port to maintain compatibility with DatagramSocket + p.setAddress(remote.getAddress()); + p.setPort(remote.getPort()); + target = remote; + } else { + // throws IllegalArgumentException if port not set + target = (InetSocketAddress) p.getSocketAddress(); } - } catch (IOException x) { - Net.translateException(x); } - } - } - - private SocketAddress receive(ByteBuffer bb) throws IOException { - assert Thread.holdsLock(dc.blockingLock()) && dc.isBlocking(); - - long to = this.timeout; - if (to == 0) { - return dc.receive(bb); - } else { - for (;;) { - if (!dc.isOpen()) - throw new ClosedChannelException(); - long st = System.currentTimeMillis(); - if (dc.pollRead(to)) { - return dc.receive(bb); - } - to -= System.currentTimeMillis() - st; - if (to <= 0) - throw new SocketTimeoutException(); + // send datagram + try { + dc.blockingSend(bb, target); + } catch (AlreadyConnectedException e) { + throw new IllegalArgumentException("Connected and packet address differ"); + } catch (ClosedChannelException e) { + var exc = new SocketException("Socket closed"); + exc.initCause(e); + throw exc; + } + } finally { + if (bb != null) { + Util.offerFirstTemporaryDirectBuffer(bb); } } } @Override public void receive(DatagramPacket p) throws IOException { - synchronized (dc.blockingLock()) { - if (!dc.isBlocking()) - throw new IllegalBlockingModeException(); - try { - synchronized (p) { - ByteBuffer bb = ByteBuffer.wrap(p.getData(), - p.getOffset(), - p.getLength()); - SocketAddress sender = receive(bb); - p.setSocketAddress(sender); - p.setLength(bb.position() - p.getOffset()); - } - } catch (IOException x) { - Net.translateException(x); + // get temporary direct buffer with a capacity of p.bufLength + int bufLength = DatagramPackets.getBufLength(p); + ByteBuffer bb = Util.getTemporaryDirectBuffer(bufLength); + try { + long nanos = MILLISECONDS.toNanos(timeout); + SocketAddress sender = dc.blockingReceive(bb, nanos); + bb.flip(); + synchronized (p) { + // copy bytes to the DatagramPacket and set length + int len = Math.min(bb.limit(), DatagramPackets.getBufLength(p)); + bb.get(p.getData(), p.getOffset(), len); + DatagramPackets.setLength(p, len); + + // sender address + p.setSocketAddress(sender); } + } catch (ClosedChannelException e) { + var exc = new SocketException("Socket closed"); + exc.initCause(e); + throw exc; + } finally { + Util.offerFirstTemporaryDirectBuffer(bb); } } @@ -257,19 +259,16 @@ class DatagramSocketAdaptor public int getLocalPort() { if (isClosed()) return -1; - try { - InetSocketAddress local = dc.localAddress(); - if (local != null) { - return local.getPort(); - } - } catch (Exception x) { + InetSocketAddress local = dc.localAddress(); + if (local != null) { + return local.getPort(); } return 0; } @Override public void setSoTimeout(int timeout) throws SocketException { - if (!dc.isOpen()) + if (isClosed()) throw new SocketException("Socket is closed"); if (timeout < 0) throw new IllegalArgumentException("timeout < 0"); @@ -278,7 +277,7 @@ class DatagramSocketAdaptor @Override public int getSoTimeout() throws SocketException { - if (!dc.isOpen()) + if (isClosed()) throw new SocketException("Socket is closed"); return timeout; } @@ -353,7 +352,6 @@ class DatagramSocketAdaptor @Override public boolean getReuseAddress() throws SocketException { return getBooleanOption(StandardSocketOptions.SO_REUSEADDR); - } @Override @@ -411,50 +409,157 @@ class DatagramSocketAdaptor return dc.supportedOptions(); } - /* - * A dummy implementation of DatagramSocketImpl that can be passed to the - * DatagramSocket constructor so that no native resources are allocated in - * super class. - */ - private static final DatagramSocketImpl dummyDatagramSocket - = new DatagramSocketImpl() - { - protected void create() throws SocketException {} - protected void bind(int lport, InetAddress laddr) throws SocketException {} + /** + * DatagramSocketImpl implementation where all methods throw an error. + */ + private static class DummyDatagramSocketImpl extends DatagramSocketImpl { + private static <T> T shouldNotGetHere() { + throw new InternalError("Should not get here"); + } - protected void send(DatagramPacket p) throws IOException {} + @Override + protected void create() { + shouldNotGetHere(); + } - protected int peek(InetAddress i) throws IOException { return 0; } + @Override + protected void bind(int lport, InetAddress laddr) { + shouldNotGetHere(); + } - protected int peekData(DatagramPacket p) throws IOException { return 0; } + @Override + protected void send(DatagramPacket p) { + shouldNotGetHere(); + } - protected void receive(DatagramPacket p) throws IOException {} + @Override + protected int peek(InetAddress address) { + return shouldNotGetHere(); + } - @Deprecated - protected void setTTL(byte ttl) throws IOException {} + @Override + protected int peekData(DatagramPacket p) { + return shouldNotGetHere(); + } - @Deprecated - protected byte getTTL() throws IOException { return 0; } + @Override + protected void receive(DatagramPacket p) { + shouldNotGetHere(); + } - protected void setTimeToLive(int ttl) throws IOException {} + @Deprecated + protected void setTTL(byte ttl) { + shouldNotGetHere(); + } - protected int getTimeToLive() throws IOException { return 0;} + @Deprecated + protected byte getTTL() { + return shouldNotGetHere(); + } - protected void join(InetAddress inetaddr) throws IOException {} + @Override + protected void setTimeToLive(int ttl) { + shouldNotGetHere(); + } - protected void leave(InetAddress inetaddr) throws IOException {} + @Override + protected int getTimeToLive() { + return shouldNotGetHere(); + } - protected void joinGroup(SocketAddress mcastaddr, - NetworkInterface netIf) throws IOException {} + @Override + protected void join(InetAddress group) { + shouldNotGetHere(); + } - protected void leaveGroup(SocketAddress mcastaddr, - NetworkInterface netIf) throws IOException {} + @Override + protected void leave(InetAddress inetaddr) { + shouldNotGetHere(); + } - protected void close() {} + @Override + protected void joinGroup(SocketAddress group, NetworkInterface netIf) { + shouldNotGetHere(); + } - public Object getOption(int optID) throws SocketException { return null;} + @Override + protected void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf) { + shouldNotGetHere(); + } - public void setOption(int optID, Object value) throws SocketException {} - }; -} + @Override + protected void close() { + shouldNotGetHere(); + } + + @Override + public Object getOption(int optID) { + return shouldNotGetHere(); + } + + @Override + public void setOption(int optID, Object value) { + shouldNotGetHere(); + } + + @Override + protected <T> void setOption(SocketOption<T> name, T value) { + shouldNotGetHere(); + } + + @Override + protected <T> T getOption(SocketOption<T> name) { + return shouldNotGetHere(); + } + + @Override + protected Set<SocketOption<?>> supportedOptions() { + return shouldNotGetHere(); + } + } + + /** + * Defines static methods to get/set DatagramPacket fields and workaround + * DatagramPacket deficiencies. + */ + private static class DatagramPackets { + private static final VarHandle LENGTH; + private static final VarHandle BUF_LENGTH; + static { + try { + PrivilegedAction<Lookup> pa = () -> { + try { + return MethodHandles.privateLookupIn(DatagramPacket.class, MethodHandles.lookup()); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + }; + MethodHandles.Lookup l = AccessController.doPrivileged(pa); + LENGTH = l.findVarHandle(DatagramPacket.class, "length", int.class); + BUF_LENGTH = l.findVarHandle(DatagramPacket.class, "bufLength", int.class); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } + + /** + * Sets the DatagramPacket.length field. DatagramPacket.setLength cannot be + * used at this time because it sets both the length and bufLength fields. + */ + static void setLength(DatagramPacket p, int value) { + synchronized (p) { + LENGTH.set(p, value); + } + } + + /** + * Returns the value of the DatagramPacket.bufLength field. + */ + static int getBufLength(DatagramPacket p) { + synchronized (p) { + return (int) BUF_LENGTH.get(p); + } + } + } +} \ No newline at end of file diff --git a/test/jdk/java/nio/channels/DatagramChannel/AdaptDatagramSocket.java b/test/jdk/java/nio/channels/DatagramChannel/AdaptorBasic.java similarity index 87% rename from test/jdk/java/nio/channels/DatagramChannel/AdaptDatagramSocket.java rename to test/jdk/java/nio/channels/DatagramChannel/AdaptorBasic.java index 494fc4538b7..4ac5d4b3c88 100644 --- a/test/jdk/java/nio/channels/DatagramChannel/AdaptDatagramSocket.java +++ b/test/jdk/java/nio/channels/DatagramChannel/AdaptorBasic.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -22,20 +22,22 @@ */ /* @test - * @bug 4313882 4981129 8143610 + * @bug 4313882 4981129 8143610 8232673 * @summary Unit test for datagram-socket-channel adaptors + * @modules java.base/java.net:+open * @library .. /test/lib * @build jdk.test.lib.Utils TestServers - * @run main AdaptDatagramSocket + * @run main AdaptorBasic * @key randomness */ import java.net.*; import java.nio.channels.*; import java.util.*; +import java.lang.reflect.Field; -public class AdaptDatagramSocket { +public class AdaptorBasic { static java.io.PrintStream out = System.out; static Random rand = new Random(); @@ -46,13 +48,19 @@ public class AdaptDatagramSocket { + "]"); } - static void test(DatagramSocket ds, InetSocketAddress dst, - boolean shouldTimeout) + static int getBufLength(DatagramPacket p) throws Exception { + Field f = DatagramPacket.class.getDeclaredField("bufLength"); + f.setAccessible(true); + return (int) f.get(p); + } + + static void test(DatagramSocket ds, InetSocketAddress dst, boolean shouldTimeout) throws Exception { DatagramPacket op = new DatagramPacket(new byte[100], 13, 42, dst); rand.nextBytes(op.getData()); - DatagramPacket ip = new DatagramPacket(new byte[100], 19, 100 - 19); + int bufLength = 100 - 19; + DatagramPacket ip = new DatagramPacket(new byte[100], 19, bufLength); out.println("pre op: " + toString(op) + " ip: " + toString(ip)); long start = System.currentTimeMillis(); @@ -61,10 +69,6 @@ public class AdaptDatagramSocket { for (;;) { try { ds.receive(ip); - if (ip.getLength() == 0) { // ## Not sure why this happens - ip.setLength(100 - 19); - continue; - } } catch (SocketTimeoutException x) { if (shouldTimeout) { out.println("Receive timed out, as expected"); @@ -88,6 +92,10 @@ public class AdaptDatagramSocket { throw new Exception("Incorrect sender address, expected: " + dst + " actual: " + ip.getSocketAddress()); } + + if (getBufLength(ip) != bufLength) { + throw new Exception("DatagramPacket bufLength changed by receive!!!"); + } } static void test(InetSocketAddress dst, diff --git a/test/jdk/java/nio/channels/DatagramChannel/AdaptorConcurrentIO.java b/test/jdk/java/nio/channels/DatagramChannel/AdaptorConcurrentIO.java new file mode 100644 index 00000000000..b02c5ea522c --- /dev/null +++ b/test/jdk/java/nio/channels/DatagramChannel/AdaptorConcurrentIO.java @@ -0,0 +1,83 @@ +/* + * 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 8232673 + * @summary Test DatagramChannel socket adaptor with concurrent send/receive + */ + +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.nio.channels.DatagramChannel; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +public class AdaptorConcurrentIO { + + public static void main(String[] args) throws Exception { + testConcurrentSendReceive(0); + testConcurrentSendReceive(60_000); + } + + /** + * Starts a task that blocks in the adaptor's receive method, then invokes + * the adaptor's send method to send a datagram. If the adaptor were using + * the channel's blockingLock then send without be blocked waiting for + * the receive to complete. + */ + static void testConcurrentSendReceive(int timeout) throws Exception { + try (DatagramChannel dc = DatagramChannel.open()) { + InetAddress lb = InetAddress.getLoopbackAddress(); + dc.bind(new InetSocketAddress(lb, 0)); + DatagramSocket s = dc.socket(); + s.setSoTimeout(timeout); + + ExecutorService pool = Executors.newSingleThreadExecutor(); + try { + Future<String> result = pool.submit(() -> { + byte[] data = new byte[100]; + DatagramPacket p = new DatagramPacket(data, 0, data.length); + s.receive(p); + return new String(p.getData(), p.getOffset(), p.getLength(), "UTF-8"); + }); + + Thread.sleep(200); // give chance for thread to block + + byte[] data = "hello".getBytes("UTF-8"); + DatagramPacket p = new DatagramPacket(data, 0, data.length); + p.setSocketAddress(s.getLocalSocketAddress()); + s.send(p); + + String msg = result.get(); + if (!msg.equals("hello")) + throw new RuntimeException("Unexpected message: " + msg); + } finally { + pool.shutdown(); + } + } + } +} + diff --git a/test/jdk/java/nio/channels/DatagramChannel/AdaptorConnect.java b/test/jdk/java/nio/channels/DatagramChannel/AdaptorConnect.java new file mode 100644 index 00000000000..a609089e459 --- /dev/null +++ b/test/jdk/java/nio/channels/DatagramChannel/AdaptorConnect.java @@ -0,0 +1,117 @@ +/* + * 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 8232673 + * @summary Test DatagramChannel socket adaptor connect method with illegal args + * @run testng AdaptorConnect + */ + +import java.net.DatagramSocket; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.SocketException; +import java.nio.channels.DatagramChannel; +import static java.net.InetAddress.getLoopbackAddress; + +import org.testng.annotations.Test; +import static org.testng.Assert.*; + +@Test +public class AdaptorConnect { + + /** + * Invoke the given socket's connect method with illegal arguments. + */ + private void testConnectWithIllegalArguments(DatagramSocket s) { + assertThrows(IllegalArgumentException.class, () -> s.connect(null)); + assertThrows(IllegalArgumentException.class, () -> s.connect(null, 7000)); + assertThrows(IllegalArgumentException.class, () -> s.connect(getLoopbackAddress(), -1)); + assertThrows(IllegalArgumentException.class, () -> s.connect(getLoopbackAddress(), 100_000)); + + SocketAddress sillyAddress = new SocketAddress() { }; + assertThrows(IllegalArgumentException.class, () -> s.connect(sillyAddress)); + + SocketAddress unresolved = InetSocketAddress.createUnresolved("foo", 7777); + assertThrows(SocketException.class, () -> s.connect(unresolved)); + } + + /** + * Test connect method with an open socket. + */ + public void testOpenSocket() throws Exception { + try (DatagramChannel dc = DatagramChannel.open()) { + DatagramSocket s = dc.socket(); + + testConnectWithIllegalArguments(s); + + // should not be bound or connected + assertTrue(s.getLocalSocketAddress() == null); + assertTrue(s.getRemoteSocketAddress() == null); + + // connect(SocketAddress) + var remote1 = new InetSocketAddress(getLoopbackAddress(), 7001); + s.connect(remote1); + assertEquals(s.getRemoteSocketAddress(), remote1); + testConnectWithIllegalArguments(s); + assertEquals(s.getRemoteSocketAddress(), remote1); + + // connect(SocketAddress) + var remote2 = new InetSocketAddress(getLoopbackAddress(), 7002); + s.connect(remote2); + assertEquals(s.getRemoteSocketAddress(), remote2); + testConnectWithIllegalArguments(s); + assertEquals(s.getRemoteSocketAddress(), remote2); + + // connect(InetAddress, int) + var remote3 = new InetSocketAddress(getLoopbackAddress(), 7003); + s.connect(remote3.getAddress(), remote3.getPort()); + assertEquals(s.getRemoteSocketAddress(), remote3); + testConnectWithIllegalArguments(s); + assertEquals(s.getRemoteSocketAddress(), remote3); + + // connect(InetAddress, int) + var remote4 = new InetSocketAddress(getLoopbackAddress(), 7004); + s.connect(remote4.getAddress(), remote4.getPort()); + assertEquals(s.getRemoteSocketAddress(), remote4); + testConnectWithIllegalArguments(s); + assertEquals(s.getRemoteSocketAddress(), remote4); + } + } + + /** + * Test connect method with a closed socket. + */ + public void testClosedSocket() throws Exception { + DatagramChannel dc = DatagramChannel.open(); + DatagramSocket s = dc.socket(); + dc.close(); + + testConnectWithIllegalArguments(s); + + // connect does not throw an exception when closed + var remote = new InetSocketAddress(getLoopbackAddress(), 7001); + s.connect(remote); + s.connect(remote.getAddress(), remote.getPort()); + } +} diff --git a/test/jdk/java/nio/channels/DatagramChannel/AdaptorGetters.java b/test/jdk/java/nio/channels/DatagramChannel/AdaptorGetters.java new file mode 100644 index 00000000000..9e12f46be04 --- /dev/null +++ b/test/jdk/java/nio/channels/DatagramChannel/AdaptorGetters.java @@ -0,0 +1,223 @@ +/* + * 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 8232673 + * @summary Test the DatagramChannel socket adaptor getter methods + * @run testng AdaptorGetters + */ + +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.nio.channels.DatagramChannel; + +import org.testng.annotations.Test; +import static org.testng.Assert.*; + +@Test +public class AdaptorGetters { + + /** + * Test getters on unbound socket, before and after it is closed. + */ + public void testUnboundSocket() throws Exception { + DatagramChannel dc = DatagramChannel.open(); + DatagramSocket s = dc.socket(); + try { + + // state + assertFalse(s.isBound()); + assertFalse(s.isConnected()); + assertFalse(s.isClosed()); + + // local address + assertTrue(s.getLocalAddress().isAnyLocalAddress()); + assertTrue(s.getLocalPort() == 0); + assertTrue(s.getLocalSocketAddress() == null); + + // remote address + assertTrue(s.getInetAddress() == null); + assertTrue(s.getPort() == -1); + + } finally { + dc.close(); + } + + // state + assertFalse(s.isBound()); + assertFalse(s.isConnected()); + assertTrue(s.isClosed()); + + // local address + assertTrue(s.getLocalAddress() == null); + assertTrue(s.getLocalPort() == -1); + assertTrue(s.getLocalSocketAddress() == null); + + // remote address + assertTrue(s.getInetAddress() == null); + assertTrue(s.getPort() == -1); + assertTrue((s.getRemoteSocketAddress() == null)); + } + + /** + * Test getters on bound socket, before and after it is closed. + */ + public void testBoundSocket() throws Exception { + DatagramChannel dc = DatagramChannel.open(); + DatagramSocket s = dc.socket(); + try { + dc.bind(new InetSocketAddress(0)); + var localAddress = (InetSocketAddress) dc.getLocalAddress(); + + // state + assertTrue(s.isBound()); + assertFalse(s.isConnected()); + assertFalse(s.isClosed()); + + // local address + assertEquals(s.getLocalAddress(), localAddress.getAddress()); + assertTrue(s.getLocalPort() == localAddress.getPort()); + assertEquals(s.getLocalSocketAddress(), localAddress); + + // remote address + assertTrue(s.getInetAddress() == null); + assertTrue(s.getPort() == -1); + assertTrue((s.getRemoteSocketAddress() == null)); + + } finally { + dc.close(); + } + + // state + assertTrue(s.isBound()); + assertFalse(s.isConnected()); + assertTrue(s.isClosed()); + + // local address + assertTrue(s.getLocalAddress() == null); + assertTrue(s.getLocalPort() == -1); + assertTrue(s.getLocalSocketAddress() == null); + + // remote address + assertTrue(s.getInetAddress() == null); + assertTrue(s.getPort() == -1); + assertTrue((s.getRemoteSocketAddress() == null)); + } + + /** + * Test getters on connected socket, before and after it is closed. + */ + public void testConnectedSocket() throws Exception { + var loopback = InetAddress.getLoopbackAddress(); + var remoteAddress = new InetSocketAddress(loopback, 7777); + DatagramChannel dc = DatagramChannel.open(); + DatagramSocket s = dc.socket(); + try { + dc.connect(remoteAddress); + var localAddress = (InetSocketAddress) dc.getLocalAddress(); + + // state + assertTrue(s.isBound()); + assertTrue(s.isConnected()); + assertFalse(s.isClosed()); + + // local address + assertEquals(s.getLocalAddress(), localAddress.getAddress()); + assertTrue(s.getLocalPort() == localAddress.getPort()); + assertEquals(s.getLocalSocketAddress(), localAddress); + + // remote address + assertEquals(s.getInetAddress(), remoteAddress.getAddress()); + assertTrue(s.getPort() == remoteAddress.getPort()); + assertEquals(s.getRemoteSocketAddress(), remoteAddress); + + } finally { + dc.close(); + } + + // state + assertTrue(s.isBound()); + assertTrue(s.isConnected()); + assertTrue(s.isClosed()); + + // local address + assertTrue(s.getLocalAddress() == null); + assertTrue(s.getLocalPort() == -1); + assertTrue(s.getLocalSocketAddress() == null); + + // remote address + assertEquals(s.getInetAddress(), remoteAddress.getAddress()); + assertTrue(s.getPort() == remoteAddress.getPort()); + assertEquals(s.getRemoteSocketAddress(), remoteAddress); + } + + /** + * Test getters on disconnected socket, before and after it is closed. + */ + public void testDisconnectedSocket() throws Exception { + DatagramChannel dc = DatagramChannel.open(); + DatagramSocket s = dc.socket(); + try { + var loopback = InetAddress.getLoopbackAddress(); + dc.connect(new InetSocketAddress(loopback, 7777)); + dc.disconnect(); + + var localAddress = (InetSocketAddress) dc.getLocalAddress(); + + // state + assertTrue(s.isBound()); + assertFalse(s.isConnected()); + assertFalse(s.isClosed()); + + // local address + assertEquals(s.getLocalAddress(), localAddress.getAddress()); + assertTrue(s.getLocalPort() == localAddress.getPort()); + assertEquals(s.getLocalSocketAddress(), localAddress); + + // remote address + assertTrue(s.getInetAddress() == null); + assertTrue(s.getPort() == -1); + assertTrue((s.getRemoteSocketAddress() == null)); + + + } finally { + dc.close(); + } + + // state + assertTrue(s.isBound()); + assertFalse(s.isConnected()); + assertTrue(s.isClosed()); + + // local address + assertTrue(s.getLocalAddress() == null); + assertTrue(s.getLocalPort() == -1); + assertTrue(s.getLocalSocketAddress() == null); + + // remote address + assertTrue(s.getInetAddress() == null); + assertTrue(s.getPort() == -1); + assertTrue((s.getRemoteSocketAddress() == null)); + } +} diff --git a/test/jdk/java/nio/channels/etc/AdaptorCloseAndInterrupt.java b/test/jdk/java/nio/channels/etc/AdaptorCloseAndInterrupt.java index 5da7ed90db5..8f85e5cadfb 100644 --- a/test/jdk/java/nio/channels/etc/AdaptorCloseAndInterrupt.java +++ b/test/jdk/java/nio/channels/etc/AdaptorCloseAndInterrupt.java @@ -22,7 +22,7 @@ */ /* @test - * @bug 7184932 + * @bug 7184932 8232673 * @summary Test asynchronous close and interrupt of timed socket adapter methods * @key randomness intermittent */ @@ -78,8 +78,10 @@ public class AdaptorCloseAndInterrupt { try (DatagramChannel peer = DatagramChannel.open()) { peer.socket().bind(null); - new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncClose(); - new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncInterrupt(); + new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncClose(0); + new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncClose(30_000); + new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncInterrupt(0); + new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncInterrupt(30_000); } new AdaptorCloseAndInterrupt().ssAcceptAsyncClose(); @@ -138,11 +140,10 @@ public class AdaptorCloseAndInterrupt { } } - void dcReceiveAsyncClose() throws IOException { + void dcReceiveAsyncClose(int timeout) throws IOException { DatagramChannel dc = DatagramChannel.open(); - dc.connect(new InetSocketAddress( - InetAddress.getLoopbackAddress(), port)); - dc.socket().setSoTimeout(30*1000); + dc.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(), port)); + dc.socket().setSoTimeout(timeout); doAsyncClose(dc); @@ -150,24 +151,23 @@ public class AdaptorCloseAndInterrupt { dc.socket().receive(new DatagramPacket(new byte[100], 100)); System.err.format("close() was invoked: %s%n", isClosed.get()); throw new RuntimeException("receive should not have completed"); - } catch (ClosedChannelException expected) {} + } catch (SocketException expected) { } if (!dc.socket().isClosed()) throw new RuntimeException("socket is not closed"); } - void dcReceiveAsyncInterrupt() throws IOException { + void dcReceiveAsyncInterrupt(int timeout) throws IOException { DatagramChannel dc = DatagramChannel.open(); - dc.connect(new InetSocketAddress( - InetAddress.getLoopbackAddress(), port)); - dc.socket().setSoTimeout(30*1000); + dc.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(), port)); + dc.socket().setSoTimeout(timeout); doAsyncInterrupt(); try { dc.socket().receive(new DatagramPacket(new byte[100], 100)); throw new RuntimeException("receive should not have completed"); - } catch (ClosedByInterruptException expected) { + } catch (SocketException expected) { System.out.format("interrupt() was invoked: %s%n", isInterrupted.get()); System.out.format("dcReceiveAsyncInterrupt was interrupted: %s%n",