From 2a717bd50ee3150ac2c43250945f8149c6cda4f4 Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Tue, 30 Dec 2014 12:20:43 +0900 Subject: [PATCH] Throw exceptions outside the native code Rebased and cleaned-up based on the work by @normanmaurer Motivation: Currently, IOExceptions and ClosedChannelExceptions are thrown from inside the JNI methods. Instantiation of Java objects inside JNI code is an expensive operation, needless to say about filling stack trace for every instantiation of an exception. Modifications: Change most JNI methods to return a negative value on failure so that the exceptions are instantiated outside the native code. Also, pre-instantiate some commonly-thrown exceptions for better performance. Result: Performance gain --- .../main/c/io_netty_channel_epoll_Native.c | 426 ++++++++---------- .../main/c/io_netty_channel_epoll_Native.h | 33 +- .../java/io/netty/channel/epoll/Native.java | 312 +++++++++++-- 3 files changed, 470 insertions(+), 301 deletions(-) diff --git a/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.c b/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.c index c8f036ad27..9f5d24f0f4 100644 --- a/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.c +++ b/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.c @@ -32,11 +32,11 @@ #include "io_netty_channel_epoll_Native.h" // optional -extern int accept4(int sockFd, struct sockaddr *addr, socklen_t *addrlen, int flags) __attribute__((weak)); +extern int accept4(int sockFd, struct sockaddr* addr, socklen_t* addrlen, int flags) __attribute__((weak)); extern int epoll_create1(int flags) __attribute__((weak)); #ifdef IO_NETTY_SENDMMSG_NOT_FOUND -extern int sendmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, unsigned int flags) __attribute__((weak)); +extern int sendmmsg(int sockfd, struct mmsghdr* msgvec, unsigned int vlen, unsigned int flags) __attribute__((weak)); #ifndef __USE_GNU struct mmsghdr { @@ -74,36 +74,36 @@ jclass datagramSocketAddressClass = NULL; jclass nativeDatagramPacketClass = NULL; static int socketType; -static const char *ip4prefix = "::ffff:"; +static const char* ip4prefix = "::ffff:"; // util methods -void throwRuntimeException(JNIEnv *env, char *message) { +void throwRuntimeException(JNIEnv* env, char* message) { (*env)->ThrowNew(env, runtimeExceptionClass, message); } -void throwIOException(JNIEnv *env, char *message) { +void throwIOException(JNIEnv* env, char* message) { (*env)->ThrowNew(env, ioExceptionClass, message); } -void throwClosedChannelException(JNIEnv *env) { +void throwClosedChannelException(JNIEnv* env) { jobject exception = (*env)->NewObject(env, closedChannelExceptionClass, closedChannelExceptionMethodId); (*env)->Throw(env, exception); } -void throwOutOfMemoryError( JNIEnv *env, char *message) { +void throwOutOfMemoryError(JNIEnv* env, char* message) { jclass exceptionClass = (*env)->FindClass(env, "java/lang/OutOfMemoryError"); (*env)->ThrowNew(env, exceptionClass, message); } -char *exceptionMessage(char *msg, int error) { - char *err = strerror(error); - char *result = malloc(strlen(msg) + strlen(err) + 1); +char* exceptionMessage(char* msg, int error) { + char* err = strerror(error); + char* result = malloc(strlen(msg) + strlen(err) + 1); strcpy(result, msg); strcat(result, err); return result; } -jint epollCtl(JNIEnv * env, jint efd, int op, jint fd, jint flags, jint id) { +jint epollCtl(JNIEnv* env, jint efd, int op, jint fd, jint flags, jint id) { uint32_t events = EPOLLET; if (flags & EPOLL_ACCEPT) { @@ -125,7 +125,7 @@ jint epollCtl(JNIEnv * env, jint efd, int op, jint fd, jint flags, jint id) { return epoll_ctl(efd, op, fd, &ev); } -jint getOption(JNIEnv *env, jint fd, int level, int optname, void *optval, socklen_t optlen) { +jint getOption(JNIEnv* env, jint fd, int level, int optname, void* optval, socklen_t optlen) { int code; code = getsockopt(fd, level, optname, optval, &optlen); if (code == 0) { @@ -136,7 +136,7 @@ jint getOption(JNIEnv *env, jint fd, int level, int optname, void *optval, sockl return code; } -int setOption(JNIEnv *env, jint fd, int level, int optname, const void *optval, socklen_t len) { +int setOption(JNIEnv* env, jint fd, int level, int optname, const void* optval, socklen_t len) { int rc = setsockopt(fd, level, optname, optval, len); if (rc < 0) { int err = errno; @@ -145,17 +145,17 @@ int setOption(JNIEnv *env, jint fd, int level, int optname, const void *optval, return rc; } -jobject createInetSocketAddress(JNIEnv * env, struct sockaddr_storage addr) { +jobject createInetSocketAddress(JNIEnv* env, struct sockaddr_storage addr) { char ipstr[INET6_ADDRSTRLEN]; int port; jstring ipString; if (addr.ss_family == AF_INET) { - struct sockaddr_in *s = (struct sockaddr_in *)&addr; + struct sockaddr_in* s = (struct sockaddr_in*) &addr; port = ntohs(s->sin_port); inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr); ipString = (*env)->NewStringUTF(env, ipstr); } else { - struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr; + struct sockaddr_in6* s = (struct sockaddr_in6*) &addr; port = ntohs(s->sin6_port); inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof ipstr); if (strncasecmp(ipstr, ip4prefix, 7) == 0) { @@ -173,10 +173,10 @@ jobject createInetSocketAddress(JNIEnv * env, struct sockaddr_storage addr) { return socketAddr; } -jbyteArray createInetSocketAddressArray(JNIEnv * env, struct sockaddr_storage addr) { +jbyteArray createInetSocketAddressArray(JNIEnv* env, struct sockaddr_storage addr) { int port; if (addr.ss_family == AF_INET) { - struct sockaddr_in *s = (struct sockaddr_in *)&addr; + struct sockaddr_in* s = (struct sockaddr_in*) &addr; port = ntohs(s->sin_port); // Encode address and port into the array @@ -195,7 +195,7 @@ jbyteArray createInetSocketAddressArray(JNIEnv * env, struct sockaddr_storage ad return bArray; } else { - struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr; + struct sockaddr_in6* s = (struct sockaddr_in6*) &addr; port = ntohs(s->sin6_port); if (s->sin6_addr.s6_addr[0] == 0x00 && s->sin6_addr.s6_addr[1] == 0x00 && s->sin6_addr.s6_addr[2] == 0x00 && s->sin6_addr.s6_addr[3] == 0x00 && s->sin6_addr.s6_addr[4] == 0x00 @@ -235,17 +235,17 @@ jbyteArray createInetSocketAddressArray(JNIEnv * env, struct sockaddr_storage ad } } -jobject createDatagramSocketAddress(JNIEnv * env, struct sockaddr_storage addr, int len) { +jobject createDatagramSocketAddress(JNIEnv* env, struct sockaddr_storage addr, int len) { char ipstr[INET6_ADDRSTRLEN]; int port; jstring ipString; if (addr.ss_family == AF_INET) { - struct sockaddr_in *s = (struct sockaddr_in *)&addr; + struct sockaddr_in* s = (struct sockaddr_in*) &addr; port = ntohs(s->sin_port); inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr); ipString = (*env)->NewStringUTF(env, ipstr); } else { - struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr; + struct sockaddr_in6* s = (struct sockaddr_in6*) &addr; port = ntohs(s->sin6_port); inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof ipstr); @@ -264,7 +264,7 @@ jobject createDatagramSocketAddress(JNIEnv * env, struct sockaddr_storage addr, return socketAddr; } -int init_sockaddr(JNIEnv * env, jbyteArray address, jint scopeId, jint jport, struct sockaddr_storage * addr) { +int init_sockaddr(JNIEnv* env, jbyteArray address, jint scopeId, jint jport, struct sockaddr_storage* addr) { uint16_t port = htons((uint16_t) jport); // Use GetPrimitiveArrayCritical and ReleasePrimitiveArrayCritical to signal the VM that we really would like // to not do a memory copy here. This is ok as we not do any blocking action here anyway. @@ -273,22 +273,21 @@ int init_sockaddr(JNIEnv * env, jbyteArray address, jint scopeId, jint jport, st if (addressBytes == NULL) { // No memory left ?!?!? throwOutOfMemoryError(env, "Can't allocate memory"); - return -1; } if (socketType == AF_INET6) { - struct sockaddr_in6* ip6addr = (struct sockaddr_in6 *) addr; + struct sockaddr_in6* ip6addr = (struct sockaddr_in6*) addr; ip6addr->sin6_family = AF_INET6; ip6addr->sin6_port = port; if (scopeId != 0) { ip6addr->sin6_scope_id = (uint32_t) scopeId; } - memcpy( &(ip6addr->sin6_addr.s6_addr), addressBytes, 16); + memcpy(&(ip6addr->sin6_addr.s6_addr), addressBytes, 16); } else { - struct sockaddr_in* ipaddr = (struct sockaddr_in *) addr; + struct sockaddr_in* ipaddr = (struct sockaddr_in*) addr; ipaddr->sin_family = AF_INET; ipaddr->sin_port = port; - memcpy( &(ipaddr->sin_addr.s_addr), addressBytes + 12, 4); + memcpy(&(ipaddr->sin_addr.s_addr), addressBytes + 12, 4); } (*env)->ReleasePrimitiveArrayCritical(env, address, addressBytes, JNI_ABORT); @@ -308,7 +307,7 @@ static int socket_type() { } } -int init_in_addr(JNIEnv * env, jbyteArray address, struct in_addr * addr) { +int init_in_addr(JNIEnv* env, jbyteArray address, struct in_addr* addr) { // Use GetPrimitiveArrayCritical and ReleasePrimitiveArrayCritical to signal the VM that we really would like // to not do a memory copy here. This is ok as we not do any blocking action here anyway. // This is important as the VM may suspend GC for the time! @@ -330,7 +329,7 @@ int init_in_addr(JNIEnv * env, jbyteArray address, struct in_addr * addr) { jint JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env; - if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_6) != JNI_OK) { + if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) { return JNI_ERR; } else { // cache classes that are used within other jni methods for performance reasons @@ -398,7 +397,7 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { return JNI_ERR; } - void *mem = malloc(1); + void* mem = malloc(1); if (mem == NULL) { throwOutOfMemoryError(env, "Error allocating native buffer"); return JNI_ERR; @@ -542,9 +541,9 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { } } -void JNI_OnUnload(JavaVM *vm, void *reserved) { +void JNI_OnUnload(JavaVM* vm, void* reserved) { JNIEnv* env; - if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_6) != JNI_OK) { + if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) { // Something is wrong but nothing we can do about this :( return; } else { @@ -567,7 +566,7 @@ void JNI_OnUnload(JavaVM *vm, void *reserved) { } } -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_eventFd(JNIEnv * env, jclass clazz) { +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_eventFd(JNIEnv* env, jclass clazz) { jint eventFD = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); if (eventFD < 0) { @@ -577,8 +576,8 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_eventFd(JNIEnv * env, return eventFD; } -JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_eventFdWrite(JNIEnv * env, jclass clazz, jint fd, jlong value) { - jint eventFD = eventfd_write(fd, (eventfd_t)value); +JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_eventFdWrite(JNIEnv* env, jclass clazz, jint fd, jlong value) { + jint eventFD = eventfd_write(fd, (eventfd_t) value); if (eventFD < 0) { int err = errno; @@ -586,7 +585,7 @@ JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_eventFdWrite(JNIEnv * } } -JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_eventFdRead(JNIEnv * env, jclass clazz, jint fd) { +JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_eventFdRead(JNIEnv* env, jclass clazz, jint fd) { uint64_t eventfd_t; if (eventfd_read(fd, &eventfd_t) != 0) { @@ -595,7 +594,7 @@ JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_eventFdRead(JNIEnv * e } } -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_epollCreate(JNIEnv * env, jclass clazz) { +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_epollCreate(JNIEnv* env, jclass clazz) { jint efd; if (epoll_create1) { efd = epoll_create1(EPOLL_CLOEXEC); @@ -623,7 +622,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_epollCreate(JNIEnv * e return efd; } -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_epollWait(JNIEnv * env, jclass clazz, jint efd, jlongArray events, jint timeout) { +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_epollWait(JNIEnv* env, jclass clazz, jint efd, jlongArray events, jint timeout) { int len = (*env)->GetArrayLength(env, events); struct epoll_event ev[len]; int ready; @@ -631,7 +630,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_epollWait(JNIEnv * env do { ready = epoll_wait(efd, ev, len, timeout); // was interrupted try again. - } while (ready == -1 && (( err = errno) == EINTR)); + } while (ready == -1 && ((err = errno) == EINTR)); if (ready < 0) { throwIOException(env, exceptionMessage("Error during epoll_wait(...): ", err)); @@ -646,7 +645,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_epollWait(JNIEnv * env // Use GetPrimitiveArrayCritical and ReleasePrimitiveArrayCritical to signal the VM that we really would like // to not do a memory copy here. This is ok as we not do any blocking action here anyway. // This is important as the VM may suspend GC for the time! - jlong *elements = (*env)->GetPrimitiveArrayCritical(env, events, &isCopy); + jlong* elements = (*env)->GetPrimitiveArrayCritical(env, events, &isCopy); if (elements == NULL) { // No memory left ?!?!? throwOutOfMemoryError(env, "Can't allocate memory"); @@ -679,21 +678,21 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_epollWait(JNIEnv * env return ready; } -JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_epollCtlAdd(JNIEnv * env, jclass clazz, jint efd, jint fd, jint flags, jint id) { +JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_epollCtlAdd(JNIEnv* env, jclass clazz, jint efd, jint fd, jint flags, jint id) { if (epollCtl(env, efd, EPOLL_CTL_ADD, fd, flags, id) < 0) { int err = errno; throwRuntimeException(env, exceptionMessage("Error during calling epoll_ctl(...): ", err)); } } -JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_epollCtlMod(JNIEnv * env, jclass clazz, jint efd, jint fd, jint flags, jint id) { +JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_epollCtlMod(JNIEnv* env, jclass clazz, jint efd, jint fd, jint flags, jint id) { if (epollCtl(env, efd, EPOLL_CTL_MOD, fd, flags, id) < 0) { int err = errno; throwRuntimeException(env, exceptionMessage("Error during calling epoll_ctl(...): ", err)); } } -JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_epollCtlDel(JNIEnv * env, jclass clazz, jint efd, jint fd) { +JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_epollCtlDel(JNIEnv* env, jclass clazz, jint efd, jint fd) { // Create an empty event to workaround a bug in older kernels which can not handle NULL. struct epoll_event event = { 0 }; if (epoll_ctl(efd, EPOLL_CTL_DEL, fd, &event) < 0) { @@ -702,43 +701,34 @@ JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_epollCtlDel(JNIEnv * e } } -jint write0(JNIEnv * env, jclass clazz, jint fd, void *buffer, jint pos, jint limit) { +jint _write(JNIEnv* env, jclass clazz, jint fd, void* buffer, jint pos, jint limit) { ssize_t res; int err; do { res = write(fd, buffer + pos, (size_t) (limit - pos)); // keep on writing if it was interrupted - } while(res == -1 && ((err = errno) == EINTR)); + } while (res == -1 && ((err = errno) == EINTR)); if (res < 0) { - // network stack saturated... try again later - if (err == EAGAIN || err == EWOULDBLOCK) { - return 0; - } - if (err == EBADF) { - throwClosedChannelException(env); - return -1; - } - throwIOException(env, exceptionMessage("Error while write(...): ", err)); - return -1; + return -err; } return (jint) res; } -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_write(JNIEnv * env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit) { - void *buffer = (*env)->GetDirectBufferAddress(env, jbuffer); +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_write0(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit) { + void* buffer = (*env)->GetDirectBufferAddress(env, jbuffer); if (buffer == NULL) { throwRuntimeException(env, "Unable to access address of buffer"); return -1; } - return write0(env, clazz, fd, buffer, pos, limit); + return _write(env, clazz, fd, buffer, pos, limit); } -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_writeAddress(JNIEnv * env, jclass clazz, jint fd, jlong address, jint pos, jint limit) { - return write0(env, clazz, fd, (void *) address, pos, limit); +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_writeAddress0(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit) { + return _write(env, clazz, fd, (void*) address, pos, limit); } -jint sendTo0(JNIEnv * env, jint fd, void* buffer, jint pos, jint limit ,jbyteArray address, jint scopeId, jint port) { +jint _sendTo(JNIEnv* env, jint fd, void* buffer, jint pos, jint limit ,jbyteArray address, jint scopeId, jint port) { struct sockaddr_storage addr; if (init_sockaddr(env, address, scopeId, port, &addr) == -1) { return -1; @@ -747,39 +737,30 @@ jint sendTo0(JNIEnv * env, jint fd, void* buffer, jint pos, jint limit ,jbyteArr ssize_t res; int err; do { - res = sendto(fd, buffer + pos, (size_t) (limit - pos), 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_storage)); + res = sendto(fd, buffer + pos, (size_t) (limit - pos), 0, (struct sockaddr*) &addr, sizeof(struct sockaddr_storage)); // keep on writing if it was interrupted - } while(res == -1 && ((err = errno) == EINTR)); + } while (res == -1 && ((err = errno) == EINTR)); if (res < 0) { - // network stack saturated... try again later - if (err == EAGAIN || err == EWOULDBLOCK) { - return 0; - } - if (err == EBADF) { - throwClosedChannelException(env); - return -1; - } - throwIOException(env, exceptionMessage("Error while sendto(...): ", err)); - return -1; + return -err; } return (jint) res; } -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_sendTo(JNIEnv * env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit, jbyteArray address, jint scopeId, jint port) { - void *buffer = (*env)->GetDirectBufferAddress(env, jbuffer); +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_sendTo0(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit, jbyteArray address, jint scopeId, jint port) { + void* buffer = (*env)->GetDirectBufferAddress(env, jbuffer); if (buffer == NULL) { throwRuntimeException(env, "Unable to access address of buffer"); return -1; } - return sendTo0(env, fd, buffer, pos, limit, address, scopeId, port); + return _sendTo(env, fd, buffer, pos, limit, address, scopeId, port); } -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_sendToAddress(JNIEnv * env, jclass clazz, jint fd, jlong memoryAddress, jint pos, jint limit ,jbyteArray address, jint scopeId, jint port) { - return sendTo0(env, fd, (void*) memoryAddress, pos, limit, address, scopeId, port); +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_sendToAddress0(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint pos, jint limit ,jbyteArray address, jint scopeId, jint port) { + return _sendTo(env, fd, (void*) memoryAddress, pos, limit, address, scopeId, port); } -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_sendToAddresses(JNIEnv * env, jclass clazz, jint fd, jlong memoryAddress, jint length, jbyteArray address, jint scopeId, jint port) { +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_sendToAddresses(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint length, jbyteArray address, jint scopeId, jint port) { struct sockaddr_storage addr; if (init_sockaddr(env, address, scopeId, port, &addr) == -1) { @@ -789,7 +770,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_sendToAddresses(JNIEnv struct msghdr m; m.msg_name = (void*) &addr; m.msg_namelen = (socklen_t) sizeof(struct sockaddr_storage); - m.msg_iov = (struct iovec *) memoryAddress; + m.msg_iov = (struct iovec*) memoryAddress; m.msg_iovlen = length; ssize_t res; @@ -797,24 +778,15 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_sendToAddresses(JNIEnv do { res = sendmsg(fd, &m, 0); // keep on writing if it was interrupted - } while(res == -1 && ((err = errno) == EINTR)); + } while (res == -1 && ((err = errno) == EINTR)); if (res < 0) { - // network stack saturated... try again later - if (err == EAGAIN || err == EWOULDBLOCK) { - return 0; - } - if (err == EBADF) { - throwClosedChannelException(env); - return -1; - } - throwIOException(env, exceptionMessage("Error while sendto(...): ", err)); - return -1; + return -err; } return (jint) res; } -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_sendmmsg(JNIEnv * env, jclass clazz, jint fd, jobjectArray packets, jint offset, jint len) { +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_sendmmsg0(JNIEnv* env, jclass clazz, jint fd, jobjectArray packets, jint offset, jint len) { struct mmsghdr msg[len]; int i; @@ -835,7 +807,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_sendmmsg(JNIEnv * env, msg[i].msg_hdr.msg_name = &addr; msg[i].msg_hdr.msg_namelen = sizeof(addr); - msg[i].msg_hdr.msg_iov = (struct iovec *) (*env)->GetLongField(env, packet, packetMemoryAddressFieldId); + msg[i].msg_hdr.msg_iov = (struct iovec*) (*env)->GetLongField(env, packet, packetMemoryAddressFieldId); msg[i].msg_hdr.msg_iovlen = (*env)->GetIntField(env, packet, packetCountFieldId);; } @@ -844,31 +816,22 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_sendmmsg(JNIEnv * env, do { res = sendmmsg(fd, msg, len, 0); // keep on writing if it was interrupted - } while(res == -1 && ((err = errno) == EINTR)); + } while (res == -1 && ((err = errno) == EINTR)); if (res < 0) { - // network stack saturated... try again later - if (err == EAGAIN || err == EWOULDBLOCK) { - return 0; - } - if (err == EBADF) { - throwClosedChannelException(env); - return -1; - } - throwIOException(env, exceptionMessage("Error while sendmmsg(...): ", err)); - return -1; + return -err; } return (jint) res; } -jobject recvFrom0(JNIEnv * env, jint fd, void* buffer, jint pos, jint limit) { +jobject recvFrom0(JNIEnv* env, jint fd, void* buffer, jint pos, jint limit) { struct sockaddr_storage addr; socklen_t addrlen = sizeof(addr); ssize_t res; int err; do { - res = recvfrom(fd, buffer + pos, (size_t) (limit - pos), 0, (struct sockaddr *)&addr, &addrlen); + res = recvfrom(fd, buffer + pos, (size_t) (limit - pos), 0, (struct sockaddr*) &addr, &addrlen); // Keep on reading if we was interrupted } while (res == -1 && ((err = errno) == EINTR)); @@ -888,8 +851,8 @@ jobject recvFrom0(JNIEnv * env, jint fd, void* buffer, jint pos, jint limit) { return createDatagramSocketAddress(env, addr, res); } -JNIEXPORT jobject JNICALL Java_io_netty_channel_epoll_Native_recvFrom(JNIEnv * env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit) { - void *buffer = (*env)->GetDirectBufferAddress(env, jbuffer); +JNIEXPORT jobject JNICALL Java_io_netty_channel_epoll_Native_recvFrom(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit) { + void* buffer = (*env)->GetDirectBufferAddress(env, jbuffer); if (buffer == NULL) { throwRuntimeException(env, "Unable to access address of buffer"); return NULL; @@ -898,34 +861,25 @@ JNIEXPORT jobject JNICALL Java_io_netty_channel_epoll_Native_recvFrom(JNIEnv * e return recvFrom0(env, fd, buffer, pos, limit); } -JNIEXPORT jobject JNICALL Java_io_netty_channel_epoll_Native_recvFromAddress(JNIEnv * env, jclass clazz, jint fd, jlong address, jint pos, jint limit) { +JNIEXPORT jobject JNICALL Java_io_netty_channel_epoll_Native_recvFromAddress(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit) { return recvFrom0(env, fd, (void*) address, pos, limit); } -jlong writev0(JNIEnv * env, jclass clazz, jint fd, struct iovec * iov, jint length) { +jlong _writev(JNIEnv* env, jclass clazz, jint fd, struct iovec* iov, jint length) { ssize_t res; int err; do { res = writev(fd, iov, length); // keep on writing if it was interrupted - } while(res == -1 && ((err = errno) == EINTR)); + } while (res == -1 && ((err = errno) == EINTR)); if (res < 0) { - if (err == EAGAIN || err == EWOULDBLOCK) { - // network stack is saturated we will try again later - return 0; - } - if (err == EBADF) { - throwClosedChannelException(env); - return -1; - } - throwIOException(env, exceptionMessage("Error while writev(...): ", err)); - return -1; + return -err; } return (jlong) res; } -JNIEXPORT jlong JNICALL Java_io_netty_channel_epoll_Native_writev(JNIEnv * env, jclass clazz, jint fd, jobjectArray buffers, jint offset, jint length) { +JNIEXPORT jlong JNICALL Java_io_netty_channel_epoll_Native_writev0(JNIEnv* env, jclass clazz, jint fd, jobjectArray buffers, jint offset, jint length) { struct iovec iov[length]; int iovidx = 0; int i; @@ -948,7 +902,7 @@ JNIEXPORT jlong JNICALL Java_io_netty_channel_epoll_Native_writev(JNIEnv * env, } else { limit = (*env)->GetIntField(env, bufObj, limitFieldId); } - void *buffer = (*env)->GetDirectBufferAddress(env, bufObj); + void* buffer = (*env)->GetDirectBufferAddress(env, bufObj); if (buffer == NULL) { throwRuntimeException(env, "Unable to access address of buffer"); return -1; @@ -963,15 +917,15 @@ JNIEXPORT jlong JNICALL Java_io_netty_channel_epoll_Native_writev(JNIEnv * env, // See https://github.com/netty/netty/issues/2623 (*env)->DeleteLocalRef(env, bufObj); } - return writev0(env, clazz, fd, iov, length); + return _writev(env, clazz, fd, iov, length); } -JNIEXPORT jlong JNICALL Java_io_netty_channel_epoll_Native_writevAddresses(JNIEnv * env, jclass clazz, jint fd, jlong memoryAddress, jint length) { - struct iovec * iov = (struct iovec *) memoryAddress; - return writev0(env, clazz, fd, iov, length); +JNIEXPORT jlong JNICALL Java_io_netty_channel_epoll_Native_writevAddresses0(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint length) { + struct iovec* iov = (struct iovec*) memoryAddress; + return _writev(env, clazz, fd, iov, length); } -jint read0(JNIEnv * env, jclass clazz, jint fd, void *buffer, jint pos, jint limit) { +jint _read(JNIEnv* env, jclass clazz, jint fd, void* buffer, jint pos, jint limit) { ssize_t res; int err; do { @@ -980,45 +934,32 @@ jint read0(JNIEnv * env, jclass clazz, jint fd, void *buffer, jint pos, jint lim } while (res == -1 && ((err = errno) == EINTR)); if (res < 0) { - if (err == EAGAIN || err == EWOULDBLOCK) { - // Nothing left to read - return 0; - } - if (err == EBADF) { - throwClosedChannelException(env); - return -1; - } - throwIOException(env, exceptionMessage("Error while read(...): ", err)); - return -1; - } - - if (res == 0) { - // end-of-stream - return -1; + return -err; } return (jint) res; } -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_read(JNIEnv * env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit) { - void *buffer = (*env)->GetDirectBufferAddress(env, jbuffer); +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_read0(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit) { + void* buffer = (*env)->GetDirectBufferAddress(env, jbuffer); if (buffer == NULL) { throwRuntimeException(env, "Unable to access address of buffer"); return -1; } - return read0(env, clazz, fd, buffer, pos, limit); + return _read(env, clazz, fd, buffer, pos, limit); } -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_readAddress(JNIEnv * env, jclass clazz, jint fd, jlong address, jint pos, jint limit) { - return read0(env, clazz, fd, (void*) address, pos, limit); +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_readAddress0(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit) { + return _read(env, clazz, fd, (void*) address, pos, limit); } -JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_close(JNIEnv * env, jclass clazz, jint fd) { +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_close0(JNIEnv* env, jclass clazz, jint fd) { if (close(fd) < 0) { - throwIOException(env, "Error closing file descriptor"); + return -errno; } + return 0; } -JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_shutdown(JNIEnv * env, jclass clazz, jint fd, jboolean read, jboolean write) { +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_shutdown0(JNIEnv* env, jclass clazz, jint fd, jboolean read, jboolean write) { int mode; if (read && write) { mode = SHUT_RDWR; @@ -1028,18 +969,17 @@ JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_shutdown(JNIEnv * env, mode = SHUT_WR; } if (shutdown(fd, mode) < 0) { - throwIOException(env, "Error shutdown socket file descriptor"); + return -errno; } + return 0; } -jint socket0(JNIEnv * env, jclass clazz, int type) { +jint socket0(JNIEnv* env, jclass clazz, int type) { // TODO: Maybe also respect -Djava.net.preferIPv4Stack=true int fd = socket(socketType, type | SOCK_NONBLOCK, 0); if (fd == -1) { - int err = errno; - throwIOException(env, exceptionMessage("Error creating socket: ", err)); - return -1; - } else if (socketType == AF_INET6){ + return -errno; + } else if (socketType == AF_INET6) { // Allow to listen /connect ipv4 and ipv6 int optval = 0; if (setOption(env, fd, IPPROTO_IPV6, IPV6_V6ONLY, &optval, sizeof(optval)) < 0) { @@ -1051,58 +991,51 @@ jint socket0(JNIEnv * env, jclass clazz, int type) { return fd; } -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_socketDgram(JNIEnv * env, jclass clazz) { +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_socketDgram(JNIEnv* env, jclass clazz) { return socket0(env, clazz, SOCK_DGRAM); } -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_socketStream(JNIEnv * env, jclass clazz) { +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_socketStream(JNIEnv* env, jclass clazz) { return socket0(env, clazz, SOCK_STREAM); } -JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_bind(JNIEnv * env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port) { +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_bind(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port) { struct sockaddr_storage addr; if (init_sockaddr(env, address, scopeId, port, &addr) == -1) { - return; + return -1; } - if(bind(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1){ - int err = errno; - throwIOException(env, exceptionMessage("Error during bind(...): ", err)); + if (bind(fd, (struct sockaddr*) &addr, sizeof(addr)) == -1) { + return -errno; } } -JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_listen(JNIEnv * env, jclass clazz, jint fd, jint backlog) { - if(listen(fd, backlog) == -1) { - int err = errno; - throwIOException(env, exceptionMessage("Error during listen(...): ", err)); +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_listen0(JNIEnv* env, jclass clazz, jint fd, jint backlog) { + if (listen(fd, backlog) == -1) { + return -errno; } } -JNIEXPORT jboolean JNICALL Java_io_netty_channel_epoll_Native_connect(JNIEnv * env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port) { +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_connect(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port) { struct sockaddr_storage addr; if (init_sockaddr(env, address, scopeId, port, &addr) == -1) { + // A runtime exception was thrown return -1; } int res; int err; do { - res = connect(fd, (struct sockaddr *) &addr, sizeof(addr)); + res = connect(fd, (struct sockaddr*) &addr, sizeof(addr)); } while (res == -1 && ((err = errno) == EINTR)); if (res < 0) { - if (err == EINPROGRESS) { - // connect not complete yet need to wait for EPOLLOUT event - return JNI_FALSE; - } - throwIOException(env, exceptionMessage("Unable to connect to remote host: ", err)); - - return JNI_FALSE; + return -err; } - return JNI_TRUE; + return 0; } -JNIEXPORT jboolean JNICALL Java_io_netty_channel_epoll_Native_finishConnect(JNIEnv * env, jclass clazz, jint fd) { +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_finishConnect0(JNIEnv* env, jclass clazz, jint fd) { // connect may be done // return true if connection finished successfully // return false if connection is still in progress @@ -1111,22 +1044,16 @@ JNIEXPORT jboolean JNICALL Java_io_netty_channel_epoll_Native_finishConnect(JNIE int res = getOption(env, fd, SOL_SOCKET, SO_ERROR, &optval, sizeof(optval)); if (res != 0) { // getOption failed - throwIOException(env, exceptionMessage("finishConnect getOption failed: ", res)); - return JNI_FALSE; - } else if (optval == EINPROGRESS) { - // connect still in progress - return JNI_FALSE; - } else if (optval == 0) { - // connect succeeded - return JNI_TRUE; - } else { - // connect failed - throwIOException(env, exceptionMessage("Unable to connect to remote host: ", optval)); - return JNI_FALSE; + return -1; } + if (optval == 0) { + // connect succeeded + return 0; + } + return -optval; } -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_accept(JNIEnv * env, jclass clazz, jint fd) { +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_accept0(JNIEnv* env, jclass clazz, jint fd) { jint socketFd; int err; @@ -1139,31 +1066,23 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_accept(JNIEnv * env, j } while (socketFd == -1 && ((err = errno) == EINTR)); if (socketFd == -1) { - if (err == EAGAIN || err == EWOULDBLOCK) { - // Everything consumed so just return -1 here. - return -1; - } else { - throwIOException(env, exceptionMessage("Error during accept(...): ", err)); - return -1; - } + return -err; } if (accept4) { return socketFd; } else { // accept4 was not present so need two more sys-calls ... if (fcntl(socketFd, F_SETFD, FD_CLOEXEC) == -1) { - throwIOException(env, exceptionMessage("Error during accept(...): ", err)); - return -1; + return -errno; } if (fcntl(socketFd, F_SETFL, O_NONBLOCK) == -1) { - throwIOException(env, exceptionMessage("Error during accept(...): ", err)); - return -1; + return -errno; } } return socketFd; } -JNIEXPORT jlong JNICALL Java_io_netty_channel_epoll_Native_sendfile0(JNIEnv *env, jclass clazz, jint fd, jobject fileRegion, jlong base_off, jlong off, jlong len) { +JNIEXPORT jlong JNICALL Java_io_netty_channel_epoll_Native_sendfile0(JNIEnv* env, jclass clazz, jint fd, jobject fileRegion, jlong base_off, jlong off, jlong len) { jobject fileChannel = (*env)->GetObjectField(env, fileRegion, fileChannelFieldId); if (fileChannel == NULL) { throwRuntimeException(env, "Unable to obtain FileChannel from FileRegion"); @@ -1186,11 +1105,7 @@ JNIEXPORT jlong JNICALL Java_io_netty_channel_epoll_Native_sendfile0(JNIEnv *env res = sendfile(fd, srcFd, &offset, (size_t) len); } while (res == -1 && ((err = errno) == EINTR)); if (res < 0) { - if (err == EAGAIN) { - return 0; - } - throwIOException(env, exceptionMessage("Error during sendfile(...): ", err)); - return -1; + return -err; } if (res > 0) { // update the transfered field in DefaultFileRegion @@ -1200,61 +1115,61 @@ JNIEXPORT jlong JNICALL Java_io_netty_channel_epoll_Native_sendfile0(JNIEnv *env return res; } -JNIEXPORT jbyteArray JNICALL Java_io_netty_channel_epoll_Native_remoteAddress0(JNIEnv * env, jclass clazz, jint fd) { +JNIEXPORT jbyteArray JNICALL Java_io_netty_channel_epoll_Native_remoteAddress0(JNIEnv* env, jclass clazz, jint fd) { socklen_t len; struct sockaddr_storage addr; len = sizeof addr; - if (getpeername(fd, (struct sockaddr*)&addr, &len) == -1) { + if (getpeername(fd, (struct sockaddr*) &addr, &len) == -1) { return NULL; } return createInetSocketAddressArray(env, addr); } -JNIEXPORT jbyteArray JNICALL Java_io_netty_channel_epoll_Native_localAddress0(JNIEnv * env, jclass clazz, jint fd) { +JNIEXPORT jbyteArray JNICALL Java_io_netty_channel_epoll_Native_localAddress0(JNIEnv* env, jclass clazz, jint fd) { socklen_t len; struct sockaddr_storage addr; len = sizeof addr; - if (getsockname(fd, (struct sockaddr*)&addr, &len) == -1) { + if (getsockname(fd, (struct sockaddr*) &addr, &len) == -1) { return NULL; } return createInetSocketAddressArray(env, addr); } -JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setReuseAddress(JNIEnv * env, jclass clazz, jint fd, jint optval) { +JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setReuseAddress(JNIEnv* env, jclass clazz, jint fd, jint optval) { setOption(env, fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); } -JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setReusePort(JNIEnv * env, jclass clazz, jint fd, jint optval) { +JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setReusePort(JNIEnv* env, jclass clazz, jint fd, jint optval) { setOption(env, fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)); } -JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setTcpNoDelay(JNIEnv *env, jclass clazz, jint fd, jint optval) { +JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setTcpNoDelay(JNIEnv* env, jclass clazz, jint fd, jint optval) { setOption(env, fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval)); } -JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setReceiveBufferSize(JNIEnv *env, jclass clazz, jint fd, jint optval) { +JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setReceiveBufferSize(JNIEnv* env, jclass clazz, jint fd, jint optval) { setOption(env, fd, SOL_SOCKET, SO_RCVBUF, &optval, sizeof(optval)); } -JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setSendBufferSize(JNIEnv *env, jclass clazz, jint fd, jint optval) { +JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setSendBufferSize(JNIEnv* env, jclass clazz, jint fd, jint optval) { setOption(env, fd, SOL_SOCKET, SO_SNDBUF, &optval, sizeof(optval)); } -JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setKeepAlive(JNIEnv *env, jclass clazz, jint fd, jint optval) { +JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setKeepAlive(JNIEnv* env, jclass clazz, jint fd, jint optval) { setOption(env, fd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)); } -JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setTcpCork(JNIEnv *env, jclass clazz, jint fd, jint optval) { +JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setTcpCork(JNIEnv* env, jclass clazz, jint fd, jint optval) { setOption(env, fd, SOL_TCP, TCP_CORK, &optval, sizeof(optval)); } -JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setSoLinger(JNIEnv *env, jclass clazz, jint fd, jint optval) { +JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setSoLinger(JNIEnv* env, jclass clazz, jint fd, jint optval) { setOption(env, fd, IPPROTO_IP, IP_TOS, &optval, sizeof(optval)); } -JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setTrafficClass(JNIEnv *env, jclass clazz, jint fd, jint optval) { +JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setTrafficClass(JNIEnv* env, jclass clazz, jint fd, jint optval) { struct linger solinger; if (optval < 0) { solinger.l_onoff = 0; @@ -1266,23 +1181,23 @@ JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setTrafficClass(JNIEnv setOption(env, fd, SOL_SOCKET, SO_LINGER, &solinger, sizeof(solinger)); } -JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setBroadcast(JNIEnv * env, jclass clazz, jint fd, jint optval) { +JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setBroadcast(JNIEnv* env, jclass clazz, jint fd, jint optval) { setOption(env, fd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)); } -JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setTcpKeepIdle(JNIEnv *env, jclass clazz, jint fd, jint optval) { +JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setTcpKeepIdle(JNIEnv* env, jclass clazz, jint fd, jint optval) { setOption(env, fd, SOL_TCP, TCP_KEEPIDLE, &optval, sizeof(optval)); } -JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setTcpKeepIntvl(JNIEnv *env, jclass clazz, jint fd, jint optval) { +JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setTcpKeepIntvl(JNIEnv* env, jclass clazz, jint fd, jint optval) { setOption(env, fd, SOL_TCP, TCP_KEEPINTVL, &optval, sizeof(optval)); } -JNIEXPORT void Java_io_netty_channel_epoll_Native_setTcpKeepCnt(JNIEnv *env, jclass clazz, jint fd, jint optval) { +JNIEXPORT void Java_io_netty_channel_epoll_Native_setTcpKeepCnt(JNIEnv* env, jclass clazz, jint fd, jint optval) { setOption(env, fd, SOL_TCP, TCP_KEEPCNT, &optval, sizeof(optval)); } -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_isReuseAddresss(JNIEnv *env, jclass clazz, jint fd) { +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_isReuseAddresss(JNIEnv* env, jclass clazz, jint fd) { int optval; if (getOption(env, fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1) { return -1; @@ -1290,7 +1205,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_isReuseAddresss(JNIEnv return optval; } -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_isReusePort(JNIEnv *env, jclass clazz, jint fd) { +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_isReusePort(JNIEnv* env, jclass clazz, jint fd) { int optval; if (getOption(env, fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)) == -1) { return -1; @@ -1298,7 +1213,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_isReusePort(JNIEnv *en return optval; } -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_isTcpNoDelay(JNIEnv *env, jclass clazz, jint fd) { +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_isTcpNoDelay(JNIEnv* env, jclass clazz, jint fd) { int optval; if (getOption(env, fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval)) == -1) { return -1; @@ -1306,7 +1221,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_isTcpNoDelay(JNIEnv *e return optval; } -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getReceiveBufferSize(JNIEnv * env, jclass clazz, jint fd) { +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getReceiveBufferSize(JNIEnv* env, jclass clazz, jint fd) { int optval; if (getOption(env, fd, SOL_SOCKET, SO_RCVBUF, &optval, sizeof(optval)) == -1) { return -1; @@ -1314,7 +1229,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getReceiveBufferSize(J return optval; } -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getSendBufferSize(JNIEnv *env, jclass clazz, jint fd) { +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getSendBufferSize(JNIEnv* env, jclass clazz, jint fd) { int optval; if (getOption(env, fd, SOL_SOCKET, SO_SNDBUF, &optval, sizeof(optval)) == -1) { return -1; @@ -1322,7 +1237,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getSendBufferSize(JNIE return optval; } -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_isTcpCork(JNIEnv *env, jclass clazz, jint fd) { +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_isTcpCork(JNIEnv* env, jclass clazz, jint fd) { int optval; if (getOption(env, fd, SOL_TCP, TCP_CORK, &optval, sizeof(optval)) == -1) { return -1; @@ -1330,7 +1245,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_isTcpCork(JNIEnv *env, return optval; } -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getSoLinger(JNIEnv *env, jclass clazz, jint fd) { +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getSoLinger(JNIEnv* env, jclass clazz, jint fd) { struct linger optval; if (getOption(env, fd, SOL_SOCKET, SO_LINGER, &optval, sizeof(optval)) == -1) { return -1; @@ -1342,7 +1257,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getSoLinger(JNIEnv *en } } -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getTrafficClass(JNIEnv *env, jclass clazz, jint fd) { +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getTrafficClass(JNIEnv* env, jclass clazz, jint fd) { int optval; if (getOption(env, fd, IPPROTO_IP, IP_TOS, &optval, sizeof(optval)) == -1) { return -1; @@ -1350,7 +1265,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getTrafficClass(JNIEnv return optval; } -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_isBroadcast(JNIEnv *env, jclass clazz, jint fd) { +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_isBroadcast(JNIEnv* env, jclass clazz, jint fd) { int optval; if (getOption(env, fd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) == -1) { return -1; @@ -1358,7 +1273,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_isBroadcast(JNIEnv *en return optval; } -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getTcpKeepIdle(JNIEnv *env, jclass clazz, jint fd) { +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getTcpKeepIdle(JNIEnv* env, jclass clazz, jint fd) { int optval; if (getOption(env, fd, SOL_TCP, TCP_KEEPIDLE, &optval, sizeof(optval)) == -1) { return -1; @@ -1366,7 +1281,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getTcpKeepIdle(JNIEnv return optval; } -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getTcpKeepIntvl(JNIEnv *env, jclass clazz, jint fd) { +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getTcpKeepIntvl(JNIEnv* env, jclass clazz, jint fd) { int optval; if (getOption(env, fd, SOL_TCP, TCP_KEEPINTVL, &optval, sizeof(optval)) == -1) { return -1; @@ -1374,7 +1289,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getTcpKeepIntvl(JNIEnv return optval; } -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getTcpKeepCnt(JNIEnv *env, jclass clazz, jint fd) { +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getTcpKeepCnt(JNIEnv* env, jclass clazz, jint fd) { int optval; if (getOption(env, fd, SOL_TCP, TCP_KEEPCNT, &optval, sizeof(optval)) == -1) { return -1; @@ -1382,7 +1297,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getTcpKeepCnt(JNIEnv * return optval; } -JNIEXPORT jstring JNICALL Java_io_netty_channel_epoll_Native_kernelVersion(JNIEnv *env, jclass clazz) { +JNIEXPORT jstring JNICALL Java_io_netty_channel_epoll_Native_kernelVersion(JNIEnv* env, jclass clazz) { struct utsname name; int res = uname(&name); @@ -1394,19 +1309,42 @@ JNIEXPORT jstring JNICALL Java_io_netty_channel_epoll_Native_kernelVersion(JNIEn return NULL; } -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_iovMax(JNIEnv *env, jclass clazz) { +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_iovMax(JNIEnv* env, jclass clazz) { return IOV_MAX; } -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_uioMaxIov(JNIEnv *env, jclass clazz) { +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_uioMaxIov(JNIEnv* env, jclass clazz) { return UIO_MAXIOV; } - -JNIEXPORT jboolean JNICALL Java_io_netty_channel_epoll_Native_isSupportingSendmmsg(JNIEnv *env, jclass clazz) { +JNIEXPORT jboolean JNICALL Java_io_netty_channel_epoll_Native_isSupportingSendmmsg(JNIEnv* env, jclass clazz) { if (sendmmsg) { return JNI_TRUE; } return JNI_FALSE; } +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_errnoEBADF(JNIEnv* env, jclass clazz) { + return EBADF; +} + +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_errnoEPIPE(JNIEnv* env, jclass clazz) { + return EPIPE; +} + +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_errnoEAGAIN(JNIEnv* env, jclass clazz) { + return EAGAIN; +} + +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_errnoEWOULDBLOCK(JNIEnv* env, jclass clazz) { + return EWOULDBLOCK; +} + +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_errnoEINPROGRESS(JNIEnv* env, jclass clazz) { + return EINPROGRESS; +} + +JNIEXPORT jstring JNICALL Java_io_netty_channel_epoll_Native_strError(JNIEnv* env, jclass clazz, jint error) { + char* err = strerror(error); + return (*env)->NewStringUTF(env, err); +} diff --git a/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.h b/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.h index 949ae092b0..58e82bfb8f 100644 --- a/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.h +++ b/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.h @@ -46,29 +46,29 @@ jint Java_io_netty_channel_epoll_Native_epollWait(JNIEnv * env, jclass clazz, ji void Java_io_netty_channel_epoll_Native_epollCtlAdd(JNIEnv * env, jclass clazz, jint efd, jint fd, jint flags, jint id); void Java_io_netty_channel_epoll_Native_epollCtlMod(JNIEnv * env, jclass clazz, jint efd, jint fd, jint flags, jint id); void Java_io_netty_channel_epoll_Native_epollCtlDel(JNIEnv * env, jclass clazz, jint efd, jint fd); -jint Java_io_netty_channel_epoll_Native_write(JNIEnv * env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit); -jint Java_io_netty_channel_epoll_Native_writeAddress(JNIEnv * env, jclass clazz, jint fd, jlong address, jint pos, jint limit); -jlong Java_io_netty_channel_epoll_Native_writev(JNIEnv * env, jclass clazz, jint fd, jobjectArray buffers, jint offset, jint length); -jlong Java_io_netty_channel_epoll_Native_writevAddresses(JNIEnv * env, jclass clazz, jint fd, jlong memoryAddress, jint length); +jint Java_io_netty_channel_epoll_Native_write0(JNIEnv * env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit); +jint Java_io_netty_channel_epoll_Native_writeAddress0(JNIEnv * env, jclass clazz, jint fd, jlong address, jint pos, jint limit); +jlong Java_io_netty_channel_epoll_Native_writev0(JNIEnv * env, jclass clazz, jint fd, jobjectArray buffers, jint offset, jint length); +jlong Java_io_netty_channel_epoll_Native_writevAddresses0(JNIEnv * env, jclass clazz, jint fd, jlong memoryAddress, jint length); jint Java_io_netty_channel_epoll_Native_sendTo(JNIEnv * env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit, jbyteArray address, jint scopeId, jint port); jint Java_io_netty_channel_epoll_Native_sendToAddress(JNIEnv * env, jclass clazz, jint fd, jlong memoryAddress, jint pos, jint limit, jbyteArray address, jint scopeId, jint port); jint Java_io_netty_channel_epoll_Native_sendToAddresses(JNIEnv * env, jclass clazz, jint fd, jlong memoryAddress, jint length, jbyteArray address, jint scopeId, jint port); jint Java_io_netty_channel_epoll_Native_sendmmsg(JNIEnv * env, jclass clazz, jint fd, jobjectArray packets, jint offset, jint len); -jint Java_io_netty_channel_epoll_Native_read(JNIEnv * env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit); -jint Java_io_netty_channel_epoll_Native_readAddress(JNIEnv * env, jclass clazz, jint fd, jlong address, jint pos, jint limit); +jint Java_io_netty_channel_epoll_Native_read0(JNIEnv * env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit); +jint Java_io_netty_channel_epoll_Native_readAddress0(JNIEnv * env, jclass clazz, jint fd, jlong address, jint pos, jint limit); jobject Java_io_netty_channel_epoll_Native_recvFrom(JNIEnv * env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit); jobject Java_io_netty_channel_epoll_Native_recvFromAddress(JNIEnv * env, jclass clazz, jint fd, jlong address, jint pos, jint limit); -void JNICALL Java_io_netty_channel_epoll_Native_close(JNIEnv * env, jclass clazz, jint fd); -void Java_io_netty_channel_epoll_Native_shutdown(JNIEnv * env, jclass clazz, jint fd, jboolean read, jboolean write); +jint Java_io_netty_channel_epoll_Native_close0(JNIEnv * env, jclass clazz, jint fd); +jint Java_io_netty_channel_epoll_Native_shutdown0(JNIEnv * env, jclass clazz, jint fd, jboolean read, jboolean write); jint Java_io_netty_channel_epoll_Native_socketStream(JNIEnv * env, jclass clazz); jint Java_io_netty_channel_epoll_Native_socketDgram(JNIEnv * env, jclass clazz); -void Java_io_netty_channel_epoll_Native_bind(JNIEnv * env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port); -void Java_io_netty_channel_epoll_Native_listen(JNIEnv * env, jclass clazz, jint fd, jint backlog); -jboolean Java_io_netty_channel_epoll_Native_connect(JNIEnv * env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port); -jboolean Java_io_netty_channel_epoll_Native_finishConnect(JNIEnv * env, jclass clazz, jint fd); -jint Java_io_netty_channel_epoll_Native_accept(JNIEnv * env, jclass clazz, jint fd); +jint Java_io_netty_channel_epoll_Native_bind(JNIEnv * env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port); +jint Java_io_netty_channel_epoll_Native_listen0(JNIEnv * env, jclass clazz, jint fd, jint backlog); +jint Java_io_netty_channel_epoll_Native_connect(JNIEnv * env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port); +jint Java_io_netty_channel_epoll_Native_finishConnect0(JNIEnv * env, jclass clazz, jint fd); +jint Java_io_netty_channel_epoll_Native_accept0(JNIEnv * env, jclass clazz, jint fd); jlong Java_io_netty_channel_epoll_Native_sendfile0(JNIEnv *env, jclass clazz, jint fd, jobject fileRegion, jlong base_off, jlong off, jlong len); jbyteArray Java_io_netty_channel_epoll_Native_remoteAddress0(JNIEnv * env, jclass clazz, jint fd); jbyteArray Java_io_netty_channel_epoll_Native_localAddress0(JNIEnv * env, jclass clazz, jint fd); @@ -103,3 +103,10 @@ jstring Java_io_netty_channel_epoll_Native_kernelVersion(JNIEnv *env, jclass cla jint Java_io_netty_channel_epoll_Native_iovMax(JNIEnv *env, jclass clazz); jint Java_io_netty_channel_epoll_Native_uioMaxIov(JNIEnv *env, jclass clazz); jboolean Java_io_netty_channel_epoll_Native_isSupportingSendmmsg(JNIEnv *env, jclass clazz); + +jint Java_io_netty_channel_epoll_Native_errnoEBADF(JNIEnv *env, jclass clazz); +jint Java_io_netty_channel_epoll_Native_errnoEPIPE(JNIEnv *env, jclass clazz); +jint Java_io_netty_channel_epoll_Native_errnoEAGAIN(JNIEnv *env, jclass clazz); +jint Java_io_netty_channel_epoll_Native_errnoEWOULDBLOCK(JNIEnv *env, jclass clazz); +jint Java_io_netty_channel_epoll_Native_errnoEINPROGRESS(JNIEnv *env, jclass clazz); +jstring Java_io_netty_channel_epoll_Native_strError(JNIEnv *env, jclass clazz, jint err); diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/Native.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/Native.java index 6c661c8788..c339b485f1 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/Native.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/Native.java @@ -18,6 +18,7 @@ package io.netty.channel.epoll; import io.netty.channel.ChannelException; import io.netty.channel.DefaultFileRegion; +import io.netty.util.internal.EmptyArrays; import io.netty.util.internal.NativeLibraryLoader; import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.SystemPropertyUtil; @@ -28,6 +29,7 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.nio.ByteBuffer; +import java.nio.channels.ClosedChannelException; import java.util.Locale; /** @@ -36,8 +38,6 @@ import java.util.Locale; * Internal usage only! */ final class Native { - private static final byte[] IPV4_MAPPED_IPV6_PREFIX = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0xff, (byte) 0xff }; static { String name = SystemPropertyUtil.get("os.name").toLowerCase(Locale.UK).trim(); @@ -56,6 +56,78 @@ final class Native { public static final int UIO_MAX_IOV = uioMaxIov(); public static final boolean IS_SUPPORTING_SENDMMSG = isSupportingSendmmsg(); + private static final byte[] IPV4_MAPPED_IPV6_PREFIX = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0xff, (byte) 0xff }; + + // As all our JNI methods return -errno on error we need to compare with the negative errno codes. + private static final int ERRNO_EBADF_NEGATIVE = -errnoEBADF(); + private static final int ERRNO_EPIPE_NEGATIVE = -errnoEPIPE(); + private static final int ERRNO_EAGAIN_NEGATIVE = -errnoEAGAIN(); + private static final int ERRNO_EWOULDBLOCK_NEGATIVE = -errnoEWOULDBLOCK(); + private static final int ERRNO_EINPROGRESS_NEGATIVE = -errnoEINPROGRESS(); + + /** + * Holds the mappings for errno codes to String messages. + * This eliminates the need to call back into JNI to get the right String message on an exception + * and thus is faster. + * + * The array length of 1024 should be more then enough because errno.h only holds < 200 codes. + */ + private static final String[] ERRORS = new String[1024]; // + + // Pre-instantiated exceptions which does not need any stacktrace and + // can be thrown multiple times for performance reasons. + private static final ClosedChannelException CLOSED_CHANNEL_EXCEPTION; + private static final IOException CONNECTION_RESET_EXCEPTION_WRITE; + private static final IOException CONNECTION_RESET_EXCEPTION_WRITEV; + private static final IOException CONNECTION_RESET_EXCEPTION_READ; + private static final IOException CONNECTION_RESET_EXCEPTION_SENDFILE; + private static final IOException CONNECTION_RESET_EXCEPTION_SENDTO; + private static final IOException CONNECTION_RESET_EXCEPTION_SENDMSG; + private static final IOException CONNECTION_RESET_EXCEPTION_SENDMMSG; + + static { + for (int i = 0; i < ERRORS.length; i++) { + // This is ok as strerror returns 'Unknown error i' when the message is not known. + ERRORS[i] = strError(i); + } + CONNECTION_RESET_EXCEPTION_WRITE = newConnectionResetException("write"); + CONNECTION_RESET_EXCEPTION_WRITEV = newConnectionResetException("writev"); + CONNECTION_RESET_EXCEPTION_READ = newConnectionResetException("read"); + CONNECTION_RESET_EXCEPTION_SENDFILE = newConnectionResetException("sendfile"); + CONNECTION_RESET_EXCEPTION_SENDTO = newConnectionResetException("sendto"); + CONNECTION_RESET_EXCEPTION_SENDMSG = newConnectionResetException("sendmsg"); + CONNECTION_RESET_EXCEPTION_SENDMMSG = newConnectionResetException("sendmmsg"); + CLOSED_CHANNEL_EXCEPTION = new ClosedChannelException(); + CLOSED_CHANNEL_EXCEPTION.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE); + } + + private static IOException newConnectionResetException(String method) { + IOException exception = newIOException(method, ERRNO_EPIPE_NEGATIVE); + exception.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE); + return exception; + } + + private static IOException newIOException(String method, int err) { + return new IOException(method + "() failed: " + ERRORS[-err]); + } + + private static int ioResult(String method, int err, IOException resetCause) throws IOException { + // network stack saturated... try again later + if (err == ERRNO_EAGAIN_NEGATIVE || err == ERRNO_EWOULDBLOCK_NEGATIVE) { + return 0; + } + if (err == ERRNO_EPIPE_NEGATIVE) { + throw resetCause; + } + if (err == ERRNO_EBADF_NEGATIVE) { + throw CLOSED_CHANNEL_EXCEPTION; + } + // TODO: We could even go futher and use a pre-instanced IOException for the other error codes, but for + // all other errors it may be better to just include a stacktrace. + throw newIOException(method, err); + } + public static native int eventFd(); public static native void eventFdWrite(int fd, long value); public static native void eventFdRead(int fd); @@ -65,18 +137,89 @@ final class Native { public static native void epollCtlMod(int efd, final int fd, final int flags, final int id); public static native void epollCtlDel(int efd, final int fd); + private static native int errnoEBADF(); + private static native int errnoEPIPE(); + private static native int errnoEAGAIN(); + private static native int errnoEWOULDBLOCK(); + private static native int errnoEINPROGRESS(); + private static native String strError(int err); + // File-descriptor operations - public static native void close(int fd) throws IOException; + public static void close(int fd) throws IOException { + int res = close0(fd); + if (res < 0) { + throw newIOException("close", res); + } + } - public static native int write(int fd, ByteBuffer buf, int pos, int limit) throws IOException; - public static native int writeAddress(int fd, long address, int pos, int limit) throws IOException; + private static native int close0(int fd); - public static native long writev(int fd, ByteBuffer[] buffers, int offset, int length) throws IOException; - public static native long writevAddresses(int fd, long memoryAddress, int length) - throws IOException; + public static int write(int fd, ByteBuffer buf, int pos, int limit) throws IOException { + int res = write0(fd, buf, pos, limit); + if (res >= 0) { + return res; + } + return ioResult("write", res, CONNECTION_RESET_EXCEPTION_WRITE); + } - public static native int read(int fd, ByteBuffer buf, int pos, int limit) throws IOException; - public static native int readAddress(int fd, long address, int pos, int limit) throws IOException; + private static native int write0(int fd, ByteBuffer buf, int pos, int limit); + + public static int writeAddress(int fd, long address, int pos, int limit) throws IOException { + int res = writeAddress0(fd, address, pos, limit); + if (res >= 0) { + return res; + } + return ioResult("write", res, CONNECTION_RESET_EXCEPTION_WRITE); + } + + private static native int writeAddress0(int fd, long address, int pos, int limit); + + public static long writev(int fd, ByteBuffer[] buffers, int offset, int length) throws IOException { + long res = writev0(fd, buffers, offset, length); + if (res >= 0) { + return res; + } + return ioResult("writev", (int) res, CONNECTION_RESET_EXCEPTION_WRITEV); + } + + private static native long writev0(int fd, ByteBuffer[] buffers, int offset, int length); + + public static long writevAddresses(int fd, long memoryAddress, int length) + throws IOException { + long res = writevAddresses0(fd, memoryAddress, length); + if (res >= 0) { + return res; + } + return ioResult("writev", (int) res, CONNECTION_RESET_EXCEPTION_WRITEV); + } + + private static native long writevAddresses0(int fd, long memoryAddress, int length); + + public static int read(int fd, ByteBuffer buf, int pos, int limit) throws IOException { + int res = read0(fd, buf, pos, limit); + if (res > 0) { + return res; + } + if (res == 0) { + return -1; + } + return ioResult("read", res, CONNECTION_RESET_EXCEPTION_READ); + } + + private static native int read0(int fd, ByteBuffer buf, int pos, int limit); + + public static int readAddress(int fd, long address, int pos, int limit) throws IOException { + int res = readAddress0(fd, address, pos, limit); + if (res > 0) { + return res; + } + if (res == 0) { + return -1; + } + return ioResult("read", res, CONNECTION_RESET_EXCEPTION_READ); + } + + private static native int readAddress0(int fd, long address, int pos, int limit); public static long sendfile( int dest, DefaultFileRegion src, long baseOffset, long offset, long length) throws IOException { @@ -84,7 +227,11 @@ final class Native { // the FileChannel field directly via JNI src.open(); - return sendfile0(dest, src, baseOffset, offset, length); + long res = sendfile0(dest, src, baseOffset, offset, length); + if (res >= 0) { + return res; + } + return ioResult("sendfile", (int) res, CONNECTION_RESET_EXCEPTION_SENDFILE); } private static native long sendfile0( @@ -104,11 +251,15 @@ final class Native { scopeId = 0; address = ipv4MappedIpv6Address(addr.getAddress()); } - return sendTo(fd, buf, pos, limit, address, scopeId, port); + int res = sendTo0(fd, buf, pos, limit, address, scopeId, port); + if (res >= 0) { + return res; + } + return ioResult("sendfile", res, CONNECTION_RESET_EXCEPTION_SENDTO); } - private static native int sendTo( - int fd, ByteBuffer buf, int pos, int limit, byte[] address, int scopeId, int port) throws IOException; + private static native int sendTo0( + int fd, ByteBuffer buf, int pos, int limit, byte[] address, int scopeId, int port); public static int sendToAddress( int fd, long memoryAddress, int pos, int limit, InetAddress addr, int port) throws IOException { @@ -124,11 +275,15 @@ final class Native { scopeId = 0; address = ipv4MappedIpv6Address(addr.getAddress()); } - return sendToAddress(fd, memoryAddress, pos, limit, address, scopeId, port); + int res = sendToAddress0(fd, memoryAddress, pos, limit, address, scopeId, port); + if (res >= 0) { + return res; + } + return ioResult("sendto", res, CONNECTION_RESET_EXCEPTION_SENDTO); } - private static native int sendToAddress( - int fd, long memoryAddress, int pos, int limit, byte[] address, int scopeId, int port) throws IOException; + private static native int sendToAddress0( + int fd, long memoryAddress, int pos, int limit, byte[] address, int scopeId, int port); public static int sendToAddresses( int fd, long memoryAddress, int length, InetAddress addr, int port) throws IOException { @@ -144,11 +299,15 @@ final class Native { scopeId = 0; address = ipv4MappedIpv6Address(addr.getAddress()); } - return sendToAddresses(fd, memoryAddress, length, address, scopeId, port); + int res = sendToAddresses(fd, memoryAddress, length, address, scopeId, port); + if (res >= 0) { + return res; + } + return ioResult("sendmsg", res, CONNECTION_RESET_EXCEPTION_SENDMSG); } private static native int sendToAddresses( - int fd, long memoryAddress, int length, byte[] address, int scopeId, int port) throws IOException; + int fd, long memoryAddress, int length, byte[] address, int scopeId, int port); public static native EpollDatagramChannel.DatagramSocketAddress recvFrom( int fd, ByteBuffer buf, int pos, int limit) throws IOException; @@ -156,50 +315,87 @@ final class Native { public static native EpollDatagramChannel.DatagramSocketAddress recvFromAddress( int fd, long memoryAddress, int pos, int limit) throws IOException; - public static native int sendmmsg( - int fd, NativeDatagramPacketArray.NativeDatagramPacket[] msgs, int offset, int len) throws IOException; + public static int sendmmsg( + int fd, NativeDatagramPacketArray.NativeDatagramPacket[] msgs, int offset, int len) throws IOException { + int res = sendmmsg0(fd, msgs, offset, len); + if (res >= 0) { + return res; + } + return ioResult("sendmmsg", res, CONNECTION_RESET_EXCEPTION_SENDMMSG); + } + + private static native int sendmmsg0( + int fd, NativeDatagramPacketArray.NativeDatagramPacket[] msgs, int offset, int len); private static native boolean isSupportingSendmmsg(); // socket operations public static int socketStreamFd() { - try { - return socketStream(); - } catch (IOException e) { - throw new ChannelException(e); + int res = socketStream(); + if (res < 0) { + throw new ChannelException(newIOException("socket", res)); } + return res; } public static int socketDgramFd() { - try { - return socketDgram(); - } catch (IOException e) { - throw new ChannelException(e); + int res = socketDgram(); + if (res < 0) { + throw new ChannelException(newIOException("socket", res)); } + return res; } - private static native int socketStream() throws IOException; - private static native int socketDgram() throws IOException; + + private static native int socketStream(); + private static native int socketDgram(); public static void bind(int fd, InetAddress addr, int port) throws IOException { NativeInetAddress address = toNativeInetAddress(addr); - bind(fd, address.address, address.scopeId, port); + int res = bind(fd, address.address, address.scopeId, port); + if (res < 0) { + throw newIOException("bind", res); + } } - static byte[] ipv4MappedIpv6Address(byte[] ipv4) { - byte[] address = new byte[16]; - System.arraycopy(IPV4_MAPPED_IPV6_PREFIX, 0, address, 0, IPV4_MAPPED_IPV6_PREFIX.length); - System.arraycopy(ipv4, 0, address, 12, ipv4.length); - return address; + private static native int bind(int fd, byte[] address, int scopeId, int port); + + public static void listen(int fd, int backlog) throws IOException { + int res = listen0(fd, backlog); + if (res < 0) { + throw newIOException("listen", res); + } } - public static native void bind(int fd, byte[] address, int scopeId, int port) throws IOException; - public static native void listen(int fd, int backlog) throws IOException; + private static native int listen0(int fd, int backlog); + public static boolean connect(int fd, InetAddress addr, int port) throws IOException { NativeInetAddress address = toNativeInetAddress(addr); - return connect(fd, address.address, address.scopeId, port); + int res = connect(fd, address.address, address.scopeId, port); + if (res < 0) { + if (res == ERRNO_EINPROGRESS_NEGATIVE) { + // connect not complete yet need to wait for EPOLLOUT event + return false; + } + throw newIOException("connect", res); + } + return true; } - public static native boolean connect(int fd, byte[] address, int scopeId, int port) throws IOException; - public static native boolean finishConnect(int fd) throws IOException; + + private static native int connect(int fd, byte[] address, int scopeId, int port); + + public static boolean finishConnect(int fd) throws IOException { + int res = finishConnect0(fd); + if (res < 0) { + if (res == ERRNO_EINPROGRESS_NEGATIVE) { + // connect still in progress + return false; + } + throw newIOException("getsockopt", res); + } + return true; + } + + private static native int finishConnect0(int fd); public static InetSocketAddress remoteAddress(int fd) { byte[] addr = remoteAddress0(fd); @@ -256,8 +452,29 @@ final class Native { private static native byte[] remoteAddress0(int fd); private static native byte[] localAddress0(int fd); - public static native int accept(int fd) throws IOException; - public static native void shutdown(int fd, boolean read, boolean write) throws IOException; + + public static int accept(int fd) throws IOException { + int res = accept0(fd); + if (res >= 0) { + return res; + } + if (res == ERRNO_EAGAIN_NEGATIVE || res == ERRNO_EWOULDBLOCK_NEGATIVE) { + // Everything consumed so just return -1 here. + return -1; + } + throw newIOException("accept", res); + } + + private static native int accept0(int fd); + + public static void shutdown(int fd, boolean read, boolean write) throws IOException { + int res = shutdown0(fd, read, write); + if (res < 0) { + throw newIOException("shutdown", res); + } + } + + private static native int shutdown0(int fd, boolean read, boolean write); // Socket option operations public static native int getReceiveBufferSize(int fd); @@ -298,6 +515,13 @@ final class Native { } } + static byte[] ipv4MappedIpv6Address(byte[] ipv4) { + byte[] address = new byte[16]; + System.arraycopy(IPV4_MAPPED_IPV6_PREFIX, 0, address, 0, IPV4_MAPPED_IPV6_PREFIX.length); + System.arraycopy(ipv4, 0, address, 12, ipv4.length); + return address; + } + private static class NativeInetAddress { final byte[] address; final int scopeId;