--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/unix/classes/sun/nio/ch/UnixDomainSocketChannelImpl.java Tue Sep 24 16:19:11 2019 +0100
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.nio.ch;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.AsynchronousCloseException;
+import java.nio.channels.ByteChannel;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.NotYetConnectedException;
+import java.nio.channels.spi.AbstractInterruptibleChannel;
+import java.util.Objects;
+import java.util.concurrent.locks.ReentrantLock;
+
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
+class UnixDomainSocketChannelImpl
+ extends AbstractInterruptibleChannel
+ implements ByteChannel
+{
+ // Used to make native read and write calls
+ private static final NativeDispatcher nd = new SocketDispatcher();
+
+ // Our file descriptor object
+ private final FileDescriptor fd;
+ // Lock held by current reading or connecting thread
+ private final ReentrantLock readLock = new ReentrantLock();
+
+ // Lock held by current writing or connecting thread
+ private final ReentrantLock writeLock = new ReentrantLock();
+
+ // Lock for managing close state
+ private final Object stateLock = new Object();
+
+ // Channel state
+ private static final int ST_INUSE = 0;
+ private static final int ST_CLOSING = 1;
+ private static final int ST_CLOSED = 2;
+ private int state;
+
+ // IDs of native threads doing reads and writes, for signalling
+ private long readerThread;
+ private long writerThread;
+
+ UnixDomainSocketChannelImpl(FileDescriptor fd)
+ throws IOException
+ {
+ this.fd = fd;
+ }
+
+ /**
+ * Checks that the channel is open.
+ *
+ * @throws ClosedChannelException if channel is closed (or closing)
+ */
+ private void ensureOpen() throws ClosedChannelException {
+ if (!isOpen())
+ throw new ClosedChannelException();
+ }
+
+ /**
+ * Closes the socket if there are no I/O operations in progress
+ */
+ private boolean tryClose() throws IOException {
+ assert Thread.holdsLock(stateLock) && state == ST_CLOSING;
+ if (readerThread == 0 && writerThread == 0) {
+ state = ST_CLOSED;
+ nd.close(fd);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Complete closure of pre-closed socket (release the file descriptor)
+ */
+ private void tryFinishClose() {
+ try {
+ tryClose();
+ } catch (IOException ignore) { }
+ }
+
+ /**
+ * Marks the beginning of a read operation
+ *
+ * @throws ClosedChannelException if the channel is closed
+ * @throws NotYetConnectedException if the channel is not yet connected
+ */
+ private void beginRead() throws ClosedChannelException {
+ // set hook for Thread.interrupt
+ begin();
+ synchronized (stateLock) {
+ ensureOpen();
+ readerThread = NativeThread.current();
+ }
+ }
+
+ /**
+ * Marks the end of a read operation that may have blocked.
+ *
+ * @throws AsynchronousCloseException if the channel was closed due to this
+ * thread being interrupted on a blocking read operation.
+ */
+ private void endRead(boolean completed)
+ throws AsynchronousCloseException
+ {
+ synchronized (stateLock) {
+ readerThread = 0;
+ if (state == ST_CLOSING) {
+ tryFinishClose();
+ }
+ }
+ end(completed);
+ }
+
+ @Override
+ public int read(ByteBuffer buf) throws IOException {
+ Objects.requireNonNull(buf);
+
+ readLock.lock();
+ try {
+ int n = 0;
+ try {
+ beginRead();
+ n = IOUtil.read(fd, buf, -1, nd);
+ while (IOStatus.okayToRetry(n) && isOpen()) {
+ park(Net.POLLIN, 0L);
+ n = IOUtil.read(fd, buf, -1, nd);
+ }
+ } finally {
+ endRead(n > 0);
+ }
+ return n;
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ /**
+ * Marks the beginning of a write operation that might block.
+ *
+ * @throws ClosedChannelException if the channel is closed
+ * @throws NotYetConnectedException if the channel is not yet connected
+ */
+ private void beginWrite() throws ClosedChannelException {
+ begin();
+ synchronized (stateLock) {
+ // set hook for Thread.interrupt
+ ensureOpen();
+ writerThread = NativeThread.current();
+ }
+ }
+
+ /**
+ * Marks the end of a write operation that may have blocked.
+ *
+ * @throws AsynchronousCloseException if the channel was closed due to this
+ * thread being interrupted on a blocking write operation.
+ */
+ private void endWrite(boolean completed)
+ throws AsynchronousCloseException
+ {
+ synchronized (stateLock) {
+ writerThread = 0;
+ if (state == ST_CLOSING) {
+ tryFinishClose();
+ }
+ }
+ end(completed);
+ }
+
+ void park(int event, long nanos) throws IOException {
+ long millis;
+ if (nanos <= 0) {
+ millis = -1;
+ } else {
+ millis = NANOSECONDS.toMillis(nanos);
+ }
+ Net.poll(fd, event, millis);
+ }
+
+ @Override
+ public int write(ByteBuffer buf) throws IOException {
+ Objects.requireNonNull(buf);
+
+ writeLock.lock();
+ try {
+ int n = 0;
+ try {
+ beginWrite();
+ n = IOUtil.write(fd, buf, -1, nd);
+ while (IOStatus.okayToRetry(n) && isOpen()) {
+ park(Net.POLLOUT, 0L);
+ n = IOUtil.write(fd, buf, -1, nd);
+ }
+ } finally {
+ endWrite(n > 0);
+ }
+ return n;
+ } finally {
+ writeLock.unlock();
+ }
+ }
+
+ /**
+ * Closes this channel
+ *
+ * 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.
+ */
+ @Override
+ protected void implCloseChannel() throws IOException {
+ synchronized (stateLock) {
+ assert state == ST_INUSE;
+ 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);
+ }
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(this.getClass().getSuperclass().getName());
+ sb.append('[');
+ if (!isOpen())
+ sb.append("closed");
+ sb.append(']');
+ return sb.toString();
+ }
+}