EPOLL Cached ECONNREFUSED Exception

Motivation:
ECONNREFUSED can be a common type of exception when attempting to finish the connection process. Generating a new exception each time can be costly and quickly bloat memory usage.

Modifications:
- Expose ECONNREFUSED from JNI and cache this exception in Socket.finishConnect

Result:
ECONNREFUSED during finish connect doesn't create a new exception each time.
This commit is contained in:
Scott Mitchell 2016-06-30 21:54:42 -07:00
parent 44b942e507
commit df033e150c
4 changed files with 45 additions and 14 deletions

View File

@ -100,6 +100,10 @@ static jint netty_unix_errors_errnoEINPROGRESS(JNIEnv* env, jclass clazz) {
return EINPROGRESS; return EINPROGRESS;
} }
static jint netty_unix_errors_errorECONNREFUSED(JNIEnv* env, jclass clazz) {
return ECONNREFUSED;
}
static jstring netty_unix_errors_strError(JNIEnv* env, jclass clazz, jint error) { static jstring netty_unix_errors_strError(JNIEnv* env, jclass clazz, jint error) {
return (*env)->NewStringUTF(env, strerror(error)); return (*env)->NewStringUTF(env, strerror(error));
} }
@ -114,6 +118,7 @@ static const JNINativeMethod statically_referenced_fixed_method_table[] = {
{ "errnoEAGAIN", "()I", (void *) netty_unix_errors_errnoEAGAIN }, { "errnoEAGAIN", "()I", (void *) netty_unix_errors_errnoEAGAIN },
{ "errnoEWOULDBLOCK", "()I", (void *) netty_unix_errors_errnoEWOULDBLOCK }, { "errnoEWOULDBLOCK", "()I", (void *) netty_unix_errors_errnoEWOULDBLOCK },
{ "errnoEINPROGRESS", "()I", (void *) netty_unix_errors_errnoEINPROGRESS }, { "errnoEINPROGRESS", "()I", (void *) netty_unix_errors_errnoEINPROGRESS },
{ "errorECONNREFUSED", "()I", (void *) netty_unix_errors_errorECONNREFUSED },
{ "strError", "(I)Ljava/lang/String;", (void *) netty_unix_errors_strError } { "strError", "(I)Ljava/lang/String;", (void *) netty_unix_errors_strError }
}; };
static const jint statically_referenced_fixed_method_table_size = sizeof(statically_referenced_fixed_method_table) / sizeof(statically_referenced_fixed_method_table[0]); static const jint statically_referenced_fixed_method_table_size = sizeof(statically_referenced_fixed_method_table) / sizeof(statically_referenced_fixed_method_table[0]);

View File

