8223353: (ch) Change channel close implementation to not wait for I/O threads
authoralanb
Wed, 08 May 2019 08:15:04 +0100
changeset 54754 193a8f1a4f3b
parent 54753 a8535f04b465
child 54755 de34f4b370b0
8223353: (ch) Change channel close implementation to not wait for I/O threads Reviewed-by: dfuchs, chegar
src/java.base/share/classes/java/nio/channels/spi/AbstractInterruptibleChannel.java
src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java
src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java
src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java
src/java.base/unix/classes/sun/nio/ch/SinkChannelImpl.java
src/java.base/unix/classes/sun/nio/ch/SourceChannelImpl.java
--- a/src/java.base/share/classes/java/nio/channels/spi/AbstractInterruptibleChannel.java	Tue May 07 18:24:36 2019 -0400
+++ b/src/java.base/share/classes/java/nio/channels/spi/AbstractInterruptibleChannel.java	Wed May 08 08:15:04 2019 +0100
@@ -30,7 +30,6 @@
 import java.nio.channels.Channel;
 import java.nio.channels.ClosedByInterruptException;
 import java.nio.channels.InterruptibleChannel;
-import java.util.concurrent.locks.ReentrantLock;
 
 import jdk.internal.access.SharedSecrets;
 import sun.nio.ch.Interruptible;
@@ -86,7 +85,7 @@
 public abstract class AbstractInterruptibleChannel
     implements Channel, InterruptibleChannel
 {
-    private final ReentrantLock closeLock = new ReentrantLock();
+    private final Object closeLock = new Object();
     private volatile boolean closed;
 
     /**
@@ -106,14 +105,11 @@
      *          If an I/O error occurs
      */
     public final void close() throws IOException {
-        closeLock.lock();
-        try {
+        synchronized (closeLock) {
             if (closed)
                 return;
             closed = true;
             implCloseChannel();
-        } finally {
-            closeLock.unlock();
         }
     }
 
@@ -157,8 +153,7 @@
         if (interruptor == null) {
             interruptor = new Interruptible() {
                     public void interrupt(Thread target) {
-                        closeLock.lock();
-                        try {
+                        synchronized (closeLock) {
                             if (closed)
                                 return;
                             closed = true;
@@ -166,8 +161,6 @@
                             try {
                                 AbstractInterruptibleChannel.this.implCloseChannel();
                             } catch (IOException x) { }
-                        } finally {
-                            closeLock.unlock();
                         }
                     }};
         }
--- a/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java	Tue May 07 18:24:36 2019 -0400
+++ b/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java	Wed May 08 08:15:04 2019 +0100
@@ -53,7 +53,6 @@
 import java.util.HashSet;
 import java.util.Objects;
 import java.util.Set;
-import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.ReentrantLock;
 
 import sun.net.ResourceManager;
@@ -90,8 +89,7 @@
 
     // 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 ReentrantLock stateLock = new ReentrantLock();
-    private final Condition stateCondition = stateLock.newCondition();
+    private final Object stateLock = new Object();
 
     // -- The following fields are protected by stateLock
 
@@ -99,8 +97,7 @@
     private static final int ST_UNCONNECTED = 0;
     private static final int ST_CONNECTED = 1;
     private static final int ST_CLOSING = 2;
-    private static final int ST_KILLPENDING = 3;
-    private static final int ST_KILLED = 4;
+    private static final int ST_CLOSED = 3;
     private int state;
 
     // IDs of native threads doing reads and writes, for signalling
@@ -181,11 +178,8 @@
                 : StandardProtocolFamily.INET;
         this.fd = fd;
         this.fdVal = IOUtil.fdVal(fd);
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             this.localAddress = Net.localAddress(fd);
-        } finally {
-            stateLock.unlock();
         }
     }
 
@@ -197,36 +191,27 @@
 
     @Override
     public DatagramSocket socket() {
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             if (socket == null)
                 socket = DatagramSocketAdaptor.create(this);
             return socket;
-        } finally {
-            stateLock.unlock();
         }
     }
 
     @Override
     public SocketAddress getLocalAddress() throws IOException {
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             ensureOpen();
             // Perform security check before returning address
             return Net.getRevealedLocalAddress(localAddress);
-        } finally {
-            stateLock.unlock();
         }
     }
 
     @Override
     public SocketAddress getRemoteAddress() throws IOException {
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             ensureOpen();
             return remoteAddress;
-        } finally {
-            stateLock.unlock();
         }
     }
 
@@ -238,8 +223,7 @@
         if (!supportedOptions().contains(name))
             throw new UnsupportedOperationException("'" + name + "' not supported");
 
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             ensureOpen();
 
             if (name == StandardSocketOptions.IP_TOS ||
@@ -279,8 +263,6 @@
             // remaining options don't need any special handling
             Net.setSocketOption(fd, Net.UNSPEC, name, value);
             return this;
-        } finally {
-            stateLock.unlock();
         }
     }
 
@@ -293,8 +275,7 @@
         if (!supportedOptions().contains(name))
             throw new UnsupportedOperationException("'" + name + "' not supported");
 
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             ensureOpen();
 
             if (name == StandardSocketOptions.IP_TOS ||
@@ -333,8 +314,6 @@
 
             // no special handling
             return (T) Net.getSocketOption(fd, Net.UNSPEC, name);
-        } finally {
-            stateLock.unlock();
         }
     }
 
@@ -382,8 +361,7 @@
             begin();
         }
         SocketAddress remote;
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             ensureOpen();
             remote = remoteAddress;
             if ((remote == null) && mustBeConnected)
@@ -392,8 +370,6 @@
                 bindInternal(null);
             if (blocking)
                 readerThread = NativeThread.current();
-        } finally {
-            stateLock.unlock();
         }
         return remote;
     }
@@ -407,15 +383,11 @@
         throws AsynchronousCloseException
     {
         if (blocking) {
-            stateLock.lock();
-            try {
+            synchronized (stateLock) {
                 readerThread = 0;
-                // notify any thread waiting in implCloseSelectableChannel
                 if (state == ST_CLOSING) {
-                    stateCondition.signalAll();
+                    tryFinishClose();
                 }
-            } finally {
-                stateLock.unlock();
             }
             // remove hook for Thread.interrupt
             end(completed);
@@ -708,8 +680,7 @@
             begin();
         }
         SocketAddress remote;
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             ensureOpen();
             remote = remoteAddress;
             if ((remote == null) && mustBeConnected)
@@ -718,8 +689,6 @@
                 bindInternal(null);
             if (blocking)
                 writerThread = NativeThread.current();
-        } finally {
-            stateLock.unlock();
         }
         return remote;
     }
