< prev index next >
src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java
Print this page
rev 48993 : imported patch nio
*** 33,50 ****
--- 33,52 ----
import java.net.SocketAddress;
import java.net.SocketOption;
import java.net.StandardProtocolFamily;
import java.net.StandardSocketOptions;
import java.nio.channels.AlreadyBoundException;
+ import java.nio.channels.AsynchronousCloseException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.NotYetBoundException;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
import java.util.Collections;
import java.util.HashSet;
+ import java.util.Objects;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
import sun.net.NetHooks;
*** 54,88 ****
class ServerSocketChannelImpl
extends ServerSocketChannel
implements SelChImpl
{
-
// Used to make native close and configure calls
private static NativeDispatcher nd;
// Our file descriptor
private final FileDescriptor fd;
private final int fdVal;
! // ID of native thread currently blocked in this channel, for signalling
! private volatile long thread;
!
! // Lock held by thread currently blocked in this channel
private final ReentrantLock acceptLock = new ReentrantLock();
// Lock held by any thread that modifies the state fields declared below
// DO NOT invoke a blocking I/O operation while holding this lock!
private final Object stateLock = new Object();
// -- The following fields are protected by stateLock
// Channel state, increases monotonically
- private static final int ST_UNINITIALIZED = -1;
private static final int ST_INUSE = 0;
! private static final int ST_KILLED = 1;
! private int state = ST_UNINITIALIZED;
// Binding
private InetSocketAddress localAddress; // null => unbound
// set true when exclusive binding is on and SO_REUSEADDR is emulated
--- 56,90 ----
class ServerSocketChannelImpl
extends ServerSocketChannel
implements SelChImpl
{
// Used to make native close and configure calls
private static NativeDispatcher nd;
// Our file descriptor
private final FileDescriptor fd;
private final int fdVal;
! // Lock held by thread currently blocked on this channel
private final ReentrantLock acceptLock = new ReentrantLock();
// Lock held by any thread that modifies the state fields declared below
// DO NOT invoke a blocking I/O operation while holding this lock!
private final Object stateLock = new Object();
// -- The following fields are protected by stateLock
// Channel state, increases monotonically
private static final int ST_INUSE = 0;
! private static final int ST_CLOSING = 1;
! private static final int ST_KILLPENDING = 2;
! private static final int ST_KILLED = 3;
! private int state;
!
! // ID of native thread currently blocked in this channel, for signalling
! private long thread;
// Binding
private InetSocketAddress localAddress; // null => unbound
// set true when exclusive binding is on and SO_REUSEADDR is emulated
*** 96,121 ****
ServerSocketChannelImpl(SelectorProvider sp) throws IOException {
super(sp);
this.fd = Net.serverSocket(true);
this.fdVal = IOUtil.fdVal(fd);
- this.state = ST_INUSE;
}
! ServerSocketChannelImpl(SelectorProvider sp,
! FileDescriptor fd,
! boolean bound)
throws IOException
{
super(sp);
this.fd = fd;
this.fdVal = IOUtil.fdVal(fd);
! this.state = ST_INUSE;
! if (bound)
localAddress = Net.localAddress(fd);
}
public ServerSocket socket() {
synchronized (stateLock) {
if (socket == null)
socket = ServerSocketAdaptor.create(this);
return socket;
--- 98,129 ----
ServerSocketChannelImpl(SelectorProvider sp) throws IOException {
super(sp);
this.fd = Net.serverSocket(true);
this.fdVal = IOUtil.fdVal(fd);
}
! ServerSocketChannelImpl(SelectorProvider sp, FileDescriptor fd, boolean bound)
throws IOException
{
super(sp);
this.fd = fd;
this.fdVal = IOUtil.fdVal(fd);
! if (bound) {
! synchronized (stateLock) {
localAddress = Net.localAddress(fd);
}
+ }
+ }
+ // @throws ClosedChannelException if channel is closed
+ private void ensureOpen() throws ClosedChannelException {
+ if (!isOpen())
+ throw new ClosedChannelException();
+ }
+
+ @Override
public ServerSocket socket() {
synchronized (stateLock) {
if (socket == null)
socket = ServerSocketAdaptor.create(this);
return socket;
*** 123,162 ****
}
@Override
public SocketAddress getLocalAddress() throws IOException {
synchronized (stateLock) {
! if (!isOpen())
! throw new ClosedChannelException();
! return localAddress == null ? localAddress
! : Net.getRevealedLocalAddress(
! Net.asInetSocketAddress(localAddress));
}
}
@Override
public <T> ServerSocketChannel setOption(SocketOption<T> name, T value)
throws IOException
{
! if (name == null)
! throw new NullPointerException();
if (!supportedOptions().contains(name))
throw new UnsupportedOperationException("'" + name + "' not supported");
synchronized (stateLock) {
! if (!isOpen())
! throw new ClosedChannelException();
if (name == StandardSocketOptions.IP_TOS) {
ProtocolFamily family = Net.isIPv6Available() ?
StandardProtocolFamily.INET6 : StandardProtocolFamily.INET;
Net.setSocketOption(fd, family, name, value);
return this;
}
! if (name == StandardSocketOptions.SO_REUSEADDR &&
! Net.useExclusiveBind())
! {
// SO_REUSEADDR emulated when using exclusive bind
isReuseAddress = (Boolean)value;
} else {
// no options that require special handling
Net.setSocketOption(fd, Net.UNSPEC, name, value);
--- 131,165 ----
}
@Override
public SocketAddress getLocalAddress() throws IOException {
synchronized (stateLock) {
! ensureOpen();
! return (localAddress == null)
! ? null
! : Net.getRevealedLocalAddress(localAddress);
}
}
@Override
public <T> ServerSocketChannel setOption(SocketOption<T> name, T value)
throws IOException
{
! Objects.requireNonNull(name);
if (!supportedOptions().contains(name))
throw new UnsupportedOperationException("'" + name + "' not supported");
synchronized (stateLock) {
! ensureOpen();
if (name == StandardSocketOptions.IP_TOS) {
ProtocolFamily family = Net.isIPv6Available() ?
StandardProtocolFamily.INET6 : StandardProtocolFamily.INET;
Net.setSocketOption(fd, family, name, value);
return this;
}
! if (name == StandardSocketOptions.SO_REUSEADDR && Net.useExclusiveBind()) {
// SO_REUSEADDR emulated when using exclusive bind
isReuseAddress = (Boolean)value;
} else {
// no options that require special handling
Net.setSocketOption(fd, Net.UNSPEC, name, value);
*** 168,188 ****
@Override
@SuppressWarnings("unchecked")
public <T> T getOption(SocketOption<T> name)
throws IOException
{
! if (name == null)
! throw new NullPointerException();
if (!supportedOptions().contains(name))
throw new UnsupportedOperationException("'" + name + "' not supported");
synchronized (stateLock) {
! if (!isOpen())
! throw new ClosedChannelException();
! if (name == StandardSocketOptions.SO_REUSEADDR &&
! Net.useExclusiveBind())
! {
// SO_REUSEADDR emulated when using exclusive bind
return (T)Boolean.valueOf(isReuseAddress);
}
// no options that require special handling
return (T) Net.getSocketOption(fd, Net.UNSPEC, name);
--- 171,187 ----
@Override
@SuppressWarnings("unchecked")
public <T> T getOption(SocketOption<T> name)
throws IOException
{
! Objects.requireNonNull(name);
if (!supportedOptions().contains(name))
throw new UnsupportedOperationException("'" + name + "' not supported");
synchronized (stateLock) {
! ensureOpen();
! if (name == StandardSocketOptions.SO_REUSEADDR && Net.useExclusiveBind()) {
// SO_REUSEADDR emulated when using exclusive bind
return (T)Boolean.valueOf(isReuseAddress);
}
// no options that require special handling
return (T) Net.getSocketOption(fd, Net.UNSPEC, name);
*** 191,201 ****
private static class DefaultOptionsHolder {
static final Set<SocketOption<?>> defaultOptions = defaultOptions();
private static Set<SocketOption<?>> defaultOptions() {
! HashSet<SocketOption<?>> set = new HashSet<>(2);
set.add(StandardSocketOptions.SO_RCVBUF);
set.add(StandardSocketOptions.SO_REUSEADDR);
if (Net.isReusePortAvailable()) {
set.add(StandardSocketOptions.SO_REUSEPORT);
}
--- 190,200 ----
private static class DefaultOptionsHolder {
static final Set<SocketOption<?>> defaultOptions = defaultOptions();
private static Set<SocketOption<?>> defaultOptions() {
! HashSet<SocketOption<?>> set = new HashSet<>();
set.add(StandardSocketOptions.SO_RCVBUF);
set.add(StandardSocketOptions.SO_REUSEADDR);
if (Net.isReusePortAvailable()) {
set.add(StandardSocketOptions.SO_REUSEPORT);
}
*** 207,294 ****
@Override
public final Set<SocketOption<?>> supportedOptions() {
return DefaultOptionsHolder.defaultOptions;
}
- public boolean isBound() {
- synchronized (stateLock) {
- return localAddress != null;
- }
- }
-
- public InetSocketAddress localAddress() {
- synchronized (stateLock) {
- return localAddress;
- }
- }
-
@Override
public ServerSocketChannel bind(SocketAddress local, int backlog) throws IOException {
acceptLock.lock();
try {
! if (!isOpen())
! throw new ClosedChannelException();
! if (isBound())
throw new AlreadyBoundException();
! InetSocketAddress isa = (local == null) ? new InetSocketAddress(0) :
! Net.checkAddress(local);
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkListen(isa.getPort());
NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort());
Net.bind(fd, isa.getAddress(), isa.getPort());
Net.listen(fd, backlog < 1 ? 50 : backlog);
- synchronized (stateLock) {
localAddress = Net.localAddress(fd);
}
} finally {
acceptLock.unlock();
}
return this;
}
public SocketChannel accept() throws IOException {
acceptLock.lock();
try {
- if (!isOpen())
- throw new ClosedChannelException();
- if (!isBound())
- throw new NotYetBoundException();
- SocketChannel sc = null;
-
int n = 0;
FileDescriptor newfd = new FileDescriptor();
InetSocketAddress[] isaa = new InetSocketAddress[1];
try {
! begin();
! if (!isOpen())
! return null;
! thread = NativeThread.current();
! for (;;) {
n = accept(this.fd, newfd, isaa);
! if ((n == IOStatus.INTERRUPTED) && isOpen())
! continue;
! break;
! }
} finally {
! thread = 0;
! end(n > 0);
assert IOStatus.check(n);
}
if (n < 1)
return null;
IOUtil.configureBlocking(newfd, true);
InetSocketAddress isa = isaa[0];
! sc = new SocketChannelImpl(provider(), newfd, isa);
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
try {
! sm.checkAccept(isa.getAddress().getHostAddress(),
! isa.getPort());
} catch (SecurityException x) {
sc.close();
throw x;
}
}
--- 206,312 ----
@Override
public final Set<SocketOption<?>> supportedOptions() {
return DefaultOptionsHolder.defaultOptions;
}
@Override
public ServerSocketChannel bind(SocketAddress local, int backlog) throws IOException {
acceptLock.lock();
try {
! synchronized (stateLock) {
! ensureOpen();
! if (localAddress != null)
throw new AlreadyBoundException();
! InetSocketAddress isa = (local == null)
! ? new InetSocketAddress(0)
! : Net.checkAddress(local);
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkListen(isa.getPort());
NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort());
Net.bind(fd, isa.getAddress(), isa.getPort());
Net.listen(fd, backlog < 1 ? 50 : backlog);
localAddress = Net.localAddress(fd);
}
} finally {
acceptLock.unlock();
}
return this;
}
+ /**
+ * Marks the beginning of an I/O operation that might block.
+ *
+ * @throws ClosedChannelException if the channel is closed
+ * @throws NotYetBoundException if the channel's socket has not been bound yet
+ */
+ private void begin(boolean blocking) throws ClosedChannelException {
+ if (blocking)
+ begin(); // set blocker to close channel if interrupted
+ synchronized (stateLock) {
+ ensureOpen();
+ if (localAddress == null)
+ throw new NotYetBoundException();
+ if (blocking)
+ thread = NativeThread.current();
+ }
+ }
+
+ /**
+ * Marks the end of an I/O operation that may have blocked.
+ *
+ * @throws AsynchronousCloseException if the channel was closed due to this
+ * thread being interrupted on a blocking I/O operation.
+ */
+ private void end(boolean blocking, boolean completed)
+ throws AsynchronousCloseException
+ {
+ if (blocking) {
+ synchronized (stateLock) {
+ thread = 0;
+ // notify any thread waiting in implCloseSelectableChannel
+ if (state == ST_CLOSING) {
+ stateLock.notifyAll();
+ }
+ }
+ end(completed);
+ }
+ }
+
+ @Override
public SocketChannel accept() throws IOException {
acceptLock.lock();
try {
int n = 0;
FileDescriptor newfd = new FileDescriptor();
InetSocketAddress[] isaa = new InetSocketAddress[1];
+ boolean blocking = isBlocking();
try {
! begin(blocking);
! do {
n = accept(this.fd, newfd, isaa);
! } while (n == IOStatus.INTERRUPTED && isOpen());
} finally {
! end(blocking, n > 0);
assert IOStatus.check(n);
}
if (n < 1)
return null;
+ // newly accepted socket is initially in blocking mode
IOUtil.configureBlocking(newfd, true);
+
InetSocketAddress isa = isaa[0];
! SocketChannel sc = new SocketChannelImpl(provider(), newfd, isa);
!
! // check permitted to accept connections from the remote address
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
try {
! sm.checkAccept(isa.getAddress().getHostAddress(), isa.getPort());
} catch (SecurityException x) {
sc.close();
throw x;
}
}
*** 297,333 ****
} finally {
acceptLock.unlock();
}
}
protected void implConfigureBlocking(boolean block) throws IOException {
IOUtil.configureBlocking(fd, block);
}
protected void implCloseSelectableChannel() throws IOException {
synchronized (stateLock) {
! if (state != ST_KILLED)
! nd.preClose(fd);
long th = thread;
! if (th != 0)
NativeThread.signal(th);
if (!isRegistered())
kill();
! }
}
public void kill() throws IOException {
synchronized (stateLock) {
! if (state == ST_KILLED)
! return;
! if (state == ST_UNINITIALIZED) {
state = ST_KILLED;
- return;
- }
- assert !isOpen() && !isRegistered();
nd.close(fd);
! state = ST_KILLED;
}
}
/**
* Translates native poll revent set into a ready operation set
--- 315,451 ----
} finally {
acceptLock.unlock();
}
}
+ @Override
protected void implConfigureBlocking(boolean block) throws IOException {
+ acceptLock.lock();
+ try {
+ synchronized (stateLock) {
+ ensureOpen();
IOUtil.configureBlocking(fd, block);
}
+ } finally {
+ acceptLock.unlock();
+ }
+ }
+ /**
+ * Invoked by implCloseChannel to close the channel.
+ *
+ * This method waits for outstanding I/O operations to complete. When in
+ * blocking mode, the socket is pre-closed and the threads in blocking I/O
+ * operations are signalled to ensure that the outstanding I/O operations
+ * complete quickly.
+ *
+ * The socket is closed by this method when it is not registered with a
+ * Selector. Note that a channel configured blocking may be registered with
+ * a Selector. This arises when a key is canceled and the channel configured
+ * to blocking mode before the key is flushed from the Selector.
+ */
+ @Override
protected void implCloseSelectableChannel() throws IOException {
+ assert !isOpen();
+
+ boolean interrupted = false;
+ boolean blocking;
+
+ // set state to ST_CLOSING
synchronized (stateLock) {
! assert state < ST_CLOSING;
! state = ST_CLOSING;
! blocking = isBlocking();
! }
!
! // wait for any outstanding accept to complete
! if (blocking) {
! synchronized (stateLock) {
! assert state == ST_CLOSING;
long th = thread;
! if (th != 0) {
! nd.preClose(fd);
NativeThread.signal(th);
+
+ // wait for accept operation to end
+ while (thread != 0) {
+ try {
+ stateLock.wait();
+ } catch (InterruptedException e) {
+ interrupted = true;
+ }
+ }
+ }
+ }
+ } else {
+ // non-blocking mode: wait for accept to complete
+ acceptLock.lock();
+ acceptLock.unlock();
+ }
+
+ // set state to ST_KILLPENDING
+ synchronized (stateLock) {
+ assert state == ST_CLOSING;
+ state = ST_KILLPENDING;
+ }
+
+ // close socket if not registered with Selector
if (!isRegistered())
kill();
!
! // restore interrupt status
! if (interrupted)
! Thread.currentThread().interrupt();
}
+ @Override
public void kill() throws IOException {
synchronized (stateLock) {
! if (state == ST_KILLPENDING) {
state = ST_KILLED;
nd.close(fd);
! }
! }
! }
!
! /**
! * Returns true if channel's socket is bound
! */
! boolean isBound() {
! synchronized (stateLock) {
! return localAddress != null;
! }
! }
!
! /**
! * Returns the local address, or null if not bound
! */
! InetSocketAddress localAddress() {
! synchronized (stateLock) {
! return localAddress;
! }
! }
!
! /**
! * Poll this channel's socket for a new connection up to the given timeout.
! * @return {@code true} if there is a connection to accept
! */
! boolean pollAccept(long timeout) throws IOException {
! assert Thread.holdsLock(blockingLock()) && isBlocking();
! acceptLock.lock();
! try {
! boolean polled = false;
! try {
! begin(true);
! int n = Net.poll(fd, Net.POLLIN, timeout);
! polled = (n > 0);
! } finally {
! end(true, polled);
! }
! return polled;
! } finally {
! acceptLock.unlock();
}
}
/**
* Translates native poll revent set into a ready operation set
*** 365,399 ****
public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) {
return translateReadyOps(ops, 0, sk);
}
- // package-private
- int poll(int events, long timeout) throws IOException {
- assert Thread.holdsLock(blockingLock()) && !isBlocking();
-
- acceptLock.lock();
- try {
- int n = 0;
- try {
- begin();
- synchronized (stateLock) {
- if (!isOpen())
- return 0;
- thread = NativeThread.current();
- }
- n = Net.poll(fd, events, timeout);
- } finally {
- thread = 0;
- end(n > 0);
- }
- return n;
- } finally {
- acceptLock.unlock();
- }
- }
-
/**
* Translates an interest operation set into a native poll event set
*/
public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) {
int newOps = 0;
--- 483,492 ----
*** 419,429 ****
sb.append('[');
if (!isOpen()) {
sb.append("closed");
} else {
synchronized (stateLock) {
! InetSocketAddress addr = localAddress();
if (addr == null) {
sb.append("unbound");
} else {
sb.append(Net.getRevealedLocalAddressAsString(addr));
}
--- 512,522 ----
sb.append('[');
if (!isOpen()) {
sb.append("closed");
} else {
synchronized (stateLock) {
! InetSocketAddress addr = localAddress;
if (addr == null) {
sb.append("unbound");
} else {
sb.append(Net.getRevealedLocalAddressAsString(addr));
}
*** 436,446 ****
/**
* Accept a connection on a socket.
*
* @implNote Wrap native call to allow instrumentation.
*/
! private int accept(FileDescriptor ssfd, FileDescriptor newfd,
InetSocketAddress[] isaa)
throws IOException
{
return accept0(ssfd, newfd, isaa);
}
--- 529,540 ----
/**
* Accept a connection on a socket.
*
* @implNote Wrap native call to allow instrumentation.
*/
! private int accept(FileDescriptor ssfd,
! FileDescriptor newfd,
InetSocketAddress[] isaa)
throws IOException
{
return accept0(ssfd, newfd, isaa);
}
*** 450,460 ****
// Accepts a new connection, setting the given file descriptor to refer to
// the new socket and setting isaa[0] to the socket's remote address.
// Returns 1 on success, or IOStatus.UNAVAILABLE (if non-blocking and no
// connections are pending) or IOStatus.INTERRUPTED.
//
! private native int accept0(FileDescriptor ssfd, FileDescriptor newfd,
InetSocketAddress[] isaa)
throws IOException;
private static native void initIDs();
--- 544,555 ----
// Accepts a new connection, setting the given file descriptor to refer to
// the new socket and setting isaa[0] to the socket's remote address.
// Returns 1 on success, or IOStatus.UNAVAILABLE (if non-blocking and no
// connections are pending) or IOStatus.INTERRUPTED.
//
! private native int accept0(FileDescriptor ssfd,
! FileDescriptor newfd,
InetSocketAddress[] isaa)
throws IOException;
private static native void initIDs();
< prev index next >