--- a/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java Sun Mar 31 12:11:12 2019 +0100
+++ b/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java Tue Apr 02 14:36:17 2019 +0100
@@ -194,13 +194,11 @@
}
/**
- * Ensures that the socket is configured non-blocking when a timeout is specified.
+ * Configures the socket to be non-blocking (if not already non-blocking)
* @throws IOException if there is an I/O error changing the blocking mode
*/
- private void configureNonBlockingIfNeeded(FileDescriptor fd, int timeout)
- throws IOException
- {
- if (timeout > 0 && !nonBlocking) {
+ private void configureNonBlocking(FileDescriptor fd) throws IOException {
+ if (!nonBlocking) {
assert readLock.isHeldByCurrentThread() || writeLock.isHeldByCurrentThread();
IOUtil.configureBlocking(fd, false);
nonBlocking = true;
@@ -257,11 +255,10 @@
* Reads bytes from the socket into the given byte array with a timeout.
* @throws SocketTimeoutException if the read timeout elapses
*/
- private int timedRead(FileDescriptor fd, byte[] b, int off, int len, int millis)
+ private int timedRead(FileDescriptor fd, byte[] b, int off, int len, long nanos)
throws IOException
{
assert nonBlocking;
- long nanos = NANOSECONDS.convert(millis, TimeUnit.MILLISECONDS);
long remainingNanos = nanos;
long startNanos = System.nanoTime();
int n;
@@ -293,12 +290,14 @@
if (isInputClosed)
return -1;
int timeout = this.timeout;
- configureNonBlockingIfNeeded(fd, timeout);
+ if (timeout > 0)
+ configureNonBlocking(fd);
n = tryRead(fd, b, off, len);
if (IOStatus.okayToRetry(n) && isOpen()) {
if (timeout > 0) {
// read with timeout
- n = timedRead(fd, b, off, len, timeout);
+ long nanos = NANOSECONDS.convert(timeout, MILLISECONDS);
+ n = timedRead(fd, b, off, len, nanos);
} else {
// read, no timeout
do {
@@ -526,8 +525,7 @@
* Waits for a connection attempt to finish with a timeout
* @throws SocketTimeoutException if the connect timeout elapses
*/
- private void timedFinishConnect(FileDescriptor fd, int millis) throws IOException {
- long nanos = NANOSECONDS.convert(millis, TimeUnit.MILLISECONDS);
+ private void timedFinishConnect(FileDescriptor fd, long nanos) throws IOException {
long remainingNanos = nanos;
long startNanos = System.nanoTime();
boolean polled;
@@ -571,7 +569,8 @@
boolean connected = false;
FileDescriptor fd = beginConnect(address, port);
try {
- configureNonBlockingIfNeeded(fd, millis);
+ if (millis > 0)
+ configureNonBlocking(fd);
int n = Net.connect(fd, address, port);
if (isOpen()) {
if (n > 0) {
@@ -581,7 +580,8 @@
// not established or interrupted
if (millis > 0) {
// finish connect with timeout
- timedFinishConnect(fd, millis);
+ long nanos = NANOSECONDS.convert(millis, MILLISECONDS);
+ timedFinishConnect(fd, nanos);
} else {
// finish connect, no timeout
boolean polled;
@@ -679,11 +679,10 @@
private int timedAccept(FileDescriptor fd,
FileDescriptor newfd,
InetSocketAddress[] isaa,
- int millis)
+ long nanos)
throws IOException
{
assert nonBlocking;
- long nanos = NANOSECONDS.convert(millis, TimeUnit.MILLISECONDS);
long remainingNanos = nanos;
long startNanos = System.nanoTime();
int n;
@@ -706,23 +705,36 @@
*/
@Override
protected void accept(SocketImpl si) throws IOException {
- // accept a connection
FileDescriptor newfd = new FileDescriptor();
InetSocketAddress[] isaa = new InetSocketAddress[1];
+ // acquire the lock, adjusting the timeout for cases where several
+ // threads are accepting connections and there is a timeout set
ReentrantLock acceptLock = readLock;
- acceptLock.lock();
+ int timeout = this.timeout;
+ long remainingNanos = 0;
+ if (timeout > 0) {
+ remainingNanos = tryLock(acceptLock, timeout, MILLISECONDS);
+ if (remainingNanos <= 0) {
+ assert !acceptLock.isHeldByCurrentThread();
+ throw new SocketTimeoutException("Accept timed out");
+ }
+ } else {
+ acceptLock.lock();
+ }
+
+ // accept a connection
try {
int n = 0;
FileDescriptor fd = beginAccept();
try {
- int timeout = this.timeout;
- configureNonBlockingIfNeeded(fd, timeout);
+ if (remainingNanos > 0)
+ configureNonBlocking(fd);
n = Net.accept(fd, newfd, isaa);
if (IOStatus.okayToRetry(n) && isOpen()) {
- if (timeout > 0) {
+ if (remainingNanos > 0) {
// accept with timeout
- n = timedAccept(fd, newfd, isaa, timeout);
+ n = timedAccept(fd, newfd, isaa, remainingNanos);
} else {
// accept, no timeout
do {
@@ -1219,6 +1231,33 @@
}
/**
+ * Attempts to acquire the given lock within the given waiting time.
+ * @return the remaining time in nanoseconds when the lock is acquired, zero
+ * or less if the lock was not acquired before the timeout expired
+ */
+ private static long tryLock(ReentrantLock lock, long timeout, TimeUnit unit) {
+ assert timeout > 0;
+ boolean interrupted = false;
+ long nanos = NANOSECONDS.convert(timeout, unit);
+ long remainingNanos = nanos;
+ long startNanos = System.nanoTime();
+ boolean acquired = false;
+ while (!acquired && (remainingNanos > 0)) {
+ try {
+ acquired = lock.tryLock(remainingNanos, NANOSECONDS);
+ } catch (InterruptedException e) {
+ interrupted = true;
+ }
+ remainingNanos = nanos - (System.nanoTime() - startNanos);
+ }
+ if (acquired && remainingNanos <= 0L)
+ lock.unlock(); // release lock if timeout has expired
+ if (interrupted)
+ Thread.currentThread().interrupt();
+ return remainingNanos;
+ }
+
+ /**
* Returns the socket protocol family.
*/
private static ProtocolFamily family() {