@ -28,6 +28,7 @@ import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoEI
import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoENOTCONN; import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoENOTCONN;
import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoEPIPE; import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoEPIPE;
import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoEWOULDBLOCK; import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoEWOULDBLOCK;
import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errorECONNREFUSED;
import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.strError; import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.strError;
/** /**
@ -43,6 +44,7 @@ public final class Errors {
public static final int ERRNO_EAGAIN_NEGATIVE = -errnoEAGAIN(); public static final int ERRNO_EAGAIN_NEGATIVE = -errnoEAGAIN();
public static final int ERRNO_EWOULDBLOCK_NEGATIVE = -errnoEWOULDBLOCK(); public static final int ERRNO_EWOULDBLOCK_NEGATIVE = -errnoEWOULDBLOCK();
public static final int ERRNO_EINPROGRESS_NEGATIVE = -errnoEINPROGRESS(); public static final int ERRNO_EINPROGRESS_NEGATIVE = -errnoEINPROGRESS();
public static final int ERROR_ECONNREFUSED_NEGATIVE = -errorECONNREFUSED();
/** /**
* Holds the mappings for errno codes to String messages. * Holds the mappings for errno codes to String messages.
@ -69,6 +71,19 @@ public final class Errors {
} }
} }
static final class NativeConnectException extends ConnectException {
private static final long serialVersionUID = -5532328671712318161L;
private final int expectedErr;
NativeConnectException(String method, int expectedErr) {
super(method);
this.expectedErr = expectedErr;
}
int expectedErr() {
return expectedErr;
}
}
static { static {
for (int i = 0; i < ERRORS.length; i++) { for (int i = 0; i < ERRORS.length; i++) {
// This is ok as strerror returns 'Unknown error i' when the message is not known. // This is ok as strerror returns 'Unknown error i' when the message is not known.
@ -76,8 +91,12 @@ public final class Errors {
} }
} }
static ConnectException newConnectException(String method, int err) { static void throwConnectException(String method, NativeConnectException refusedCause, int err)
return new ConnectException(method + "() failed: " + ERRORS[-err]); throws ConnectException {
if (err == refusedCause.expectedErr()) {
throw refusedCause;
}
throw new ConnectException(method + "() failed: " + ERRORS[-err]);
} }
public static NativeIoException newConnectionResetException(String method, int errnoNegative) { public static NativeIoException newConnectionResetException(String method, int errnoNegative) {

View File

@ -37,5 +37,6 @@ final class ErrorsStaticallyReferencedJniMethods {
static native int errnoEAGAIN(); static native int errnoEAGAIN();
static native int errnoEWOULDBLOCK(); static native int errnoEWOULDBLOCK();
static native int errnoEINPROGRESS(); static native int errnoEINPROGRESS();
static native int errorECONNREFUSED();
static native String strError(int err); static native String strError(int err);
} }

View File

@ -17,7 +17,6 @@ package io.netty.channel.unix;
import io.netty.channel.ChannelException; import io.netty.channel.ChannelException;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import io.netty.util.internal.ThrowableUtil;
import java.io.IOException; import java.io.IOException;
import java.net.Inet6Address; import java.net.Inet6Address;
@ -31,36 +30,43 @@ import static io.netty.channel.unix.Errors.ERRNO_EAGAIN_NEGATIVE;
import static io.netty.channel.unix.Errors.ERRNO_EINPROGRESS_NEGATIVE; import static io.netty.channel.unix.Errors.ERRNO_EINPROGRESS_NEGATIVE;
import static io.netty.channel.unix.Errors.ERRNO_EWOULDBLOCK_NEGATIVE; import static io.netty.channel.unix.Errors.ERRNO_EWOULDBLOCK_NEGATIVE;
import static io.netty.channel.unix.Errors.ioResult; import static io.netty.channel.unix.Errors.ioResult;
import static io.netty.channel.unix.Errors.newConnectException; import static io.netty.channel.unix.Errors.throwConnectException;
import static io.netty.channel.unix.Errors.newIOException; import static io.netty.channel.unix.Errors.newIOException;
import static io.netty.channel.unix.NativeInetAddress.address; import static io.netty.channel.unix.NativeInetAddress.address;
import static io.netty.channel.unix.NativeInetAddress.ipv4MappedIpv6Address; import static io.netty.channel.unix.NativeInetAddress.ipv4MappedIpv6Address;
import static io.netty.util.internal.ThrowableUtil.unknownStackTrace;
/** /**
* Provides a JNI bridge to native socket operations. * Provides a JNI bridge to native socket operations.
* <strong>Internal usage only!</strong> * <strong>Internal usage only!</strong>
*/ */
public final class Socket extends FileDescriptor { public final class Socket extends FileDescriptor {
private static final ClosedChannelException SHUTDOWN_CLOSED_CHANNEL_EXCEPTION = ThrowableUtil.unknownStackTrace( private static final ClosedChannelException SHUTDOWN_CLOSED_CHANNEL_EXCEPTION = unknownStackTrace(
new ClosedChannelException(), Socket.class, "shutdown(...)"); new ClosedChannelException(), Socket.class, "shutdown(...)");
private static final ClosedChannelException SEND_TO_CLOSED_CHANNEL_EXCEPTION = ThrowableUtil.unknownStackTrace( private static final ClosedChannelException SEND_TO_CLOSED_CHANNEL_EXCEPTION = unknownStackTrace(
new ClosedChannelException(), Socket.class, "sendTo(...)"); new ClosedChannelException(), Socket.class, "sendTo(...)");
private static final ClosedChannelException SEND_TO_ADDRESS_CLOSED_CHANNEL_EXCEPTION = private static final ClosedChannelException SEND_TO_ADDRESS_CLOSED_CHANNEL_EXCEPTION =
ThrowableUtil.unknownStackTrace(new ClosedChannelException(), Socket.class, "sendToAddress(...)"); unknownStackTrace(new ClosedChannelException(), Socket.class, "sendToAddress(...)");
private static final ClosedChannelException SEND_TO_ADDRESSES_CLOSED_CHANNEL_EXCEPTION = private static final ClosedChannelException SEND_TO_ADDRESSES_CLOSED_CHANNEL_EXCEPTION =
ThrowableUtil.unknownStackTrace(new ClosedChannelException(), Socket.class, "sendToAddresses(...)"); unknownStackTrace(new ClosedChannelException(), Socket.class, "sendToAddresses(...)");
private static final Errors.NativeIoException SEND_TO_CONNECTION_RESET_EXCEPTION = ThrowableUtil.unknownStackTrace( private static final Errors.NativeIoException SEND_TO_CONNECTION_RESET_EXCEPTION = unknownStackTrace(
Errors.newConnectionResetException("syscall:sendto(...)", Errors.ERRNO_EPIPE_NEGATIVE), Errors.newConnectionResetException("syscall:sendto(...)", Errors.ERRNO_EPIPE_NEGATIVE),
Socket.class, "sendTo(...)"); Socket.class, "sendTo(...)");
private static final Errors.NativeIoException SEND_TO_ADDRESS_CONNECTION_RESET_EXCEPTION = private static final Errors.NativeIoException SEND_TO_ADDRESS_CONNECTION_RESET_EXCEPTION =
ThrowableUtil.unknownStackTrace(Errors.newConnectionResetException("syscall:sendto(...)", unknownStackTrace(Errors.newConnectionResetException("syscall:sendto(...)",
Errors.ERRNO_EPIPE_NEGATIVE), Socket.class, "sendToAddress(...)"); Errors.ERRNO_EPIPE_NEGATIVE), Socket.class, "sendToAddress(...)");
private static final Errors.NativeIoException CONNECTION_RESET_EXCEPTION_SENDMSG = ThrowableUtil.unknownStackTrace( private static final Errors.NativeIoException CONNECTION_RESET_EXCEPTION_SENDMSG = unknownStackTrace(
Errors.newConnectionResetException("syscall:sendmsg(...)", Errors.newConnectionResetException("syscall:sendmsg(...)",
Errors.ERRNO_EPIPE_NEGATIVE), Socket.class, "sendToAddresses(...)"); Errors.ERRNO_EPIPE_NEGATIVE), Socket.class, "sendToAddresses(...)");
private static final Errors.NativeIoException CONNECTION_NOT_CONNECTED_SHUTDOWN_EXCEPTION = private static final Errors.NativeIoException CONNECTION_NOT_CONNECTED_SHUTDOWN_EXCEPTION =
ThrowableUtil.unknownStackTrace(Errors.newConnectionResetException("syscall:shutdown(...)", unknownStackTrace(Errors.newConnectionResetException("syscall:shutdown(...)",
Errors.ERRNO_ENOTCONN_NEGATIVE), Socket.class, "shutdown(...)"); Errors.ERRNO_ENOTCONN_NEGATIVE), Socket.class, "shutdown(...)");
private static final Errors.NativeConnectException FINISH_CONNECT_REFUSED_EXCEPTION =
unknownStackTrace(new Errors.NativeConnectException("syscall:getsockopt(...)",
Errors.ERROR_ECONNREFUSED_NEGATIVE), Socket.class, "finishConnect(...)");
private static final Errors.NativeConnectException CONNECT_REFUSED_EXCEPTION =
unknownStackTrace(new Errors.NativeConnectException("syscall:connect(...)",
Errors.ERROR_ECONNREFUSED_NEGATIVE), Socket.class, "connect(...)");
public Socket(int fd) { public Socket(int fd) {
super(fd); super(fd);
} }
@ -199,7 +205,7 @@ public final class Socket extends FileDescriptor {
// connect not complete yet need to wait for EPOLLOUT event // connect not complete yet need to wait for EPOLLOUT event
return false; return false;
} }
throw newConnectException("connect", res); throwConnectException("connect", CONNECT_REFUSED_EXCEPTION, res);
} }
return true; return true;
} }
@ -211,7 +217,7 @@ public final class Socket extends FileDescriptor {
// connect still in progress // connect still in progress
return false; return false;
} }
throw newConnectException("finishConnect", res); throwConnectException("finishConnect", FINISH_CONNECT_REFUSED_EXCEPTION, res);
} }
return true; return true;
} }