@@ -733,15 +702,11 @@
         throws AsynchronousCloseException
     {
         if (blocking) {
-            stateLock.lock();
-            try {
+            synchronized (stateLock) {
                 writerThread = 0;
-                // notify any thread waiting in implCloseSelectableChannel
                 if (state == ST_CLOSING) {
-                    stateCondition.signalAll();
+                    tryFinishClose();
                 }
-            } finally {
-                stateLock.unlock();
             }
             // remove hook for Thread.interrupt
             end(completed);
@@ -810,12 +775,9 @@
         try {
             writeLock.lock();
             try {
-                stateLock.lock();
-                try {
+                synchronized (stateLock) {
                     ensureOpen();
                     IOUtil.configureBlocking(fd, block);
-                } finally {
-                    stateLock.unlock();
                 }
             } finally {
                 writeLock.unlock();
@@ -826,20 +788,14 @@
     }
 
     InetSocketAddress localAddress() {
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             return localAddress;
-        } finally {
-            stateLock.unlock();
         }
     }
 
     InetSocketAddress remoteAddress() {
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             return remoteAddress;
-        } finally {
-            stateLock.unlock();
         }
     }
 
@@ -849,14 +805,11 @@
         try {
             writeLock.lock();
             try {
-                stateLock.lock();
-                try {
+                synchronized (stateLock) {
                     ensureOpen();
                     if (localAddress != null)
                         throw new AlreadyBoundException();
                     bindInternal(local);
-                } finally {
-                    stateLock.unlock();
                 }
             } finally {
                 writeLock.unlock();
@@ -868,7 +821,7 @@
     }
 
     private void bindInternal(SocketAddress local) throws IOException {
-        assert stateLock.isHeldByCurrentThread() && (localAddress == null);
+        assert Thread.holdsLock(stateLock )&& (localAddress == null);
 
         InetSocketAddress isa;
         if (local == null) {
@@ -891,11 +844,8 @@
 
     @Override
     public boolean isConnected() {
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             return (state == ST_CONNECTED);
-        } finally {
-            stateLock.unlock();
         }
     }
 
@@ -917,8 +867,7 @@
         try {
             writeLock.lock();
             try {
-                stateLock.lock();
-                try {
+                synchronized (stateLock) {
                     ensureOpen();
                     if (state == ST_CONNECTED)
                         throw new AlreadyConnectedException();
@@ -952,9 +901,6 @@
                             IOUtil.configureBlocking(fd, true);
                         }
                     }
-
-                } finally {
-                    stateLock.unlock();
                 }
             } finally {
                 writeLock.unlock();
@@ -971,8 +917,7 @@
         try {
             writeLock.lock();
             try {
-                stateLock.lock();
-                try {
+                synchronized (stateLock) {
                     if (!isOpen() || (state != ST_CONNECTED))
                         return this;
 
@@ -986,8 +931,6 @@
 
                     // refresh local address
                     localAddress = Net.localAddress(fd);
-                } finally {
-                    stateLock.unlock();
                 }
             } finally {
                 writeLock.unlock();
@@ -1035,8 +978,7 @@
         if (sm != null)
             sm.checkMulticast(group);
 
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             ensureOpen();
 
             // check the registry to see if we are already a member of the group
@@ -1091,8 +1033,6 @@
 
             registry.add(key);
             return key;
-        } finally {
-            stateLock.unlock();
         }
     }
 
@@ -1118,8 +1058,7 @@
     void drop(MembershipKeyImpl key) {
         assert key.channel() == this;
 
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             if (!key.isValid())
                 return;
 
@@ -1140,8 +1079,6 @@
 
             key.invalidate();
             registry.remove(key);
-        } finally {
-            stateLock.unlock();
         }
     }
 
@@ -1155,8 +1092,7 @@
         assert key.channel() == this;
         assert key.sourceAddress() == null;
 
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             if (!key.isValid())
                 throw new IllegalStateException("key is no longer valid");
             if (source.isAnyLocalAddress())
@@ -1182,8 +1118,6 @@
                 // ancient kernel
                 throw new UnsupportedOperationException();
             }
-        } finally {
-            stateLock.unlock();
         }
     }
 
@@ -1194,8 +1128,7 @@
         assert key.channel() == this;
         assert key.sourceAddress() == null;
 
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             if (!key.isValid())
                 throw new IllegalStateException("key is no longer valid");
 
@@ -1215,116 +1148,117 @@
                 // should not happen
                 throw new AssertionError(ioe);
             }
-        } finally {
-            stateLock.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.
+     * Closes the socket if there are no I/O operations in progress and the
+     * channel is not registered with a Selector.
      */
-    @Override
-    protected void implCloseSelectableChannel() throws IOException {
-        assert !isOpen();
+    private boolean tryClose() throws IOException {
+        assert Thread.holdsLock(stateLock) && state == ST_CLOSING;
+        if ((readerThread == 0) && (writerThread == 0) && !isRegistered()) {
+            state = ST_CLOSED;
+            try {
+                nd.close(fd);
+            } finally {
+                // notify resource manager
+                ResourceManager.afterUdpClose();
+            }
+            return true;
+        } else {
+            return false;
+        }
+    }
 
-        boolean blocking;
-        boolean interrupted = false;
+    /**
+     * Invokes tryClose to attempt to close the socket.
+     *
+     * This method is used for deferred closing by I/O and Selector operations.
+     */
+    private void tryFinishClose() {
+        try {
+            tryClose();
+        } catch (IOException ignore) { }
+    }
 
-        // set state to ST_CLOSING and invalid membership keys
-        stateLock.lock();
-        try {
+    /**
+     * Closes this channel when configured in blocking mode.
+     *
+     * If there is an I/O operation in progress then the socket is pre-closed
+     * and the I/O threads signalled, in which case the final close is deferred
+     * until all I/O operations complete.
+     */
+    private void implCloseBlockingMode() throws IOException {
+        synchronized (stateLock) {
             assert state < ST_CLOSING;
-            blocking = isBlocking();
             state = ST_CLOSING;
 
             // if member of any multicast groups then invalidate the keys
             if (registry != null)
                 registry.invalidateAll();
-        } finally {
-            stateLock.unlock();
-        }
 
-        // wait for any outstanding I/O operations to complete
-        if (blocking) {
-            stateLock.lock();
-            try {
-                assert state == ST_CLOSING;
+            if (!tryClose()) {
                 long reader = readerThread;
                 long writer = writerThread;
                 if (reader != 0 || writer != 0) {
                     nd.preClose(fd);
-
                     if (reader != 0)
                         NativeThread.signal(reader);
                     if (writer != 0)
                         NativeThread.signal(writer);
-
-                    // wait for blocking I/O operations to end
-                    while (readerThread != 0 || writerThread != 0) {
-                        try {
-                            stateCondition.await();
-                        } catch (InterruptedException e) {
-                            interrupted = true;
-                        }
-                    }
                 }
-            } finally {
-                stateLock.unlock();
-            }
-        } else {
-            // non-blocking mode: wait for read/write to complete
-            readLock.lock();
-            try {
-                writeLock.lock();
-                writeLock.unlock();
-            } finally {
-                readLock.unlock();
             }
         }
+    }
 
-        // set state to ST_KILLPENDING
-        stateLock.lock();
-        try {
-            assert state == ST_CLOSING;
-            state = ST_KILLPENDING;
-        } finally {
-            stateLock.unlock();
+    /**
+     * Closes this channel when configured in non-blocking mode.
+     *
+     * If the channel is registered with a Selector then the close is deferred
+     * until the channel is flushed from all Selectors.
+     */
+    private void implCloseNonBlockingMode() throws IOException {
+        synchronized (stateLock) {
+            assert state < ST_CLOSING;
+            state = ST_CLOSING;
+
+            // if member of any multicast groups then invalidate the keys
+            if (registry != null)
+                registry.invalidateAll();
         }
 
-        // close socket if not registered with Selector
-        if (!isRegistered())
-            kill();
+        // wait for any read/write operations to complete before trying to close
+        readLock.lock();
+        readLock.unlock();
+        writeLock.lock();
+        writeLock.unlock();
+        synchronized (stateLock) {
+            if (state == ST_CLOSING) {
+                tryClose();
+            }
+        }
+    }
 
-        // restore interrupt status
-        if (interrupted)
-            Thread.currentThread().interrupt();
+    /**
+     * Invoked by implCloseChannel to close the channel.
+     */
+    @Override
+    protected void implCloseSelectableChannel() throws IOException {
+        assert !isOpen();
+        if (isBlocking()) {
+            implCloseBlockingMode();
+        } else {
+            implCloseNonBlockingMode();
+        }
     }
 
     @Override
-    public void kill() throws IOException {
-        stateLock.lock();
-        try {
-            if (state == ST_KILLPENDING) {
-                state = ST_KILLED;
-                try {
-                    nd.close(fd);
-                } finally {
-                    // notify resource manager
-                    ResourceManager.afterUdpClose();
-                }
+    public void kill() {
+        synchronized (stateLock) {
+            if (state == ST_CLOSING) {
+                tryFinishClose();
             }
-        } finally {
-            stateLock.unlock();
         }
     }
 
--- a/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java	Tue May 07 18:24:36 2019 -0400
+++ b/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java	Wed May 08 08:15:04 2019 +0100
@@ -46,7 +46,6 @@
 import java.util.HashSet;
 import java.util.Objects;
 import java.util.Set;
-import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.ReentrantLock;
 
 import sun.net.NetHooks;
@@ -72,16 +71,14 @@
 
     // 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 ReentrantLock stateLock = new ReentrantLock();
-    private final Condition stateCondition = stateLock.newCondition();
+    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 static final int ST_CLOSED = 2;
     private int state;
 
     // ID of native thread currently blocked in this channel, for signalling
@@ -112,11 +109,8 @@
         this.fd =  fd;
         this.fdVal = IOUtil.fdVal(fd);
         if (bound) {
-            stateLock.lock();
-            try {
+            synchronized (stateLock) {
                 localAddress = Net.localAddress(fd);
-            } finally {
-                stateLock.unlock();
             }
         }
     }
@@ -129,26 +123,20 @@
 
     @Override
     public ServerSocket socket() {
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             if (socket == null)
                 socket = ServerSocketAdaptor.create(this);
             return socket;
-        } finally {
-            stateLock.unlock();
         }
     }
 
     @Override
     public SocketAddress getLocalAddress() throws IOException {
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             ensureOpen();
             return (localAddress == null)
                     ? null
                     : Net.getRevealedLocalAddress(localAddress);
-        } finally {
-            stateLock.unlock();
         }
     }
 
@@ -159,8 +147,7 @@
         Objects.requireNonNull(name);
         if (!supportedOptions().contains(name))
             throw new UnsupportedOperationException("'" + name + "' not supported");
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             ensureOpen();
 
             if (name == StandardSocketOptions.SO_REUSEADDR && Net.useExclusiveBind()) {
@@ -171,8 +158,6 @@
                 Net.setSocketOption(fd, Net.UNSPEC, name, value);
             }
             return this;
-        } finally {
-            stateLock.unlock();
         }
     }
 
@@ -185,8 +170,7 @@
         if (!supportedOptions().contains(name))
             throw new UnsupportedOperationException("'" + name + "' not supported");
 
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             ensureOpen();
             if (name == StandardSocketOptions.SO_REUSEADDR && Net.useExclusiveBind()) {
                 // SO_REUSEADDR emulated when using exclusive bind
@@ -194,8 +178,6 @@
             }
             // no options that require special handling
             return (T) Net.getSocketOption(fd, Net.UNSPEC, name);
-        } finally {
-            stateLock.unlock();
         }
     }
 
@@ -221,8 +203,7 @@
 
     @Override
     public ServerSocketChannel bind(SocketAddress local, int backlog) throws IOException {
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             ensureOpen();
             if (localAddress != null)
                 throw new AlreadyBoundException();
@@ -236,8 +217,6 @@
             Net.bind(fd, isa.getAddress(), isa.getPort());
             Net.listen(fd, backlog < 1 ? 50 : backlog);
             localAddress = Net.localAddress(fd);
-        } finally {
-            stateLock.unlock();
         }
         return this;
     }
@@ -251,15 +230,12 @@
     private void begin(boolean blocking) throws ClosedChannelException {
         if (blocking)
             begin();  // set blocker to close channel if interrupted
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             ensureOpen();
             if (localAddress == null)
                 throw new NotYetBoundException();
             if (blocking)
                 thread = NativeThread.current();
-        } finally {
-            stateLock.unlock();
         }
     }
 
@@ -273,15 +249,11 @@
         throws AsynchronousCloseException
     {
         if (blocking) {
-            stateLock.lock();
-            try {
+            synchronized (stateLock) {
                 thread = 0;
-                // notify any thread waiting in implCloseSelectableChannel
                 if (state == ST_CLOSING) {
-                    stateCondition.signalAll();
+                    tryFinishClose();
                 }
-            } finally {
-                stateLock.unlock();
             }
             end(completed);
         }
@@ -405,101 +377,99 @@
      */
     private void lockedConfigureBlocking(boolean block) throws IOException {
         assert acceptLock.isHeldByCurrentThread();
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             ensureOpen();
             IOUtil.configureBlocking(fd, block);
-        } finally {
-            stateLock.unlock();
+        }
+    }
+
+    /**
+     * Closes the socket if there are no accept in progress and the channel is
+     * not registered with a Selector.
+     */
+    private boolean tryClose() throws IOException {
+        assert Thread.holdsLock(stateLock) && state == ST_CLOSING;
+        if ((thread == 0) && !isRegistered()) {
+            state = ST_CLOSED;
+            nd.close(fd);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Invokes tryClose to attempt to close the socket.
+     *
+     * This method is used for deferred closing by I/O and Selector operations.
+     */
+    private void tryFinishClose() {
+        try {
+            tryClose();
+        } catch (IOException ignore) { }
+    }
+
+    /**
+     * Closes this channel when configured in blocking mode.
+     *
+     * If there is an accept in progress then the socket is pre-closed and the
+     * accept thread is signalled, in which case the final close is deferred
+     * until the accept aborts.
+     */
+    private void implCloseBlockingMode() throws IOException {
+        synchronized (stateLock) {
+            assert state < ST_CLOSING;
+            state = ST_CLOSING;
+            if (!tryClose()) {
+                long th = thread;
+                if (th != 0) {
+                    nd.preClose(fd);
+                    NativeThread.signal(th);
+                }
+            }
+        }
+    }
+
+    /**
+     * Closes this channel when configured in non-blocking mode.
+     *
+     * If the channel is registered with a Selector then the close is deferred
+     * until the channel is flushed from all Selectors.
+     */
+    private void implCloseNonBlockingMode() throws IOException {
+        synchronized (stateLock) {
+            assert state < ST_CLOSING;
+            state = ST_CLOSING;
+        }
+        // wait for any accept to complete before trying to close
+        acceptLock.lock();
+        acceptLock.unlock();
+        synchronized (stateLock) {
+            if (state == ST_CLOSING) {
+                tryClose();
+            }
         }
     }
 
     /**
      * 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
-        stateLock.lock();
-        try {
-            assert state < ST_CLOSING;
-            state = ST_CLOSING;
-            blocking = isBlocking();
-        } finally {
-            stateLock.unlock();
+        if (isBlocking()) {
+            implCloseBlockingMode();
+        } else {
+            implCloseNonBlockingMode();
         }
-
-        // wait for any outstanding accept to complete
-        if (blocking) {
-            stateLock.lock();
-            try {
-                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 {
-                            stateCondition.await();
-                        } catch (InterruptedException e) {
-                            interrupted = true;
-                        }
-                    }
-                }
-            } finally {
-                stateLock.unlock();
-            }
-        } else {
-            // non-blocking mode: wait for accept to complete
-            acceptLock.lock();
-            acceptLock.unlock();
-        }
-
-        // set state to ST_KILLPENDING
-        stateLock.lock();
-        try {
-            assert state == ST_CLOSING;
-            state = ST_KILLPENDING;
-        } finally {
-            stateLock.unlock();
-        }
-
-        // close socket if not registered with Selector
-        if (!isRegistered())
-            kill();
-
-        // restore interrupt status
-        if (interrupted)
-            Thread.currentThread().interrupt();
     }
 
     @Override
-    public void kill() throws IOException {
-        stateLock.lock();
-        try {
-            if (state == ST_KILLPENDING) {
-                state = ST_KILLED;
-                nd.close(fd);
+    public void kill() {
+        synchronized (stateLock) {
+            if (state == ST_CLOSING) {
+                tryFinishClose();
             }
-        } finally {
-            stateLock.unlock();
         }
     }
 
@@ -507,11 +477,8 @@
      * Returns true if channel's socket is bound
      */
     boolean isBound() {
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             return localAddress != null;
-        } finally {
-            stateLock.unlock();
         }
     }
 
@@ -519,11 +486,8 @@
      * Returns the local address, or null if not bound
      */
     InetSocketAddress localAddress() {
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             return localAddress;
-        } finally {
-            stateLock.unlock();
         }
     }
 
@@ -589,16 +553,13 @@
         if (!isOpen()) {
             sb.append("closed");
         } else {
-            stateLock.lock();
-            try {
+            synchronized (stateLock) {
                 InetSocketAddress addr = localAddress;
                 if (addr == null) {
                     sb.append("unbound");
                 } else {
                     sb.append(Net.getRevealedLocalAddressAsString(addr));
                 }
-            } finally {
-                stateLock.unlock();
             }
         }
         sb.append(']');
--- a/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java	Tue May 07 18:24:36 2019 -0400
+++ b/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java	Wed May 08 08:15:04 2019 +0100
@@ -53,7 +53,6 @@
 import java.util.HashSet;
 import java.util.Objects;
 import java.util.Set;
-import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.ReentrantLock;
 
 import sun.net.ConnectionResetException;
@@ -84,8 +83,7 @@
 
     // 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 ReentrantLock stateLock = new ReentrantLock();
-    private final Condition stateCondition = stateLock.newCondition();
+    private final Object stateLock = new Object();
 
     // Input/Output closed
     private volatile boolean isInputClosed;
@@ -104,8 +102,7 @@
     private static final int ST_CONNECTIONPENDING = 1;
     private static final int ST_CONNECTED = 2;
     private static final int ST_CLOSING = 3;
-    private static final int ST_KILLPENDING = 4;
-    private static final int ST_KILLED = 5;
+    private static final int ST_CLOSED = 4;
     private volatile int state;  // need stateLock to change
 
     // IDs of native threads doing reads and writes, for signalling
@@ -137,11 +134,8 @@
         this.fd = fd;
         this.fdVal = IOUtil.fdVal(fd);
         if (bound) {
-            stateLock.lock();
-            try {
+            synchronized (stateLock) {
                 this.localAddress = Net.localAddress(fd);
-            } finally {
-                stateLock.unlock();
             }
         }
     }
@@ -154,13 +148,10 @@
         super(sp);
         this.fd = fd;
         this.fdVal = IOUtil.fdVal(fd);
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             this.localAddress = Net.localAddress(fd);
             this.remoteAddress = isa;
             this.state = ST_CONNECTED;
-        } finally {
-            stateLock.unlock();
         }
     }
 
@@ -197,35 +188,26 @@
 
     @Override
     public Socket socket() {
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             if (socket == null)
                 socket = SocketAdaptor.create(this);
             return socket;
-        } finally {
-            stateLock.unlock();
         }
     }
 
     @Override
     public SocketAddress getLocalAddress() throws IOException {
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             ensureOpen();
             return Net.getRevealedLocalAddress(localAddress);
-        } finally {
-            stateLock.unlock();
         }
     }
 
     @Override
     public SocketAddress getRemoteAddress() throws IOException {
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             ensureOpen();
             return remoteAddress;
-        } finally {
-            stateLock.unlock();
         }
     }
 
