8164147: Improve streaming socket output
Reviewed-by: chegar, igerasim
This commit is contained in:
parent
6e132741b6
commit
3463ee94d8
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1995, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -155,11 +155,12 @@ class SocketInputStream extends FileInputStream
|
|||||||
}
|
}
|
||||||
|
|
||||||
// bounds check
|
// bounds check
|
||||||
if (length <= 0 || off < 0 || off + length > b.length) {
|
if (length <= 0 || off < 0 || length > b.length - off) {
|
||||||
if (length == 0) {
|
if (length == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
throw new ArrayIndexOutOfBoundsException();
|
throw new ArrayIndexOutOfBoundsException("length == " + length
|
||||||
|
+ " off == " + off + " buffer length == " + b.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean gotReset = false;
|
boolean gotReset = false;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1995, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -97,11 +97,13 @@ class SocketOutputStream extends FileOutputStream
|
|||||||
*/
|
*/
|
||||||
private void socketWrite(byte b[], int off, int len) throws IOException {
|
private void socketWrite(byte b[], int off, int len) throws IOException {
|
||||||
|
|
||||||
if (len <= 0 || off < 0 || off + len > b.length) {
|
|
||||||
|
if (len <= 0 || off < 0 || len > b.length - off) {
|
||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new ArrayIndexOutOfBoundsException();
|
throw new ArrayIndexOutOfBoundsException("len == " + len
|
||||||
|
+ " off == " + off + " buffer length == " + b.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
FileDescriptor fd = impl.acquireFD();
|
FileDescriptor fd = impl.acquireFD();
|
||||||
|
@ -98,27 +98,31 @@ Java_java_net_SocketOutputStream_socketWrite0(JNIEnv *env, jobject this,
|
|||||||
int llen = chunkLen;
|
int llen = chunkLen;
|
||||||
(*env)->GetByteArrayRegion(env, data, off, chunkLen, (jbyte *)bufP);
|
(*env)->GetByteArrayRegion(env, data, off, chunkLen, (jbyte *)bufP);
|
||||||
|
|
||||||
while(llen > 0) {
|
if ((*env)->ExceptionCheck(env)) {
|
||||||
int n = NET_Send(fd, bufP + loff, llen, 0);
|
break;
|
||||||
if (n > 0) {
|
} else {
|
||||||
llen -= n;
|
while(llen > 0) {
|
||||||
loff += n;
|
int n = NET_Send(fd, bufP + loff, llen, 0);
|
||||||
continue;
|
if (n > 0) {
|
||||||
|
llen -= n;
|
||||||
|
loff += n;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (errno == ECONNRESET) {
|
||||||
|
JNU_ThrowByName(env, "sun/net/ConnectionResetException",
|
||||||
|
"Connection reset");
|
||||||
|
} else {
|
||||||
|
JNU_ThrowByNameWithMessageAndLastError
|
||||||
|
(env, "java/net/SocketException", "Write failed");
|
||||||
|
}
|
||||||
|
if (bufP != BUF) {
|
||||||
|
free(bufP);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (errno == ECONNRESET) {
|
len -= chunkLen;
|
||||||
JNU_ThrowByName(env, "sun/net/ConnectionResetException",
|
off += chunkLen;
|
||||||
"Connection reset");
|
|
||||||
} else {
|
|
||||||
JNU_ThrowByNameWithMessageAndLastError
|
|
||||||
(env, "java/net/SocketException", "Write failed");
|
|
||||||
}
|
|
||||||
if (bufP != BUF) {
|
|
||||||
free(bufP);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
len -= chunkLen;
|
|
||||||
off += chunkLen;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bufP != BUF) {
|
if (bufP != BUF) {
|
||||||
|
@ -92,66 +92,69 @@ Java_java_net_SocketOutputStream_socketWrite0(JNIEnv *env, jobject this,
|
|||||||
int retry = 0;
|
int retry = 0;
|
||||||
|
|
||||||
(*env)->GetByteArrayRegion(env, data, off, chunkLen, (jbyte *)bufP);
|
(*env)->GetByteArrayRegion(env, data, off, chunkLen, (jbyte *)bufP);
|
||||||
|
if ((*env)->ExceptionCheck(env)) {
|
||||||
while(llen > 0) {
|
break;
|
||||||
int n = send(fd, bufP + loff, llen, 0);
|
} else {
|
||||||
if (n > 0) {
|
while(llen > 0) {
|
||||||
llen -= n;
|
int n = send(fd, bufP + loff, llen, 0);
|
||||||
loff += n;
|
if (n > 0) {
|
||||||
continue;
|
llen -= n;
|
||||||
}
|
loff += n;
|
||||||
|
|
||||||
/*
|
|
||||||
* Due to a bug in Windows Sockets (observed on NT and Windows
|
|
||||||
* 2000) it may be necessary to retry the send. The issue is that
|
|
||||||
* on blocking sockets send/WSASend is supposed to block if there
|
|
||||||
* is insufficient buffer space available. If there are a large
|
|
||||||
* number of threads blocked on write due to congestion then it's
|
|
||||||
* possile to hit the NT/2000 bug whereby send returns WSAENOBUFS.
|
|
||||||
* The workaround we use is to retry the send. If we have a
|
|
||||||
* large buffer to send (>2k) then we retry with a maximum of
|
|
||||||
* 2k buffer. If we hit the issue with <=2k buffer then we backoff
|
|
||||||
* for 1 second and retry again. We repeat this up to a reasonable
|
|
||||||
* limit before bailing out and throwing an exception. In load
|
|
||||||
* conditions we've observed that the send will succeed after 2-3
|
|
||||||
* attempts but this depends on network buffers associated with
|
|
||||||
* other sockets draining.
|
|
||||||
*/
|
|
||||||
if (WSAGetLastError() == WSAENOBUFS) {
|
|
||||||
if (llen > MAX_BUFFER_LEN) {
|
|
||||||
buflen = MAX_BUFFER_LEN;
|
|
||||||
chunkLen = MAX_BUFFER_LEN;
|
|
||||||
llen = MAX_BUFFER_LEN;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (retry >= 30) {
|
|
||||||
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
|
|
||||||
"No buffer space available - exhausted attempts to queue buffer");
|
|
||||||
if (bufP != BUF) {
|
|
||||||
free(bufP);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Sleep(1000);
|
|
||||||
retry++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Send failed - can be caused by close or write error.
|
* Due to a bug in Windows Sockets (observed on NT and Windows
|
||||||
*/
|
* 2000) it may be necessary to retry the send. The issue is that
|
||||||
if (WSAGetLastError() == WSAENOTSOCK) {
|
* on blocking sockets send/WSASend is supposed to block if there
|
||||||
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
|
* is insufficient buffer space available. If there are a large
|
||||||
} else {
|
* number of threads blocked on write due to congestion then it's
|
||||||
NET_ThrowCurrent(env, "socket write error");
|
* possile to hit the NT/2000 bug whereby send returns WSAENOBUFS.
|
||||||
|
* The workaround we use is to retry the send. If we have a
|
||||||
|
* large buffer to send (>2k) then we retry with a maximum of
|
||||||
|
* 2k buffer. If we hit the issue with <=2k buffer then we backoff
|
||||||
|
* for 1 second and retry again. We repeat this up to a reasonable
|
||||||
|
* limit before bailing out and throwing an exception. In load
|
||||||
|
* conditions we've observed that the send will succeed after 2-3
|
||||||
|
* attempts but this depends on network buffers associated with
|
||||||
|
* other sockets draining.
|
||||||
|
*/
|
||||||
|
if (WSAGetLastError() == WSAENOBUFS) {
|
||||||
|
if (llen > MAX_BUFFER_LEN) {
|
||||||
|
buflen = MAX_BUFFER_LEN;
|
||||||
|
chunkLen = MAX_BUFFER_LEN;
|
||||||
|
llen = MAX_BUFFER_LEN;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (retry >= 30) {
|
||||||
|
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
|
||||||
|
"No buffer space available - exhausted attempts to queue buffer");
|
||||||
|
if (bufP != BUF) {
|
||||||
|
free(bufP);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Sleep(1000);
|
||||||
|
retry++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send failed - can be caused by close or write error.
|
||||||
|
*/
|
||||||
|
if (WSAGetLastError() == WSAENOTSOCK) {
|
||||||
|
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
|
||||||
|
} else {
|
||||||
|
NET_ThrowCurrent(env, "socket write error");
|
||||||
|
}
|
||||||
|
if (bufP != BUF) {
|
||||||
|
free(bufP);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (bufP != BUF) {
|
len -= chunkLen;
|
||||||
free(bufP);
|
off += chunkLen;
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
len -= chunkLen;
|
|
||||||
off += chunkLen;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bufP != BUF) {
|
if (bufP != BUF) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user