OpenJDK / loom / loom
changeset 53292:293ab6e11a1c fibers
ServerSocket.implAccept musy handle a mix of SocketImpl implementations
author | alanb |
---|---|
date | Sun, 13 Jan 2019 07:59:01 +0000 |
parents | 5848a43df1cd |
children | d6496338e000 |
files | src/java.base/share/classes/java/net/ServerSocket.java src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java |
diffstat | 2 files changed, 173 insertions(+), 45 deletions(-) [+] |
line wrap: on
line diff
--- a/src/java.base/share/classes/java/net/ServerSocket.java Sun Jan 13 07:56:59 2019 +0000 +++ b/src/java.base/share/classes/java/net/ServerSocket.java Sun Jan 13 07:59:01 2019 +0000 @@ -543,37 +543,78 @@ * @spec JSR-51 */ protected final void implAccept(Socket s) throws IOException { - // create a new impl for the new connection - SocketImpl si = Socket.createImpl(); - getImpl().accept(si); + SocketImpl impl = getImpl(); + SocketImpl si = s.impl; + + // Socket does not have a SocketImpl + if (si == null) { + // create a SocketImpl and accept the connection + si = Socket.createImpl(); + impl.accept(si); + securityCheckAccept(si); + + // a custom impl has accepted the connection with a NIO SocketImpl + if (!(impl instanceof NioSocketImpl) && (si instanceof NioSocketImpl)) { + ((NioSocketImpl) si).postCustomAccept(); + } + + // bind Socket to the SocketImpl and update socket state + s.setImpl(si); + s.postAccept(); + return; + } + + // ServerSocket or Socket is using NIO SocketImpl + if (impl instanceof NioSocketImpl || si instanceof NioSocketImpl) { + // not implemented yet + if (impl instanceof NioSocketImpl && impl.getClass() != NioSocketImpl.class) + throw new UnsupportedOperationException(); + if (si instanceof NioSocketImpl && si.getClass() != NioSocketImpl.class) + throw new UnsupportedOperationException(); + + // accept connection via new SocketImpl + NioSocketImpl nsi = new NioSocketImpl(false); + impl.accept(nsi); + securityCheckAccept(nsi); + + // copy state to the existing SocketImpl and update socket state + nsi.copyTo(si); + s.postAccept(); + return; + } + + // ServerSocket and Socket bound to custom SocketImpls + s.impl = null; // break connection to impl + boolean completed = false; + try { + si.reset(); + si.fd = new FileDescriptor(); + si.address = new InetAddress(); + impl.accept(si); + securityCheckAccept(si); + completed = true; + } finally { + if (!completed) + si.reset(); + s.impl = si; // restore connection to impl + } + s.postAccept(); + } + + /** + * Invokes the security manager's checkAccept method. If the permission + * check fails then it closes the SocketImpl. + */ + private void securityCheckAccept(SocketImpl si) throws IOException { SecurityManager sm = System.getSecurityManager(); if (sm != null) { try { sm.checkAccept(si.getInetAddress().getHostAddress(), si.getPort()); - } catch (SecurityException e) { + } catch (SecurityException se) { si.close(); - throw e; + throw se; } } - - // copy the timeout from the old impl to the new impl - SocketImpl previous = s.impl; - if (previous != null) { - int timeout = (int) previous.getOption(SocketOptions.SO_TIMEOUT); - if (timeout != 0) { - si.setOption(SocketOptions.SO_TIMEOUT, timeout); - } - } - - // replace the impl, eagerly closing the old impl to ensure any - // resources can be released early - s.setImpl(si); - s.postAccept(); - if (previous != null) { - try { - previous.close(); - } catch (IOException ignore) { } - } } /**
--- a/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java Sun Jan 13 07:56:59 2019 +0000 +++ b/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java Sun Jan 13 07:59:01 2019 +0000 @@ -379,6 +379,74 @@ } /** + * For use by ServerSocket to set the state and other fields after a + * connection is accepted by a ServerSocket using a custom SocketImpl. + * The protected fields defined by SocketImpl should be set. + */ + public void postCustomAccept() { + synchronized (stateLock) { + assert state == ST_NEW; + assert fd.valid() && localport != 0 && address != null && port != 0; + stream = true; + closer = FileDescriptorCloser.create(this); + state = ST_CONNECTED; + } + } + + /** + * For use by ServerSocket to copy the state from this connected SocketImpl + * to a target SocketImpl. If the target SocketImpl is not a newly created + * SocketImpl then it is first closed to release any resources. The target + * SocketImpl becomes the owner of the file descriptor, this SocketImpl + * is marked as closed and should be discarded. + */ + public void copyTo(SocketImpl si) { + if (si instanceof NioSocketImpl) { + NioSocketImpl nsi = (NioSocketImpl) si; + synchronized (nsi.stateLock) { + if (nsi.state != ST_NEW) { + try { + nsi.close(); + } catch (IOException ignore) { } + } + synchronized (this.stateLock) { + // this SocketImpl should be connected + assert state == ST_CONNECTED && fd.valid() + && localport != 0 && address != null && port != 0; + + // copy fields + nsi.fd = this.fd; + nsi.stream = this.stream; + nsi.closer = FileDescriptorCloser.create(nsi); + nsi.localport = this.localport; + nsi.address = this.address; + nsi.port = this.port; + nsi.state = ST_CONNECTED; + + // disable closer to prevent GC'ing of this impl from + // closing the file descriptor + this.closer.disable(); + this.state = ST_CLOSED; + } + } + } else { + synchronized (this.stateLock) { + // this SocketImpl should be connected + assert state == ST_CONNECTED && fd.valid() + && localport != 0 && address != null && port != 0; + + // set fields in foreign impl + setSocketImplFields(si, fd, localport, address, port); + + // disable closer to prevent GC'ing of this impl from + // closing the file descriptor + this.closer.disable(); + this.state = ST_CLOSED; + } + } + } + + /** * Marks the beginning of a connect operation that might block. * @throws SocketException if the socket is closed or already connected */ @@ -636,30 +704,14 @@ nsi.state = ST_CONNECTED; } } else { - // foreign SocketImpl, patch the known fields - PrivilegedExceptionAction<Void> pa = () -> { - setSocketImplField(si, "fd", newfd); - setSocketImplField(si, "localport", localAddress.getPort()); - setSocketImplField(si, "address", remoteAddress.getAddress()); - setSocketImplField(si, "port", remoteAddress.getPort()); - return null; - }; - try { - AccessController.doPrivileged(pa); - } catch (PrivilegedActionException pae) { - throw new InternalError(pae); - } + // set fields in foreign impl + setSocketImplFields(si, newfd, + localAddress.getPort(), + remoteAddress.getAddress(), + remoteAddress.getPort()); } } - private void setSocketImplField(SocketImpl si, String name, Object value) - throws Exception - { - Field field = SocketImpl.class.getDeclaredField(name); - field.setAccessible(true); - field.set(si, value); - } - @Override protected InputStream getInputStream() { return new InputStream() { @@ -1097,6 +1149,10 @@ } } } + + boolean disable() { + return CLOSED.compareAndSet(this, false, true); + } } /** @@ -1126,4 +1182,35 @@ assert fdVal == IOUtil.fdVal(fd); return fdVal; } + + /** + * Sets the SocketImpl fields to the given values. + */ + private static void setSocketImplFields(SocketImpl si, + FileDescriptor fd, + int localport, + InetAddress address, + int port) + { + PrivilegedExceptionAction<Void> pa = () -> { + setSocketImplField(si, "fd", fd); + setSocketImplField(si, "localport", localport); + setSocketImplField(si, "address", address); + setSocketImplField(si, "port", port); + return null; + }; + try { + AccessController.doPrivileged(pa); + } catch (PrivilegedActionException pae) { + throw new InternalError(pae); + } + } + + private static void setSocketImplField(SocketImpl si, String name, Object value) + throws Exception + { + Field field = SocketImpl.class.getDeclaredField(name); + field.setAccessible(true); + field.set(si, value); + } }