@@ -237,8 +219,7 @@
         if (!supportedOptions().contains(name))
             throw new UnsupportedOperationException("'" + name + "' not supported");
 
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             ensureOpen();
 
             if (name == StandardSocketOptions.IP_TOS) {
@@ -257,8 +238,6 @@
             // no options that require special handling
             Net.setSocketOption(fd, name, value);
             return this;
-        } finally {
-            stateLock.unlock();
         }
     }
 
@@ -271,8 +250,7 @@
         if (!supportedOptions().contains(name))
             throw new UnsupportedOperationException("'" + name + "' not supported");
 
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             ensureOpen();
 
             if (name == StandardSocketOptions.SO_REUSEADDR && Net.useExclusiveBind()) {
@@ -289,8 +267,6 @@
 
             // no options that require special handling
             return (T) Net.getSocketOption(fd, name);
-        } finally {
-            stateLock.unlock();
         }
     }
 
@@ -332,13 +308,10 @@
             // set hook for Thread.interrupt
             begin();
 
-            stateLock.lock();
-            try {
+            synchronized (stateLock) {
                 ensureOpenAndConnected();
                 // record thread so it can be signalled if needed
                 readerThread = NativeThread.current();
-            } finally {
-                stateLock.unlock();
             }
         } else {
             ensureOpenAndConnected();
@@ -355,15 +328,11 @@
         throws AsynchronousCloseException
     {
         if (blocking) {
-            stateLock.lock();
-            try {
+            synchronized (stateLock) {
                 readerThread = 0;
-                // notify any thread waiting in implCloseSelectableChannel
                 if (state == ST_CLOSING) {
-                    stateCondition.signalAll();
+                    tryFinishClose();
                 }
-            } finally {
-                stateLock.unlock();
             }
             // remove hook for Thread.interrupt
             end(completed);
@@ -467,15 +436,12 @@
             // set hook for Thread.interrupt
             begin();
 
-            stateLock.lock();
-            try {
+            synchronized (stateLock) {
                 ensureOpenAndConnected();
                 if (isOutputClosed)
                     throw new ClosedChannelException();
                 // record thread so it can be signalled if needed
                 writerThread = NativeThread.current();
-            } finally {
-                stateLock.unlock();
             }
         } else {
             ensureOpenAndConnected();
@@ -492,15 +458,11 @@
         throws AsynchronousCloseException
     {
         if (blocking) {
-            stateLock.lock();
-            try {
+            synchronized (stateLock) {
                 writerThread = 0;
-                // notify any thread waiting in implCloseSelectableChannel
                 if (state == ST_CLOSING) {
-                    stateCondition.signalAll();
+                    tryFinishClose();
                 }
-            } finally {
-                stateLock.unlock();
             }
             // remove hook for Thread.interrupt
             end(completed);
@@ -613,12 +575,9 @@
      */
     private void lockedConfigureBlocking(boolean block) throws IOException {
         assert readLock.isHeldByCurrentThread() || writeLock.isHeldByCurrentThread();
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             ensureOpen();
             IOUtil.configureBlocking(fd, block);
-        } finally {
-            stateLock.unlock();
         }
     }
 
@@ -626,11 +585,8 @@
      * Returns the local address, or null if not bound
      */
     InetSocketAddress localAddress() {
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             return localAddress;
-        } finally {
-            stateLock.unlock();
         }
     }
 
@@ -638,11 +594,8 @@
      * Returns the remote address, or null if not connected
      */
     InetSocketAddress remoteAddress() {
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             return remoteAddress;
-        } finally {
-            stateLock.unlock();
         }
     }
 
@@ -652,8 +605,7 @@
         try {
             writeLock.lock();
             try {
-                stateLock.lock();
-                try {
+                synchronized (stateLock) {
                     ensureOpen();
                     if (state == ST_CONNECTIONPENDING)
                         throw new ConnectionPendingException();
@@ -668,8 +620,6 @@
                     NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort());
                     Net.bind(fd, isa.getAddress(), isa.getPort());
                     localAddress = Net.localAddress(fd);
-                } finally {
-                    stateLock.unlock();
                 }
             } finally {
                 writeLock.unlock();
@@ -706,8 +656,7 @@
             // set hook for Thread.interrupt
             begin();
         }
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             ensureOpen();
             int state = this.state;
             if (state == ST_CONNECTED)
@@ -725,8 +674,6 @@
                 // record thread so it can be signalled if needed
                 readerThread = NativeThread.current();
             }
-        } finally {
-            stateLock.unlock();
         }
     }
 
@@ -743,14 +690,11 @@
         endRead(blocking, completed);
 
         if (completed) {
-            stateLock.lock();
-            try {
+            synchronized (stateLock) {
                 if (state == ST_CONNECTIONPENDING) {
                     localAddress = Net.localAddress(fd);
                     state = ST_CONNECTED;
                 }
-            } finally {
-                stateLock.unlock();
             }
         }
     }
@@ -823,8 +767,7 @@
             // set hook for Thread.interrupt
             begin();
         }
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             ensureOpen();
             if (state != ST_CONNECTIONPENDING)
                 throw new NoConnectionPendingException();
@@ -832,8 +775,6 @@
                 // record thread so it can be signalled if needed
                 readerThread = NativeThread.current();
             }
-        } finally {
-            stateLock.unlock();
         }
     }
 
@@ -850,14 +791,11 @@
         endRead(blocking, completed);
 
         if (completed) {
-            stateLock.lock();
-            try {
+            synchronized (stateLock) {
                 if (state == ST_CONNECTIONPENDING) {
                     localAddress = Net.localAddress(fd);
                     state = ST_CONNECTED;
                 }
-            } finally {
-                stateLock.unlock();
             }
         }
     }
@@ -904,90 +842,89 @@
     }
 
     /**
-     * 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.
-     *
-     * If the socket is connected then it is shutdown by this method. The
-     * shutdown ensures that the peer reads EOF for the case that the socket is
-     * not pre-closed or closed by this method.
-     *
-     * 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.
+     * Closes the socket if there are no I/O operations in progress and the
+     * channel is not registered with a Selector.
      */
-    @Override
-    protected void implCloseSelectableChannel() throws IOException {
-        assert !isOpen();
+    private boolean tryClose() throws IOException {
+        assert Thread.holdsLock(stateLock) && state == ST_CLOSING;
+        if ((readerThread == 0) && (writerThread == 0) && !isRegistered()) {
+            state = ST_CLOSED;
+            nd.close(fd);
+            return true;
+        } else {
+            return false;
+        }
+    }
 
-        boolean blocking;
-        boolean connected;
-        boolean interrupted = false;
+    /**
+     * Invokes tryClose to attempt to close the socket.
+     *
+     * This method is used for deferred closing by I/O and Selector operations.
+     */
+    private void tryFinishClose() {
+        try {
+            tryClose();
+        } catch (IOException ignore) { }
+    }
 
-        // set state to ST_CLOSING
-        stateLock.lock();
-        try {
+    /**
+     * Closes this channel when configured in blocking mode.
+     *
+     * If there is an I/O operation in progress then the socket is pre-closed
+     * and the I/O threads signalled, in which case the final close is deferred
+     * until all I/O operations complete.
+     *
+     * 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.
+     */
+    private void implCloseBlockingMode() throws IOException {
+        synchronized (stateLock) {
             assert state < ST_CLOSING;
-            blocking = isBlocking();
-            connected = (state == ST_CONNECTED);
             state = ST_CLOSING;
-        } finally {
-            stateLock.unlock();
-        }
-
-        // wait for any outstanding I/O operations to complete
-        if (blocking) {
-            stateLock.lock();
-            try {
-                assert state == ST_CLOSING;
+            if (!tryClose()) {
                 long reader = readerThread;
                 long writer = writerThread;
                 if (reader != 0 || writer != 0) {
                     nd.preClose(fd);
-                    connected = false; // fd is no longer connected socket
-
                     if (reader != 0)
                         NativeThread.signal(reader);
                     if (writer != 0)
                         NativeThread.signal(writer);
-
-                    // wait for blocking I/O operations to end
-                    while (readerThread != 0 || writerThread != 0) {
-                        try {
-                            stateCondition.await();
-                        } catch (InterruptedException e) {
-                            interrupted = true;
-                        }
-                    }
                 }
-            } finally {
-                stateLock.unlock();
-            }
-        } else {
-            // non-blocking mode: wait for read/write to complete
-            readLock.lock();
-            try {
-                writeLock.lock();
-                writeLock.unlock();
-            } finally {
-                readLock.unlock();
             }
         }
+    }
 
-        // set state to ST_KILLPENDING
-        stateLock.lock();
-        try {
-            assert state == ST_CLOSING;
-            // if connected and the channel is registered with a Selector then
-            // shutdown the output if possible so that the peer reads EOF. If
-            // SO_LINGER is enabled and set to a non-zero value then it needs to
-            // be disabled so that the Selector does not wait when it closes
-            // the socket.
-            if (connected && isRegistered()) {
+    /**
+     * Closes this channel when configured in non-blocking mode.
+     *
+     * If the channel is registered with a Selector then the close is deferred
+     * until the channel is flushed from all Selectors.
+     *
+     * If the socket is connected and the channel is registered with a Selector
+     * then the socket is shutdown for writing so that the peer reads EOF. In
+     * addition, if SO_LINGER is set to a non-zero value then it is disabled so
+     * that the deferred close does not wait.
+     */
+    private void implCloseNonBlockingMode() throws IOException {
+        boolean connected;
+        synchronized (stateLock) {
+            assert state < ST_CLOSING;
+            connected = (state == ST_CONNECTED);
+            state = ST_CLOSING;
+        }
+
+        // wait for any read/write operations to complete
+        readLock.lock();
+        readLock.unlock();
+        writeLock.lock();
+        writeLock.unlock();
+
+        // if the socket cannot be closed because it's registered with a Selector
+        // then shutdown the socket for writing.
+        synchronized (stateLock) {
+            if (state == ST_CLOSING && !tryClose() && connected && isRegistered()) {
                 try {
                     SocketOption<Integer> opt = StandardSocketOptions.SO_LINGER;
                     int interval = (int) Net.getSocketOption(fd, Net.UNSPEC, opt);
@@ -1000,37 +937,34 @@
                     }
                 } catch (IOException ignore) { }
             }
-            state = ST_KILLPENDING;
-        } finally {
-            stateLock.unlock();
         }
+    }
 
-        // close socket if not registered with Selector
-        if (!isRegistered())
-            kill();
-
-        // restore interrupt status
-        if (interrupted)
-            Thread.currentThread().interrupt();
+    /**
+     * Invoked by implCloseChannel to close the channel.
+     */
+    @Override
+    protected void implCloseSelectableChannel() throws IOException {
+        assert !isOpen();
+        if (isBlocking()) {
+            implCloseBlockingMode();
+        } else {
+            implCloseNonBlockingMode();
+        }
     }
 
     @Override
-    public void kill() throws IOException {
-        stateLock.lock();
-        try {
-            if (state == ST_KILLPENDING) {
-                state = ST_KILLED;
-                nd.close(fd);
+    public void kill() {
+        synchronized (stateLock) {
+            if (state == ST_CLOSING) {
+                tryFinishClose();
             }
-        } finally {
-            stateLock.unlock();
         }
     }
 
     @Override
     public SocketChannel shutdownInput() throws IOException {
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             ensureOpen();
             if (!isConnected())
                 throw new NotYetConnectedException();
@@ -1042,15 +976,12 @@
                 isInputClosed = true;
             }
             return this;
-        } finally {
-            stateLock.unlock();
         }
     }
 
     @Override
     public SocketChannel shutdownOutput() throws IOException {
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             ensureOpen();
             if (!isConnected())
                 throw new NotYetConnectedException();
@@ -1062,8 +993,6 @@
                 isOutputClosed = true;
             }
             return this;
-        } finally {
-            stateLock.unlock();
         }
     }
 
@@ -1300,16 +1229,13 @@
      * Return the number of bytes in the socket input buffer.
      */
     int available() throws IOException {
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             ensureOpenAndConnected();
             if (isInputClosed) {
                 return 0;
             } else {
                 return Net.available(fd);
             }
-        } finally {
-            stateLock.unlock();
         }
     }
 
@@ -1389,8 +1315,7 @@
         if (!isOpen())
             sb.append("closed");
         else {
-            stateLock.lock();
-            try {
+            synchronized (stateLock) {
                 switch (state) {
                 case ST_UNCONNECTED:
                     sb.append("unconnected");
@@ -1415,8 +1340,6 @@
                     sb.append(" remote=");
                     sb.append(remoteAddress().toString());
                 }
-            } finally {
-                stateLock.unlock();
             }
         }
         sb.append(']');
--- a/src/java.base/unix/classes/sun/nio/ch/SinkChannelImpl.java	Tue May 07 18:24:36 2019 -0400
+++ b/src/java.base/unix/classes/sun/nio/ch/SinkChannelImpl.java	Wed May 08 08:15:04 2019 +0100
@@ -35,7 +35,6 @@
 import java.nio.channels.SelectionKey;
 import java.nio.channels.spi.SelectorProvider;
 import java.util.Objects;
-import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.ReentrantLock;
 
 class SinkChannelImpl
@@ -54,16 +53,14 @@
 
     // 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 ReentrantLock stateLock = new ReentrantLock();
-    private final Condition stateCondition = stateLock.newCondition();
+    private final Object stateLock = new Object();
 
     // -- The following fields are protected by stateLock
 
     // Channel state
     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 static final int ST_CLOSED = 2;
     private int state;
 
     // ID of native thread doing write, for signalling
@@ -87,82 +84,92 @@
     }
 
     /**
+     * Closes the write end of the pipe if there are no write operation in
+     * progress and the channel is not registered with a Selector.
+     */
+    private boolean tryClose() throws IOException {
+        assert Thread.holdsLock(stateLock) && state == ST_CLOSING;
+        if (thread == 0 && !isRegistered()) {
+            state = ST_CLOSED;
+            nd.close(fd);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Invokes tryClose to attempt to close the write end of the pipe.
+     *
+     * This method is used for deferred closing by I/O and Selector operations.
+     */
+    private void tryFinishClose() {
+        try {
+            tryClose();
+        } catch (IOException ignore) { }
+    }
+
+    /**
+     * Closes this channel when configured in blocking mode.
+     *
+     * If there is a write operation in progress then the write-end of the pipe
+     * is pre-closed and the writer is signalled, in which case the final close
+     * is deferred until the writer aborts.
+     */
+    private void implCloseBlockingMode() throws IOException {
+        synchronized (stateLock) {
+            assert state < ST_CLOSING;
+            state = ST_CLOSING;
+            if (!tryClose()) {
+                long th = thread;
+                if (th != 0) {
+                    nd.preClose(fd);
+                    NativeThread.signal(th);
+                }
+            }
+        }
+    }
+
+    /**
+     * Closes this channel when configured in non-blocking mode.
+     *
+     * If the channel is registered with a Selector then the close is deferred
+     * until the channel is flushed from all Selectors.
+     */
+    private void implCloseNonBlockingMode() throws IOException {
+        synchronized (stateLock) {
+            assert state < ST_CLOSING;
+            state = ST_CLOSING;
+        }
+        // wait for any write operation to complete before trying to close
+        writeLock.lock();
+        writeLock.unlock();
+        synchronized (stateLock) {
+            if (state == ST_CLOSING) {
+                tryClose();
+            }
+        }
+    }
+
+    /**
      * Invoked by implCloseChannel to close the channel.
      */
     @Override
     protected void implCloseSelectableChannel() throws IOException {
         assert !isOpen();
-
-        boolean interrupted = false;
-        boolean blocking;
-
-        // set state to ST_CLOSING
-        stateLock.lock();
-        try {
-            assert state < ST_CLOSING;
-            state = ST_CLOSING;
-            blocking = isBlocking();
-        } finally {
-            stateLock.unlock();
+        if (isBlocking()) {
+            implCloseBlockingMode();
+        } else {
+            implCloseNonBlockingMode();
         }
-
-        // wait for any outstanding write to complete
-        if (blocking) {
-            stateLock.lock();
-            try {
-                assert state == ST_CLOSING;
-                long th = thread;
-                if (th != 0) {
-                    nd.preClose(fd);
-                    NativeThread.signal(th);
-
-                    // wait for write operation to end
-                    while (thread != 0) {
-                        try {
-                            stateCondition.await();
-                        } catch (InterruptedException e) {
-                            interrupted = true;
-                        }
-                    }
-                }
-            } finally {
-                stateLock.unlock();
-            }
-        } else {
-            // non-blocking mode: wait for write to complete
-            writeLock.lock();
-            writeLock.unlock();
-        }
-
-        // set state to ST_KILLPENDING
-        stateLock.lock();
-        try {
-            assert state == ST_CLOSING;
-            state = ST_KILLPENDING;
-        } finally {
-            stateLock.unlock();
-        }
-
-        // close socket if not registered with Selector
-        if (!isRegistered())
-            kill();
-
-        // restore interrupt status
-        if (interrupted)
-            Thread.currentThread().interrupt();
     }
 
     @Override
-    public void kill() throws IOException {
-        stateLock.lock();
-        try {
-            assert thread == 0;
-            if (state == ST_KILLPENDING) {
-                state = ST_KILLED;
-                nd.close(fd);
+    public void kill() {
+        synchronized (stateLock) {
+            if (state == ST_CLOSING) {
+                tryFinishClose();
             }
-        } finally {
-            stateLock.unlock();
         }
     }
 
@@ -170,11 +177,10 @@
     protected void implConfigureBlocking(boolean block) throws IOException {
         writeLock.lock();
         try {
-            stateLock.lock();
-            try {
+            synchronized (stateLock) {
+                if (!isOpen())
+                    throw new ClosedChannelException();
                 IOUtil.configureBlocking(fd, block);
-            } finally {
-                stateLock.unlock();
             }
         } finally {
             writeLock.unlock();
@@ -229,14 +235,11 @@
             // set hook for Thread.interrupt
             begin();
         }
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             if (!isOpen())
                 throw new ClosedChannelException();
             if (blocking)
                 thread = NativeThread.current();
-        } finally {
-            stateLock.unlock();
         }
     }
 
@@ -250,15 +253,11 @@
         throws AsynchronousCloseException
     {
         if (blocking) {
-            stateLock.lock();
-            try {
+            synchronized (stateLock) {
                 thread = 0;
-                // notify any thread waiting in implCloseSelectableChannel
                 if (state == ST_CLOSING) {
-                    stateCondition.signalAll();
+                    tryFinishClose();
                 }
-            } finally {
-                stateLock.unlock();
             }
             // remove hook for Thread.interrupt
             end(completed);
--- a/src/java.base/unix/classes/sun/nio/ch/SourceChannelImpl.java	Tue May 07 18:24:36 2019 -0400
+++ b/src/java.base/unix/classes/sun/nio/ch/SourceChannelImpl.java	Wed May 08 08:15:04 2019 +0100
@@ -35,7 +35,6 @@
 import java.nio.channels.SelectionKey;
 import java.nio.channels.spi.SelectorProvider;
 import java.util.Objects;
-import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.ReentrantLock;
 
 class SourceChannelImpl
@@ -54,16 +53,14 @@
 
     // 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 ReentrantLock stateLock = new ReentrantLock();
-    private final Condition stateCondition = stateLock.newCondition();
+    private final Object stateLock = new Object();
 
     // -- The following fields are protected by stateLock
 
     // Channel state
     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 static final int ST_CLOSED = 2;
     private int state;
 
     // ID of native thread doing read, for signalling
@@ -87,82 +84,92 @@
     }
 
     /**
+     * Closes the read end of the pipe if there are no read operation in
+     * progress and the channel is not registered with a Selector.
+     */
+    private boolean tryClose() throws IOException {
+        assert Thread.holdsLock(stateLock) && state == ST_CLOSING;
+        if (thread == 0 && !isRegistered()) {
+            state = ST_CLOSED;
+            nd.close(fd);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Invokes tryClose to attempt to close the read end of the pipe.
+     *
+     * This method is used for deferred closing by I/O and Selector operations.
+     */
+    private void tryFinishClose() {
+        try {
+            tryClose();
+        } catch (IOException ignore) { }
+    }
+
+    /**
+     * Closes this channel when configured in blocking mode.
+     *
+     * If there is a read operation in progress then the read-end of the pipe
+     * is pre-closed and the reader is signalled, in which case the final close
+     * is deferred until the reader aborts.
+     */
+    private void implCloseBlockingMode() throws IOException {
+        synchronized (stateLock) {
+            assert state < ST_CLOSING;
+            state = ST_CLOSING;
+            if (!tryClose()) {
+                long th = thread;
+                if (th != 0) {
+                    nd.preClose(fd);
+                    NativeThread.signal(th);
+                }
+            }
+        }
+    }
+
+    /**
+     * Closes this channel when configured in non-blocking mode.
+     *
+     * If the channel is registered with a Selector then the close is deferred
+     * until the channel is flushed from all Selectors.
+     */
+    private void implCloseNonBlockingMode() throws IOException {
+        synchronized (stateLock) {
+            assert state < ST_CLOSING;
+            state = ST_CLOSING;
+        }
+        // wait for any read operation to complete before trying to close
+        readLock.lock();
+        readLock.unlock();
+        synchronized (stateLock) {
+            if (state == ST_CLOSING) {
+                tryClose();
+            }
+        }
+    }
+
+    /**
      * Invoked by implCloseChannel to close the channel.
      */
     @Override
     protected void implCloseSelectableChannel() throws IOException {
         assert !isOpen();
-
-        boolean interrupted = false;
-        boolean blocking;
-
-        // set state to ST_CLOSING
-        stateLock.lock();
-        try {
-            assert state < ST_CLOSING;
-            state = ST_CLOSING;
-            blocking = isBlocking();
-        } finally {
-            stateLock.unlock();
+        if (isBlocking()) {
+            implCloseBlockingMode();
+        } else {
+            implCloseNonBlockingMode();
         }
-
-        // wait for any outstanding read to complete
-        if (blocking) {
-            stateLock.lock();
-            try {
-                assert state == ST_CLOSING;
-                long th = thread;
-                if (th != 0) {
-                    nd.preClose(fd);
-                    NativeThread.signal(th);
-
-                    // wait for read operation to end
-                    while (thread != 0) {
-                        try {
-                            stateCondition.await();
-                        } catch (InterruptedException e) {
-                            interrupted = true;
-                        }
-                    }
-                }
-            } finally {
-                stateLock.unlock();
+    }
+    @Override
+    public void kill() {
+        synchronized (stateLock) {
+            assert !isOpen();
+            if (state == ST_CLOSING) {
+                tryFinishClose();
             }
-        } else {
-            // non-blocking mode: wait for read to complete
-            readLock.lock();
-            readLock.unlock();
-        }
-
-        // set state to ST_KILLPENDING
-        stateLock.lock();
-        try {
-            assert state == ST_CLOSING;
-            state = ST_KILLPENDING;
-        } finally {
-            stateLock.unlock();
-        }
-
-        // close socket if not registered with Selector
-        if (!isRegistered())
-            kill();
-
-        // restore interrupt status
-        if (interrupted)
-            Thread.currentThread().interrupt();
-    }
-
-    @Override
-    public void kill() throws IOException {
-        stateLock.lock();
-        try {
-            assert thread == 0;
-            if (state == ST_KILLPENDING) {
-                state = ST_KILLED;
-                nd.close(fd);
-            }
-        } finally {
-            stateLock.unlock();
         }
     }
 
@@ -170,11 +177,10 @@
     protected void implConfigureBlocking(boolean block) throws IOException {
         readLock.lock();
         try {
-            stateLock.lock();
-            try {
+            synchronized (stateLock) {
+                if (!isOpen())
+                    throw new ClosedChannelException();
                 IOUtil.configureBlocking(fd, block);
-            } finally {
-                stateLock.unlock();
             }
         } finally {
             readLock.unlock();
@@ -229,14 +235,11 @@
             // set hook for Thread.interrupt
             begin();
         }
-        stateLock.lock();
-        try {
+        synchronized (stateLock) {
             if (!isOpen())
                 throw new ClosedChannelException();
             if (blocking)
                 thread = NativeThread.current();
-        } finally {
-            stateLock.unlock();
         }
     }
 
@@ -250,15 +253,11 @@
         throws AsynchronousCloseException
     {
         if (blocking) {
-            stateLock.lock();
-            try {
+            synchronized (stateLock) {
                 thread = 0;
-                // notify any thread waiting in implCloseSelectableChannel
                 if (state == ST_CLOSING) {
-                    stateCondition.signalAll();
+                    tryFinishClose();
                 }
-            } finally {
-                stateLock.unlock();
             }
             // remove hook for Thread.interrupt
             end(completed);