Decouple Unix from Linux in Native Transport

Motivation:
transport-native-epoll is designed to be specific to Linux. However there is native code that can be extracted out and made to work on more Unix like distributions. There are a few steps to be completely decoupled but the first step is to extract out code that can run in a more general Unix environment from the Linux specific code base.

Modifications:
- Move all non-Linux specific stuff from Native.java into the io.netty.channel.unix package.
- io.netty.channel.unix.FileDescriptor will inherit all the native methods that are specific to file descriptors.
- io_netty_channel_epoll_Native.[c|h] will only have code that is specific to Linux.

Result:
Code is decoupled and design is streamlined in FileDescriptor.
This commit is contained in:
Scott Mitchell 2015-10-06 19:00:59 -07:00
parent 19658e9cd8
commit dbbdbe11a6
33 changed files with 2407 additions and 1909 deletions

View File

@ -41,48 +41,16 @@ jint Java_io_netty_channel_epoll_Native_epollWait0(JNIEnv* env, jclass clazz, ji
jint Java_io_netty_channel_epoll_Native_epollCtlAdd0(JNIEnv* env, jclass clazz, jint efd, jint fd, jint flags);
jint Java_io_netty_channel_epoll_Native_epollCtlMod0(JNIEnv* env, jclass clazz, jint efd, jint fd, jint flags);
jint Java_io_netty_channel_epoll_Native_epollCtlDel0(JNIEnv* env, jclass clazz, jint efd, jint fd);
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_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);
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);
jint Java_io_netty_channel_epoll_Native_socketDomain(JNIEnv* env, jclass clazz);
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_bindDomainSocket(JNIEnv* env, jclass clazz, jint fd, jstring address);
jint Java_io_netty_channel_epoll_Native_recvFd0(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_epoll_Native_sendFd0(JNIEnv* env, jclass clazz, jint socketFd, jint fd);
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_connectDomainSocket(JNIEnv* env, jclass clazz, jint fd, jstring address);
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, jbyteArray acceptedAddress);
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);
void Java_io_netty_channel_epoll_Native_setReuseAddress(JNIEnv* env, jclass clazz, jint fd, jint optval);
void Java_io_netty_channel_epoll_Native_setReusePort(JNIEnv* env, jclass clazz, jint fd, jint optval);
void Java_io_netty_channel_epoll_Native_setTcpNoDelay(JNIEnv* env, jclass clazz, jint fd, jint optval);
void Java_io_netty_channel_epoll_Native_setReceiveBufferSize(JNIEnv* env, jclass clazz, jint fd, jint optval);
void Java_io_netty_channel_epoll_Native_setSendBufferSize(JNIEnv* env, jclass clazz, jint fd, jint optval);
void Java_io_netty_channel_epoll_Native_setKeepAlive(JNIEnv* env, jclass clazz, jint fd, jint optval);
void Java_io_netty_channel_epoll_Native_setTcpCork(JNIEnv* env, jclass clazz, jint fd, jint optval);
void Java_io_netty_channel_epoll_Native_setTcpFastopen(JNIEnv* env, jclass clazz, jint fd, jint optval);
void Java_io_netty_channel_epoll_Native_setTcpNotSentLowAt(JNIEnv* env, jclass clazz, jint fd, jint optval);
void Java_io_netty_channel_epoll_Native_setSoLinger(JNIEnv* env, jclass clazz, jint fd, jint optval);
void Java_io_netty_channel_epoll_Native_setTrafficClass(JNIEnv* env, jclass clazz, jint fd, jint optval);
void Java_io_netty_channel_epoll_Native_setBroadcast(JNIEnv* env, jclass clazz, jint fd, jint optval);
void Java_io_netty_channel_epoll_Native_setTcpKeepIdle(JNIEnv* env, jclass clazz, jint fd, jint optval);
@ -92,19 +60,14 @@ void Java_io_netty_channel_epoll_Native_setIpFreeBind(JNIEnv* env, jclass clazz,
jint Java_io_netty_channel_epoll_Native_isReuseAddresss(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_epoll_Native_isReusePort(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_epoll_Native_isTcpNoDelay(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_epoll_Native_getReceiveBufferSize(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_epoll_Native_getSendBufferSize(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_epoll_Native_isTcpCork(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_epoll_Native_getTcpNotSentLowAt(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_epoll_Native_getSoLinger(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_epoll_Native_getTrafficClass(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_epoll_Native_isBroadcast(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_epoll_Native_getTcpKeepIdle(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_epoll_Native_getTcpKeepIntvl(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_epoll_Native_getTcpKeepCnt(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_epoll_Native_getSoError(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_epoll_Native_isIpFreeBind(JNIEnv* env, jclass clazz, jint fd);
void Java_io_netty_channel_epoll_Native_tcpInfo0(JNIEnv* env, jclass clazz, jint fd, jintArray array);
jstring Java_io_netty_channel_epoll_Native_kernelVersion(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_epoll_Native_iovMax(JNIEnv* env, jclass clazz);
@ -113,15 +76,6 @@ jlong Java_io_netty_channel_epoll_Native_ssizeMax(JNIEnv* env, jclass clazz);
jboolean Java_io_netty_channel_epoll_Native_isSupportingSendmmsg(JNIEnv* env, jclass clazz);
jboolean Java_io_netty_channel_epoll_Native_isSupportingTcpFastopen(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_epoll_Native_errnoENOTCONN(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_errnoECONNRESET(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);
jint Java_io_netty_channel_epoll_Native_epollin(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_epoll_Native_epollout(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_epoll_Native_epollrdhup(JNIEnv* env, jclass clazz);
@ -130,7 +84,6 @@ jint Java_io_netty_channel_epoll_Native_epollerr(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_epoll_Native_sizeofEpollEvent(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_epoll_Native_offsetofEpollData(JNIEnv* env, jclass clazz);
jlong Java_io_netty_channel_epoll_Native_pipe0(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_epoll_Native_splice0(JNIEnv* env, jclass clazz, jint fd, jlong offIn, jint fdOut, jlong offOut, jlong len);
jint Java_io_netty_channel_epoll_Native_tcpMd5SigMaxKeyLen(JNIEnv* env, jclass clazz);

View File

@ -0,0 +1,191 @@
/*
* Copyright 2015 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include "netty_unix_errors.h"
#include "io_netty_channel_unix_Errors.h"
static jclass runtimeExceptionClass = NULL;
static jclass channelExceptionClass = NULL;
static jclass ioExceptionClass = NULL;
static jclass closedChannelExceptionClass = NULL;
static jmethodID closedChannelExceptionMethodId = NULL;
/** Notice: every usage of exceptionMessage needs to release the allocated memory for the sequence of char */
static char* exceptionMessage(char* msg, int error) {
if (error < 0) {
// some functions return negative values
// and it's hard to keep track of when to send -error and when not
// this will just take care when things are forgotten
// what would generate a proper error
error = error * -1;
}
//strerror is returning a constant, so no need to free anything coming from strerror
char* err = strerror(error);
char* result = malloc(strlen(msg) + strlen(err) + 1);
strcpy(result, msg);
strcat(result, err);
return result;
}
// Exported C methods
void netty_unix_errors_throwRuntimeException(JNIEnv* env, char* message) {
(*env)->ThrowNew(env, runtimeExceptionClass, message);
}
void netty_unix_errors_throwRuntimeExceptionErrorNo(JNIEnv* env, char* message, int errorNumber) {
char* allocatedMessage = exceptionMessage(message, errorNumber);
(*env)->ThrowNew(env, runtimeExceptionClass, allocatedMessage);
free(allocatedMessage);
}
void netty_unix_errors_throwChannelExceptionErrorNo(JNIEnv* env, char* message, int errorNumber) {
char* allocatedMessage = exceptionMessage(message, errorNumber);
(*env)->ThrowNew(env, channelExceptionClass, allocatedMessage);
free(allocatedMessage);
}
void netty_unix_errors_throwIOException(JNIEnv* env, char* message) {
(*env)->ThrowNew(env, ioExceptionClass, message);
}
void netty_unix_errors_throwIOExceptionErrorNo(JNIEnv* env, char* message, int errorNumber) {
char* allocatedMessage = exceptionMessage(message, errorNumber);
(*env)->ThrowNew(env, ioExceptionClass, allocatedMessage);
free(allocatedMessage);
}
void netty_unix_errors_throwClosedChannelException(JNIEnv* env) {
jobject exception = (*env)->NewObject(env, closedChannelExceptionClass, closedChannelExceptionMethodId);
(*env)->Throw(env, exception);
}
void netty_unix_errors_throwOutOfMemoryError(JNIEnv* env) {
jclass exceptionClass = (*env)->FindClass(env, "java/lang/OutOfMemoryError");
(*env)->ThrowNew(env, exceptionClass, "");
}
jint netty_unix_errors_JNI_OnLoad(JNIEnv* env) {
jclass localRuntimeExceptionClass = (*env)->FindClass(env, "java/lang/RuntimeException");
if (localRuntimeExceptionClass == NULL) {
// pending exception...
return JNI_ERR;
}
runtimeExceptionClass = (jclass) (*env)->NewGlobalRef(env, localRuntimeExceptionClass);
if (runtimeExceptionClass == NULL) {
// out-of-memory!
netty_unix_errors_throwOutOfMemoryError(env);
return JNI_ERR;
}
jclass localChannelExceptionClass = (*env)->FindClass(env, "io/netty/channel/ChannelException");
if (localChannelExceptionClass == NULL) {
// pending exception...
return JNI_ERR;
}
channelExceptionClass = (jclass) (*env)->NewGlobalRef(env, localChannelExceptionClass);
if (channelExceptionClass == NULL) {
// out-of-memory!
netty_unix_errors_throwOutOfMemoryError(env);
return JNI_ERR;
}
// cache classes that are used within other jni methods for performance reasons
jclass localClosedChannelExceptionClass = (*env)->FindClass(env, "java/nio/channels/ClosedChannelException");
if (localClosedChannelExceptionClass == NULL) {
// pending exception...
return JNI_ERR;
}
closedChannelExceptionClass = (jclass) (*env)->NewGlobalRef(env, localClosedChannelExceptionClass);
if (closedChannelExceptionClass == NULL) {
// out-of-memory!
netty_unix_errors_throwOutOfMemoryError(env);
return JNI_ERR;
}
closedChannelExceptionMethodId = (*env)->GetMethodID(env, closedChannelExceptionClass, "<init>", "()V");
if (closedChannelExceptionMethodId == NULL) {
netty_unix_errors_throwRuntimeException(env, "failed to get method ID: ClosedChannelException.<init>()");
return JNI_ERR;
}
jclass localIoExceptionClass = (*env)->FindClass(env, "java/io/IOException");
if (localIoExceptionClass == NULL) {
// pending exception...
return JNI_ERR;
}
ioExceptionClass = (jclass) (*env)->NewGlobalRef(env, localIoExceptionClass);
if (ioExceptionClass == NULL) {
// out-of-memory!
netty_unix_errors_throwOutOfMemoryError(env);
return JNI_ERR;
}
return JNI_VERSION_1_6;
}
void netty_unix_errors_JNI_OnUnLoad(JNIEnv* env) {
// delete global references so the GC can collect them
if (runtimeExceptionClass != NULL) {
(*env)->DeleteGlobalRef(env, runtimeExceptionClass);
runtimeExceptionClass = NULL;
}
if (channelExceptionClass != NULL) {
(*env)->DeleteGlobalRef(env, channelExceptionClass);
channelExceptionClass = NULL;
}
if (ioExceptionClass != NULL) {
(*env)->DeleteGlobalRef(env, ioExceptionClass);
ioExceptionClass = NULL;
}
if (closedChannelExceptionClass != NULL) {
(*env)->DeleteGlobalRef(env, closedChannelExceptionClass);
closedChannelExceptionClass = NULL;
}
}
// Exported JNI Methods
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Errors_errnoENOTCONN(JNIEnv* env, jclass clazz) {
return ENOTCONN;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Errors_errnoEBADF(JNIEnv* env, jclass clazz) {
return EBADF;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Errors_errnoEPIPE(JNIEnv* env, jclass clazz) {
return EPIPE;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Errors_errnoECONNRESET(JNIEnv* env, jclass clazz) {
return ECONNRESET;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Errors_errnoEAGAIN(JNIEnv* env, jclass clazz) {
return EAGAIN;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Errors_errnoEWOULDBLOCK(JNIEnv* env, jclass clazz) {
return EWOULDBLOCK;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Errors_errnoEINPROGRESS(JNIEnv* env, jclass clazz) {
return EINPROGRESS;
}
JNIEXPORT jstring JNICALL Java_io_netty_channel_unix_Errors_strError(JNIEnv* env, jclass clazz, jint error) {
return (*env)->NewStringUTF(env, strerror(error));
}

View File

@ -0,0 +1,31 @@
/*
* Copyright 2015 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#ifndef IO_NETTY_ERROR_UNIX_H_
#define IO_NETTY_ERROR_UNIX_H_
#include <jni.h>
// Exported JNI methods
jint Java_io_netty_channel_unix_Errors_errnoENOTCONN(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_unix_Errors_errnoEBADF(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_unix_Errors_errnoEPIPE(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_unix_Errors_errnoECONNRESET(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_unix_Errors_errnoEAGAIN(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_unix_Errors_errnoEWOULDBLOCK(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_unix_Errors_errnoEINPROGRESS(JNIEnv* env, jclass clazz);
jstring Java_io_netty_channel_unix_Errors_strError(JNIEnv* env, jclass clazz, jint error);
#endif /* IO_NETTY_ERROR_UNIX_H_ */

View File

@ -13,12 +13,126 @@
* License for the specific language governing permissions and limitations
* under the License.
*/
#include <jni.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/uio.h>
#include "netty_unix_errors.h"
#include "netty_unix_filedescriptor.h"
#include "io_netty_channel_unix_FileDescriptor.h"
static jmethodID posId = NULL;
static jmethodID limitId = NULL;
static jfieldID posFieldId = NULL;
static jfieldID limitFieldId = NULL;
// Optional external methods
extern int pipe2(int pipefd[2], int flags) __attribute__((weak));
static 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));
if (res < 0) {
return -err;
}
return (jint) res;
}
static 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));
if (res < 0) {
return -err;
}
return (jlong) res;
}
static jint _read(JNIEnv* env, jclass clazz, jint fd, void* buffer, jint pos, jint limit) {
ssize_t res;
int err;
do {
res = read(fd, buffer + pos, (size_t) (limit - pos));
// Keep on reading if we was interrupted
} while (res == -1 && ((err = errno) == EINTR));
if (res < 0) {
return -err;
}
return (jint) res;
}
jint netty_unix_filedescriptor_JNI_OnLoad(JNIEnv* env) {
void* mem = malloc(1);
if (mem == NULL) {
netty_unix_errors_throwOutOfMemoryError(env);
return JNI_ERR;
}
jobject directBuffer = (*env)->NewDirectByteBuffer(env, mem, 1);
if (directBuffer == NULL) {
free(mem);
netty_unix_errors_throwOutOfMemoryError(env);
return JNI_ERR;
}
if ((*env)->GetDirectBufferAddress(env, directBuffer) == NULL) {
free(mem);
netty_unix_errors_throwRuntimeException(env, "failed to get direct buffer address");
return JNI_ERR;
}
jclass cls = (*env)->GetObjectClass(env, directBuffer);
// Get the method id for Buffer.position() and Buffer.limit(). These are used as fallback if
// it is not possible to obtain the position and limit using the fields directly.
posId = (*env)->GetMethodID(env, cls, "position", "()I");
if (posId == NULL) {
free(mem);
// position method was not found.. something is wrong so bail out
netty_unix_errors_throwRuntimeException(env, "failed to get method ID: ByteBuffer.position()");
return JNI_ERR;
}
limitId = (*env)->GetMethodID(env, cls, "limit", "()I");
if (limitId == NULL) {
free(mem);
// limit method was not found.. something is wrong so bail out
netty_unix_errors_throwRuntimeException(env, "failed to get method ID: ByteBuffer.limit()");
return JNI_ERR;
}
// Try to get the ids of the position and limit fields. We later then check if we was able
// to find them and if so use them get the position and limit of the buffer. This is
// much faster then call back into java via (*env)->CallIntMethod(...).
posFieldId = (*env)->GetFieldID(env, cls, "position", "I");
if (posFieldId == NULL) {
// this is ok as we can still use the method so just clear the exception
(*env)->ExceptionClear(env);
}
limitFieldId = (*env)->GetFieldID(env, cls, "limit", "I");
if (limitFieldId == NULL) {
// this is ok as we can still use the method so just clear the exception
(*env)->ExceptionClear(env);
}
free(mem);
}
void netty_unix_filedescriptor_JNI_OnUnLoad(JNIEnv* env) { }
JNIEXPORT int JNICALL Java_io_netty_channel_unix_FileDescriptor_close(JNIEnv* env, jclass clazz, jint fd) {
if (close(fd) < 0) {
return -errno;
@ -27,7 +141,6 @@ JNIEXPORT int JNICALL Java_io_netty_channel_unix_FileDescriptor_close(JNIEnv* en
}
JNIEXPORT int JNICALL Java_io_netty_channel_unix_FileDescriptor_open(JNIEnv* env, jclass clazz, jstring path) {
const char* f_path = (*env)->GetStringUTFChars(env, path, 0);
int res = open(f_path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
@ -38,3 +151,95 @@ JNIEXPORT int JNICALL Java_io_netty_channel_unix_FileDescriptor_open(JNIEnv* env
}
return res;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_FileDescriptor_write(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit) {
// We check that GetDirectBufferAddress will not return NULL in OnLoad
return _write(env, clazz, fd, (*env)->GetDirectBufferAddress(env, jbuffer), pos, limit);
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_FileDescriptor_writeAddress(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit) {
return _write(env, clazz, fd, (void*) address, pos, limit);
}
JNIEXPORT jlong JNICALL Java_io_netty_channel_unix_FileDescriptor_writevAddresses(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint length) {
struct iovec* iov = (struct iovec*) memoryAddress;
return _writev(env, clazz, fd, iov, length);
}
JNIEXPORT jlong JNICALL Java_io_netty_channel_unix_FileDescriptor_writev(JNIEnv* env, jclass clazz, jint fd, jobjectArray buffers, jint offset, jint length) {
struct iovec iov[length];
int iovidx = 0;
int i;
int num = offset + length;
for (i = offset; i < num; i++) {
jobject bufObj = (*env)->GetObjectArrayElement(env, buffers, i);
jint pos;
// Get the current position using the (*env)->GetIntField if possible and fallback
// to slower (*env)->CallIntMethod(...) if needed
if (posFieldId == NULL) {
pos = (*env)->CallIntMethod(env, bufObj, posId, NULL);
} else {
pos = (*env)->GetIntField(env, bufObj, posFieldId);
}
jint limit;
// Get the current limit using the (*env)->GetIntField if possible and fallback
// to slower (*env)->CallIntMethod(...) if needed
if (limitFieldId == NULL) {
limit = (*env)->CallIntMethod(env, bufObj, limitId, NULL);
} else {
limit = (*env)->GetIntField(env, bufObj, limitFieldId);
}
void* buffer = (*env)->GetDirectBufferAddress(env, bufObj);
// We check that GetDirectBufferAddress will not return NULL in OnLoad
iov[iovidx].iov_base = buffer + pos;
iov[iovidx].iov_len = (size_t) (limit - pos);
iovidx++;
// Explicit delete local reference as otherwise the local references will only be released once the native method returns.
// Also there may be a lot of these and JNI specification only specify that 16 must be able to be created.
//
// See https://github.com/netty/netty/issues/2623
(*env)->DeleteLocalRef(env, bufObj);
}
return _writev(env, clazz, fd, iov, length);
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_FileDescriptor_read(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit) {
// We check that GetDirectBufferAddress will not return NULL in OnLoad
return _read(env, clazz, fd, (*env)->GetDirectBufferAddress(env, jbuffer), pos, limit);
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_FileDescriptor_readAddress(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit) {
return _read(env, clazz, fd, (void*) address, pos, limit);
}
JNIEXPORT jlong JNICALL Java_io_netty_channel_unix_FileDescriptor_newPipe(JNIEnv* env, jclass clazz) {
int fd[2];
if (pipe2) {
// we can just use pipe2 and so save extra syscalls;
if (pipe2(fd, O_NONBLOCK) != 0) {
return -errno;
}
} else {
if (pipe(fd) == 0) {
if (fcntl(fd[0], F_SETFD, O_NONBLOCK) < 0) {
int err = errno;
close(fd[0]);
close(fd[1]);
return -err;
}
if (fcntl(fd[1], F_SETFD, O_NONBLOCK) < 0) {
int err = errno;
close(fd[0]);
close(fd[1]);
return -err;
}
} else {
return -errno;
}
}
// encode the fds into a 64 bit value
return (((jlong) fd[0]) << 32) | fd[1];
}

View File

@ -15,5 +15,16 @@
*/
#include <jni.h>
int Java_io_netty_channel_unix_FileDescriptor_close(JNIEnv* env, jclass clazz, jint fd);
int Java_io_netty_channel_unix_FileDescriptor_open(JNIEnv* env, jclass clazz, jstring path);
// Exported JNI Methods
jint Java_io_netty_channel_unix_FileDescriptor_close(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_unix_FileDescriptor_open(JNIEnv* env, jclass clazz, jstring path);
jlong Java_io_netty_channel_unix_FileDescriptor_newPipe(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_unix_FileDescriptor_write(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit);
jint Java_io_netty_channel_unix_FileDescriptor_writeAddress(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit);
jlong Java_io_netty_channel_unix_FileDescriptor_writev(JNIEnv* env, jclass clazz, jint fd, jobjectArray buffers, jint offset, jint length);
jlong Java_io_netty_channel_unix_FileDescriptor_writevAddresses(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint length);
jint Java_io_netty_channel_unix_FileDescriptor_read(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit);
jint Java_io_netty_channel_unix_FileDescriptor_readAddress(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit);

View File

@ -0,0 +1,715 @@
/*
* Copyright 2015 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include "netty_unix_errors.h"
#include "netty_unix_socket.h"
#include "io_netty_channel_unix_Socket.h"
static jclass datagramSocketAddressClass = NULL;
static jmethodID datagramSocketAddrMethodId = NULL;
static jmethodID inetSocketAddrMethodId = NULL;
static jclass inetSocketAddressClass = NULL;
static jclass netUtilClass = NULL;
static jmethodID netUtilClassIpv4PreferredMethodId = NULL;
static int socketType;
static const char* ip4prefix = "::ffff:";
// Optional external methods
extern int accept4(int sockFd, struct sockaddr* addr, socklen_t* addrlen, int flags) __attribute__((weak));
// macro to calculate the length of a sockaddr_un struct for a given path length.
// see sys/un.h#SUN_LEN, this is modified to allow nul bytes
#define _UNIX_ADDR_LENGTH(path_len) (uintptr_t) (((struct sockaddr_un *) 0)->sun_path) + path_len
static jobject createDatagramSocketAddress(JNIEnv* env, const 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;
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;
port = ntohs(s->sin6_port);
inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof ipstr);
if (strncasecmp(ipstr, ip4prefix, 7) == 0) {
// IPv4-mapped-on-IPv6.
// Cut of ::ffff: prefix to workaround performance issues when parsing these
// addresses in InetAddress.getByName(...).
//
// See https://github.com/netty/netty/issues/2867
ipString = (*env)->NewStringUTF(env, &ipstr[7]);
} else {
ipString = (*env)->NewStringUTF(env, ipstr);
}
}
jobject socketAddr = (*env)->NewObject(env, datagramSocketAddressClass, datagramSocketAddrMethodId, ipString, port, len);
return socketAddr;
}
static int addressLength(const struct sockaddr_storage* addr) {
if (addr->ss_family == AF_INET) {
return 8;
} else {
struct sockaddr_in6* s = (struct sockaddr_in6*) addr;
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
&& s->sin6_addr.s6_addr[5] == 0x00 && s->sin6_addr.s6_addr[6] == 0x00 && s->sin6_addr.s6_addr[7] == 0x00 && s->sin6_addr.s6_addr[8] == 0x00 && s->sin6_addr.s6_addr[9] == 0x00
&& s->sin6_addr.s6_addr[10] == 0xff && s->sin6_addr.s6_addr[11] == 0xff) {
// IPv4-mapped-on-IPv6
return 8;
} else {
return 24;
}
}
}
static void initInetSocketAddressArray(JNIEnv* env, const struct sockaddr_storage* addr, jbyteArray bArray, int offset, int len) {
int port;
if (addr->ss_family == AF_INET) {
struct sockaddr_in* s = (struct sockaddr_in*) addr;
port = ntohs(s->sin_port);
// Encode address and port into the array
unsigned char a[4];
a[0] = port >> 24;
a[1] = port >> 16;
a[2] = port >> 8;
a[3] = port;
(*env)->SetByteArrayRegion(env, bArray, offset, 4, (jbyte*) &s->sin_addr.s_addr);
(*env)->SetByteArrayRegion(env, bArray, offset + 4, 4, (jbyte*) &a);
} else {
struct sockaddr_in6* s = (struct sockaddr_in6*) addr;
port = ntohs(s->sin6_port);
if (len == 8) {
// IPv4-mapped-on-IPv6
// Encode port into the array and write it into the jbyteArray
unsigned char a[4];
a[0] = port >> 24;
a[1] = port >> 16;
a[2] = port >> 8;
a[3] = port;
// we only need the last 4 bytes for mapped address
(*env)->SetByteArrayRegion(env, bArray, offset, 4, (jbyte*) &(s->sin6_addr.s6_addr[12]));
(*env)->SetByteArrayRegion(env, bArray, offset + 4, 4, (jbyte*) &a);
} else {
// Encode scopeid and port into the array
unsigned char a[8];
a[0] = s->sin6_scope_id >> 24;
a[1] = s->sin6_scope_id >> 16;
a[2] = s->sin6_scope_id >> 8;
a[3] = s->sin6_scope_id;
a[4] = port >> 24;
a[5] = port >> 16;
a[6] = port >> 8;
a[7] = port;
(*env)->SetByteArrayRegion(env, bArray, offset, 16, (jbyte*) &(s->sin6_addr.s6_addr));
(*env)->SetByteArrayRegion(env, bArray, offset + 16, 8, (jbyte*) &a);
}
}
}
static jbyteArray createInetSocketAddressArray(JNIEnv* env, const struct sockaddr_storage* addr) {
int len = addressLength(addr);
jbyteArray bArray = (*env)->NewByteArray(env, len);
initInetSocketAddressArray(env, addr, bArray, 0, len);
return bArray;
}
static jobject createInetSocketAddress(JNIEnv* env, const 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;
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;
port = ntohs(s->sin6_port);
inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof ipstr);
if (strncasecmp(ipstr, ip4prefix, 7) == 0) {
// IPv4-mapped-on-IPv6.
// Cut of ::ffff: prefix to workaround performance issues when parsing these
// addresses in InetAddress.getByName(...).
//
// See https://github.com/netty/netty/issues/2867
ipString = (*env)->NewStringUTF(env, &ipstr[7]);
} else {
ipString = (*env)->NewStringUTF(env, ipstr);
}
}
jobject socketAddr = (*env)->NewObject(env, inetSocketAddressClass, inetSocketAddrMethodId, ipString, port);
return socketAddr;
}
static int socket_type(JNIEnv* env) {
jboolean ipv4Preferred = (*env)->CallStaticBooleanMethod(env, netUtilClass, netUtilClassIpv4PreferredMethodId);
if (ipv4Preferred) {
// User asked to use ipv4 explicitly.
return AF_INET;
}
int fd = socket(AF_INET6, SOCK_STREAM | SOCK_NONBLOCK, 0);
if (fd == -1) {
if (errno == EAFNOSUPPORT) {
return AF_INET;
}
return AF_INET6;
} else {
close(fd);
return AF_INET6;
}
}
static jint _socket(JNIEnv* env, jclass clazz, int type) {
int fd = socket(socketType, type | SOCK_NONBLOCK, 0);
if (fd == -1) {
return -errno;
} else if (socketType == AF_INET6) {
// Allow to listen /connect ipv4 and ipv6
int optval = 0;
if (netty_unix_socket_setOption(env, fd, IPPROTO_IPV6, IPV6_V6ONLY, &optval, sizeof(optval)) < 0) {
// Something went wrong so close the fd and return here. setOption(...) itself throws the exception already.
close(fd);
return -1;
}
}
return fd;
}
int netty_unix_socket_initSockaddr(JNIEnv* env, jbyteArray address, jint scopeId, jint jport, const 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.
// This is important as the VM may suspend GC for the time!
jbyte* addressBytes = (*env)->GetPrimitiveArrayCritical(env, address, 0);
if (addressBytes == NULL) {
// No memory left ?!?!?
netty_unix_errors_throwOutOfMemoryError(env);
return -1;
}
if (socketType == AF_INET6) {
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);
} else {
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);
}
(*env)->ReleasePrimitiveArrayCritical(env, address, addressBytes, JNI_ABORT);
return 0;
}
static jint _sendTo(JNIEnv* env, jint fd, void* buffer, jint pos, jint limit, jbyteArray address, jint scopeId, jint port) {
struct sockaddr_storage addr;
if (netty_unix_socket_initSockaddr(env, address, scopeId, port, &addr) == -1) {
return -1;
}
ssize_t res;
int err;
do {
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));
if (res < 0) {
return -err;
}
return (jint) res;
}
static jobject _recvFrom(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);
// Keep on reading if we was interrupted
} while (res == -1 && ((err = errno) == EINTR));
if (res < 0) {
if (err == EAGAIN || err == EWOULDBLOCK) {
// Nothing left to read
return NULL;
}
if (err == EBADF) {
netty_unix_errors_throwClosedChannelException(env);
return NULL;
}
netty_unix_errors_throwIOExceptionErrorNo(env, "recvfrom() failed: ", err);
return NULL;
}
return createDatagramSocketAddress(env, &addr, res);
}
int netty_unix_socket_getOption(JNIEnv* env, jint fd, int level, int optname, void* optval, socklen_t optlen) {
int rc = getsockopt(fd, level, optname, optval, &optlen);
if (rc < 0) {
netty_unix_errors_throwChannelExceptionErrorNo(env, "getsockopt() failed: ", errno);
}
return rc;
}
int netty_unix_socket_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) {
netty_unix_errors_throwChannelExceptionErrorNo(env, "setsockopt() failed: ", errno);
}
return rc;
}
jint netty_unix_socket_JNI_OnLoad(JNIEnv* env) {
jclass localDatagramSocketAddressClass = (*env)->FindClass(env, "io/netty/channel/unix/DatagramSocketAddress");
if (localDatagramSocketAddressClass == NULL) {
// pending exception...
return JNI_ERR;
}
datagramSocketAddressClass = (jclass) (*env)->NewGlobalRef(env, localDatagramSocketAddressClass);
if (datagramSocketAddressClass == NULL) {
// out-of-memory!
netty_unix_errors_throwOutOfMemoryError(env);
return JNI_ERR;
}
datagramSocketAddrMethodId = (*env)->GetMethodID(env, datagramSocketAddressClass, "<init>", "(Ljava/lang/String;II)V");
if (datagramSocketAddrMethodId == NULL) {
netty_unix_errors_throwRuntimeException(env, "failed to get method ID: DatagramSocketAddress.<init>(String, int, int)");
return JNI_ERR;
}
jclass localInetSocketAddressClass = (*env)->FindClass(env, "java/net/InetSocketAddress");
if (localInetSocketAddressClass == NULL) {
// pending exception...
return JNI_ERR;
}
inetSocketAddressClass = (jclass) (*env)->NewGlobalRef(env, localInetSocketAddressClass);
if (inetSocketAddressClass == NULL) {
// out-of-memory!
netty_unix_errors_throwOutOfMemoryError(env);
return JNI_ERR;
}
inetSocketAddrMethodId = (*env)->GetMethodID(env, inetSocketAddressClass, "<init>", "(Ljava/lang/String;I)V");
if (inetSocketAddrMethodId == NULL) {
netty_unix_errors_throwRuntimeException(env, "failed to get method ID: InetSocketAddress.<init>(String, int)");
return JNI_ERR;
}
jclass localNetUtilClass = (*env)->FindClass(env, "io/netty/util/NetUtil" );
if (localNetUtilClass == NULL) {
// pending exception...
return JNI_ERR;
}
netUtilClass = (jclass) (*env)->NewGlobalRef(env, localNetUtilClass);
if (netUtilClass == NULL) {
// out-of-memory!
netty_unix_errors_throwOutOfMemoryError(env);
return JNI_ERR;
}
netUtilClassIpv4PreferredMethodId = (*env)->GetStaticMethodID(env, netUtilClass, "isIpV4StackPreferred", "()Z" );
if (netUtilClassIpv4PreferredMethodId == NULL) {
// position method was not found.. something is wrong so bail out
netty_unix_errors_throwRuntimeException(env, "failed to get method ID: NetUild.isIpV4StackPreferred()");
return JNI_ERR;
}
void* mem = malloc(1);
if (mem == NULL) {
netty_unix_errors_throwOutOfMemoryError(env);
return JNI_ERR;
}
jobject directBuffer = (*env)->NewDirectByteBuffer(env, mem, 1);
if (directBuffer == NULL) {
free(mem);
netty_unix_errors_throwOutOfMemoryError(env);
return JNI_ERR;
}
if ((*env)->GetDirectBufferAddress(env, directBuffer) == NULL) {
free(mem);
netty_unix_errors_throwRuntimeException(env, "failed to get direct buffer address");
return JNI_ERR;
}
free(mem);
socketType = socket_type(env);
}
void netty_unix_socket_JNI_OnUnLoad(JNIEnv* env) {
if (datagramSocketAddressClass != NULL) {
(*env)->DeleteGlobalRef(env, datagramSocketAddressClass);
datagramSocketAddressClass = NULL;
}
if (inetSocketAddressClass != NULL) {
(*env)->DeleteGlobalRef(env, inetSocketAddressClass);
inetSocketAddressClass = NULL;
}
if (netUtilClass != NULL) {
(*env)->DeleteGlobalRef(env, netUtilClass);
netUtilClass = NULL;
}
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_shutdown(JNIEnv* env, jclass clazz, jint fd, jboolean read, jboolean write) {
int mode;
if (read && write) {
mode = SHUT_RDWR;
} else if (read) {
mode = SHUT_RD;
} else if (write) {
mode = SHUT_WR;
} else {
return -EINVAL;
}
if (shutdown(fd, mode) < 0) {
return -errno;
}
return 0;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_bind(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port) {
struct sockaddr_storage addr;
if (netty_unix_socket_initSockaddr(env, address, scopeId, port, &addr) == -1) {
return -1;
}
if (bind(fd, (struct sockaddr*) &addr, sizeof(addr)) == -1) {
return -errno;
}
return 0;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_listen(JNIEnv* env, jclass clazz, jint fd, jint backlog) {
if (listen(fd, backlog) == -1) {
return -errno;
}
return 0;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_connect(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port) {
struct sockaddr_storage addr;
if (netty_unix_socket_initSockaddr(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));
} while (res == -1 && ((err = errno) == EINTR));
if (res < 0) {
return -err;
}
return 0;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_finishConnect(JNIEnv* env, jclass clazz, jint fd) {
// connect may be done
// return true if connection finished successfully
// return false if connection is still in progress
// throw exception if connection failed
int optval;
int res = netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_ERROR, &optval, sizeof(optval));
if (res != 0) {
// getOption failed
return -1;
}
if (optval == 0) {
// connect succeeded
return 0;
}
return -optval;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_accept(JNIEnv* env, jclass clazz, jint fd, jbyteArray acceptedAddress) {
jint socketFd;
int err;
struct sockaddr_storage addr;
socklen_t address_len = sizeof(addr);
do {
if (accept4) {
socketFd = accept4(fd, (struct sockaddr*) &addr, &address_len, SOCK_NONBLOCK | SOCK_CLOEXEC);
} else {
socketFd = accept(fd, (struct sockaddr*) &addr, &address_len);
}
} while (socketFd == -1 && ((err = errno) == EINTR));
if (socketFd == -1) {
return -err;
}
int len = addressLength(&addr);
// Fill in remote address details
(*env)->SetByteArrayRegion(env, acceptedAddress, 0, 4, (jbyte*) &len);
initInetSocketAddressArray(env, &addr, acceptedAddress, 1, len);
if (accept4) {
return socketFd;
} else {
// accept4 was not present so need two more sys-calls ...
if (fcntl(socketFd, F_SETFD, FD_CLOEXEC) == -1 || fcntl(socketFd, F_SETFL, O_NONBLOCK) == -1) {
return -errno;
}
}
return socketFd;
}
JNIEXPORT jbyteArray JNICALL Java_io_netty_channel_unix_Socket_remoteAddress(JNIEnv* env, jclass clazz, jint fd) {
struct sockaddr_storage addr;
socklen_t len = sizeof(addr);
if (getpeername(fd, (struct sockaddr*) &addr, &len) == -1) {
return NULL;
}
return createInetSocketAddressArray(env, &addr);
}
JNIEXPORT jbyteArray JNICALL Java_io_netty_channel_unix_Socket_localAddress(JNIEnv* env, jclass clazz, jint fd) {
struct sockaddr_storage addr;
socklen_t len = sizeof(addr);
if (getsockname(fd, (struct sockaddr*) &addr, &len) == -1) {
return NULL;
}
return createInetSocketAddressArray(env, &addr);
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_newSocketDgramFd(JNIEnv* env, jclass clazz) {
return _socket(env, clazz, SOCK_DGRAM);
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_newSocketStreamFd(JNIEnv* env, jclass clazz) {
return _socket(env, clazz, SOCK_STREAM);
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_newSocketDomainFd(JNIEnv* env, jclass clazz) {
int fd = socket(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
if (fd == -1) {
return -errno;
}
return fd;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_sendTo(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit, jbyteArray address, jint scopeId, jint port) {
// We check that GetDirectBufferAddress will not return NULL in OnLoad
return _sendTo(env, fd, (*env)->GetDirectBufferAddress(env, jbuffer), pos, limit, address, scopeId, port);
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_sendToAddress(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_unix_Socket_sendToAddresses(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint length, jbyteArray address, jint scopeId, jint port) {
struct sockaddr_storage addr;
if (netty_unix_socket_initSockaddr(env, address, scopeId, port, &addr) == -1) {
return -1;
}
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_iovlen = length;
ssize_t res;
int err;
do {
res = sendmsg(fd, &m, 0);
// keep on writing if it was interrupted
} while (res == -1 && ((err = errno) == EINTR));
if (res < 0) {
return -err;
}
return (jint) res;
}
JNIEXPORT jobject JNICALL Java_io_netty_channel_unix_Socket_recvFrom(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit) {
// We check that GetDirectBufferAddress will not return NULL in OnLoad
return _recvFrom(env, fd, (*env)->GetDirectBufferAddress(env, jbuffer), pos, limit);
}
JNIEXPORT jobject JNICALL Java_io_netty_channel_unix_Socket_recvFromAddress(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit) {
return _recvFrom(env, fd, (void*) address, pos, limit);
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_bindDomainSocket(JNIEnv* env, jclass clazz, jint fd, jbyteArray socketPath) {
struct sockaddr_un addr;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
jbyte* socket_path = (*env)->GetByteArrayElements(env, socketPath, 0);
jint socket_path_len = (*env)->GetArrayLength(env, socketPath);
if (socket_path_len > sizeof(addr.sun_path)) {
socket_path_len = sizeof(addr.sun_path);
}
memcpy(addr.sun_path, socket_path, socket_path_len);
if (unlink(socket_path) == -1 && errno != ENOENT) {
return -errno;
}
int res = bind(fd, (struct sockaddr*) &addr, _UNIX_ADDR_LENGTH(socket_path_len));
(*env)->ReleaseByteArrayElements(env, socketPath, socket_path, 0);
if (res == -1) {
return -errno;
}
return res;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_connectDomainSocket(JNIEnv* env, jclass clazz, jint fd, jbyteArray socketPath) {
struct sockaddr_un addr;
jint socket_path_len;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
jbyte* socket_path = (*env)->GetByteArrayElements(env, socketPath, 0);
socket_path_len = (*env)->GetArrayLength(env, socketPath);
if (socket_path_len > sizeof(addr.sun_path)) {
socket_path_len = sizeof(addr.sun_path);
}
memcpy(addr.sun_path, socket_path, socket_path_len);
int res;
int err;
do {
res = connect(fd, (struct sockaddr*) &addr, _UNIX_ADDR_LENGTH(socket_path_len));
} while (res == -1 && ((err = errno) == EINTR));
(*env)->ReleaseByteArrayElements(env, socketPath, socket_path, 0);
if (res < 0) {
return -err;
}
return 0;
}
JNIEXPORT void JNICALL Java_io_netty_channel_unix_Socket_setTcpNoDelay(JNIEnv* env, jclass clazz, jint fd, jint optval) {
netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval));
}
JNIEXPORT void JNICALL Java_io_netty_channel_unix_Socket_setReceiveBufferSize(JNIEnv* env, jclass clazz, jint fd, jint optval) {
netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_RCVBUF, &optval, sizeof(optval));
}
JNIEXPORT void JNICALL Java_io_netty_channel_unix_Socket_setSendBufferSize(JNIEnv* env, jclass clazz, jint fd, jint optval) {
netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_SNDBUF, &optval, sizeof(optval));
}
JNIEXPORT void JNICALL Java_io_netty_channel_unix_Socket_setKeepAlive(JNIEnv* env, jclass clazz, jint fd, jint optval) {
netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
}
JNIEXPORT void JNICALL Java_io_netty_channel_unix_Socket_setTcpCork(JNIEnv* env, jclass clazz, jint fd, jint optval) {
netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_CORK, &optval, sizeof(optval));
}
JNIEXPORT void JNICALL Java_io_netty_channel_unix_Socket_setSoLinger(JNIEnv* env, jclass clazz, jint fd, jint optval) {
struct linger solinger;
if (optval < 0) {
solinger.l_onoff = 0;
solinger.l_linger = 0;
} else {
solinger.l_onoff = 1;
solinger.l_linger = optval;
}
netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_LINGER, &solinger, sizeof(solinger));
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_isTcpNoDelay(JNIEnv* env, jclass clazz, jint fd) {
int optval;
if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_getReceiveBufferSize(JNIEnv* env, jclass clazz, jint fd) {
int optval;
if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_RCVBUF, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_getSendBufferSize(JNIEnv* env, jclass clazz, jint fd) {
int optval;
if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_SNDBUF, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_isTcpCork(JNIEnv* env, jclass clazz, jint fd) {
int optval;
if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_CORK, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_getSoLinger(JNIEnv* env, jclass clazz, jint fd) {
struct linger optval;
if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_LINGER, &optval, sizeof(optval)) == -1) {
return -1;
}
if (optval.l_onoff == 0) {
return -1;
} else {
return optval.l_linger;
}
}
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_getSoError(JNIEnv* env, jclass clazz, jint fd) {
int optval;
if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_ERROR, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
}

View File

@ -0,0 +1,56 @@
/*
* Copyright 2015 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#include <jni.h>
// Exported JNI Methods
jint Java_io_netty_channel_unix_Socket_shutdown(JNIEnv* env, jclass clazz, jint fd, jboolean read, jboolean write);
jint Java_io_netty_channel_unix_Socket_newSocketStreamFd(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_unix_Socket_newSocketDgramFd(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_unix_Socket_newSocketDomainFd(JNIEnv* env, jclass clazz);
jint Java_io_netty_channel_unix_Socket_sendTo(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit, jbyteArray address, jint scopeId, jint port);
jint Java_io_netty_channel_unix_Socket_sendToAddress(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint pos, jint limit, jbyteArray address, jint scopeId, jint port);
jint Java_io_netty_channel_unix_Socket_sendToAddresses(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint length, jbyteArray address, jint scopeId, jint port);
jobject Java_io_netty_channel_unix_Socket_recvFrom(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit);
jobject Java_io_netty_channel_unix_Socket_recvFromAddress(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit);
jint Java_io_netty_channel_unix_Socket_bind(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port);
jint Java_io_netty_channel_unix_Socket_bindDomainSocket(JNIEnv* env, jclass clazz, jint fd, jstring address);
jint Java_io_netty_channel_unix_Socket_listen(JNIEnv* env, jclass clazz, jint fd, jint backlog);
jint Java_io_netty_channel_unix_Socket_accept(JNIEnv* env, jclass clazz, jint fd, jbyteArray acceptedAddress);
jint Java_io_netty_channel_unix_Socket_connect(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port);
jint Java_io_netty_channel_unix_Socket_connectDomainSocket(JNIEnv* env, jclass clazz, jint fd, jstring address);
jint Java_io_netty_channel_unix_Socket_finishConnect(JNIEnv* env, jclass clazz, jint fd);
jbyteArray Java_io_netty_channel_unix_Socket_remoteAddress(JNIEnv* env, jclass clazz, jint fd);
jbyteArray Java_io_netty_channel_unix_Socket_localAddress(JNIEnv* env, jclass clazz, jint fd);
void Java_io_netty_channel_unix_Socket_setTcpNoDelay(JNIEnv* env, jclass clazz, jint fd, jint optval);
void Java_io_netty_channel_unix_Socket_setReceiveBufferSize(JNIEnv* env, jclass clazz, jint fd, jint optval);
void Java_io_netty_channel_unix_Socket_setSendBufferSize(JNIEnv* env, jclass clazz, jint fd, jint optval);
void Java_io_netty_channel_unix_Socket_setKeepAlive(JNIEnv* env, jclass clazz, jint fd, jint optval);
void Java_io_netty_channel_unix_Socket_setTcpCork(JNIEnv* env, jclass clazz, jint fd, jint optval);
void Java_io_netty_channel_unix_Socket_setSoLinger(JNIEnv* env, jclass clazz, jint fd, jint optval);
jint Java_io_netty_channel_unix_Socket_isTcpNoDelay(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_unix_Socket_getReceiveBufferSize(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_unix_Socket_getSendBufferSize(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_unix_Socket_isTcpCork(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_unix_Socket_getSoLinger(JNIEnv* env, jclass clazz, jint fd);
jint Java_io_netty_channel_unix_Socket_getSoError(JNIEnv* env, jclass clazz, jint fd);

View File

@ -0,0 +1,33 @@
/*
* Copyright 2015 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#ifndef NETTY_UNIX_ERRORS_H_
#define NETTY_UNIX_ERRORS_H_
#include <jni.h>
void netty_unix_errors_throwRuntimeException(JNIEnv* env, char* message);
void netty_unix_errors_throwRuntimeExceptionErrorNo(JNIEnv* env, char* message, int errorNumber);
void netty_unix_errors_throwChannelExceptionErrorNo(JNIEnv* env, char* message, int errorNumber);
void netty_unix_errors_throwIOException(JNIEnv* env, char* message);
void netty_unix_errors_throwIOExceptionErrorNo(JNIEnv* env, char* message, int errorNumber);
void netty_unix_errors_throwClosedChannelException(JNIEnv* env);
void netty_unix_errors_throwOutOfMemoryError(JNIEnv* env);
// JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods.
jint netty_unix_errors_JNI_OnLoad(JNIEnv* env);
void netty_unix_errors_JNI_OnUnLoad(JNIEnv* env);
#endif /* NETTY_UNIX_ERRORS_H_ */

View File

@ -13,12 +13,13 @@
* License for the specific language governing permissions and limitations
* under the License.
*/
#ifndef NETTY_UNIX_FILEDESCRIPTOR_H_
#define NETTY_UNIX_FILEDESCRIPTOR_H_
void throwRuntimeException(JNIEnv* env, char* message);
void throwRuntimeExceptionErrorNo(JNIEnv* env, char* message, int errorNumber);
void throwChannelExceptionErrorNo(JNIEnv* env, char* message, int errorNumber);
void throwIOException(JNIEnv* env, char* message);
void throwIOExceptionErrorNo(JNIEnv* env, char* message, int errorNumber);
void throwClosedChannelException(JNIEnv* env);
void throwOutOfMemoryError(JNIEnv* env);
char* exceptionMessage(char* msg, int error);
#include <jni.h>
// JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods.
jint netty_unix_filedescriptor_JNI_OnLoad(JNIEnv* env);
void netty_unix_filedescriptor_JNI_OnUnLoad(JNIEnv* env);
#endif /* NETTY_UNIX_FILEDESCRIPTOR_H_ */

View File

@ -0,0 +1,31 @@
/*
* Copyright 2015 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#ifndef NETTY_UNIX_SOCKET_H_
#define NETTY_UNIX_SOCKET_H_
#include <sys/socket.h>
#include <jni.h>
// External C methods
int netty_unix_socket_initSockaddr(JNIEnv* env, jbyteArray address, jint scopeId, jint jport, const struct sockaddr_storage* addr);
int netty_unix_socket_getOption(JNIEnv* env, jint fd, int level, int optname, void* optval, socklen_t optlen);
int netty_unix_socket_setOption(JNIEnv* env, jint fd, int level, int optname, const void* optval, socklen_t len);
// JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods.
jint netty_unix_socket_JNI_OnLoad(JNIEnv* env);
void netty_unix_socket_JNI_OnUnLoad(JNIEnv* env);
#endif /* NETTY_UNIX_SOCKET_H_ */

View File

@ -27,6 +27,7 @@ import io.netty.channel.EventLoop;
import io.netty.channel.RecvByteBufAllocator;
import io.netty.channel.socket.ChannelInputShutdownEvent;
import io.netty.channel.unix.FileDescriptor;
import io.netty.channel.unix.Socket;
import io.netty.channel.unix.UnixChannel;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.internal.OneTimeTask;
@ -36,32 +37,27 @@ import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.UnresolvedAddressException;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
abstract class AbstractEpollChannel extends AbstractChannel implements UnixChannel {
private static final ChannelMetadata METADATA = new ChannelMetadata(false);
private final int readFlag;
private final FileDescriptor fileDescriptor;
private final Socket fileDescriptor;
protected int flags = Native.EPOLLET;
protected volatile boolean active;
private volatile boolean inputShutdown;
AbstractEpollChannel(int fd, int flag) {
AbstractEpollChannel(Socket fd, int flag) {
this(null, fd, flag, false);
}
AbstractEpollChannel(Channel parent, int fd, int flag, boolean active) {
this(parent, new FileDescriptor(fd), flag, active);
}
AbstractEpollChannel(Channel parent, FileDescriptor fd, int flag, boolean active) {
AbstractEpollChannel(Channel parent, Socket fd, int flag, boolean active) {
super(parent);
if (fd == null) {
throw new NullPointerException("fd");
}
fileDescriptor = checkNotNull(fd, "fd");
readFlag = flag;
flags |= flag;
this.active = active;
fileDescriptor = fd;
}
void setFlag(int flag) throws IOException {
@ -83,7 +79,7 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
}
@Override
public final FileDescriptor fd() {
public final Socket fd() {
return fileDescriptor;
}
@ -109,7 +105,11 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
// deregister from epoll now and shutdown the socket.
doDeregister();
if (active) {
shutdown(fd.intValue());
try {
fd().shutdown(true, true);
} catch (IOException ignored) {
// The FD will be closed, so if shutdown fails there is nothing we can do.
}
}
} finally {
// Ensure the file descriptor is closed in all cases.
@ -117,14 +117,6 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
}
}
/**
* Called on {@link #doClose()} before the actual {@link FileDescriptor} is closed.
* This implementation does nothing.
*/
protected void shutdown(int fd) throws IOException {
// NOOP
}
@Override
protected void doDisconnect() throws Exception {
doClose();
@ -253,11 +245,10 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
int localReadAmount;
unsafe().recvBufAllocHandle().attemptedBytesRead(byteBuf.writableBytes());
if (byteBuf.hasMemoryAddress()) {
localReadAmount = Native.readAddress(
fileDescriptor.intValue(), byteBuf.memoryAddress(), writerIndex, byteBuf.capacity());
localReadAmount = fileDescriptor.readAddress(byteBuf.memoryAddress(), writerIndex, byteBuf.capacity());
} else {
ByteBuffer buf = byteBuf.internalNioBuffer(writerIndex, byteBuf.writableBytes());
localReadAmount = Native.read(fileDescriptor.intValue(), buf, buf.position(), buf.limit());
localReadAmount = fileDescriptor.read(buf, buf.position(), buf.limit());
}
if (localReadAmount > 0) {
byteBuf.writerIndex(writerIndex + localReadAmount);
@ -273,8 +264,7 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
int readerIndex = buf.readerIndex();
int writerIndex = buf.writerIndex();
for (int i = writeSpinCount - 1; i >= 0; i--) {
int localFlushedAmount = Native.writeAddress(
fileDescriptor.intValue(), memoryAddress, readerIndex, writerIndex);
int localFlushedAmount = fileDescriptor.writeAddress(memoryAddress, readerIndex, writerIndex);
if (localFlushedAmount > 0) {
writtenBytes += localFlushedAmount;
if (writtenBytes == readableBytes) {
@ -295,7 +285,7 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
for (int i = writeSpinCount - 1; i >= 0; i--) {
int pos = nioBuf.position();
int limit = nioBuf.limit();
int localFlushedAmount = Native.write(fileDescriptor.intValue(), nioBuf, pos, limit);
int localFlushedAmount = fileDescriptor.write(nioBuf, pos, limit);
if (localFlushedAmount > 0) {
nioBuf.position(pos + localFlushedAmount);
writtenBytes += localFlushedAmount;
@ -382,7 +372,7 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
if (isOpen()) {
if (Boolean.TRUE.equals(config().getOption(ChannelOption.ALLOW_HALF_CLOSURE))) {
try {
Native.shutdown(fd().intValue(), true, false);
fd().shutdown(true, false);
clearEpollIn0();
pipeline().fireUserEventTriggered(ChannelInputShutdownEvent.INSTANCE);
} catch (IOException e) {

View File

@ -25,6 +25,7 @@ import io.netty.channel.EventLoop;
import io.netty.channel.RecvByteBufAllocator;
import io.netty.channel.ServerChannel;
import io.netty.channel.unix.FileDescriptor;
import io.netty.channel.unix.Socket;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
@ -32,12 +33,31 @@ import java.net.SocketAddress;
public abstract class AbstractEpollServerChannel extends AbstractEpollChannel implements ServerChannel {
private static final ChannelMetadata METADATA = new ChannelMetadata(false, 16);
/**
* @deprecated Use {@link #AbstractEpollServerChannel(Socket, boolean)}.
*/
protected AbstractEpollServerChannel(int fd) {
super(fd, Native.EPOLLIN);
this(new Socket(fd), false);
}
/**
* @deprecated Use {@link #AbstractEpollServerChannel(Socket, boolean)}.
*/
@Deprecated
protected AbstractEpollServerChannel(FileDescriptor fd) {
super(null, fd, Native.EPOLLIN, Native.getSoError(fd.intValue()) == 0);
this(new Socket(fd.intValue()));
}
/**
* @deprecated Use {@link #AbstractEpollServerChannel(Socket, boolean)}.
*/
@Deprecated
protected AbstractEpollServerChannel(Socket fd) {
this(fd, fd.getSoError() == 0);
}
protected AbstractEpollServerChannel(Socket fd, boolean active) {
super(null, fd, Native.EPOLLIN, active);
}
@Override
@ -109,7 +129,7 @@ public abstract class AbstractEpollServerChannel extends AbstractEpollChannel im
try {
try {
do {
int socketFd = Native.accept(fd().intValue(), acceptedAddress);
int socketFd = fd().accept(acceptedAddress);
if (socketFd == -1) {
// this means everything was handled for now
break;

View File

@ -15,7 +15,6 @@
*/
package io.netty.channel.epoll;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.CompositeByteBuf;
@ -31,6 +30,7 @@ import io.netty.channel.DefaultFileRegion;
import io.netty.channel.EventLoop;
import io.netty.channel.RecvByteBufAllocator;
import io.netty.channel.unix.FileDescriptor;
import io.netty.channel.unix.Socket;
import io.netty.util.internal.EmptyArrays;
import io.netty.util.internal.MpscLinkedQueueNode;
import io.netty.util.internal.OneTimeTask;
@ -47,6 +47,9 @@ import java.util.Queue;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import static io.netty.channel.unix.FileDescriptor.pipe;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
private static final String EXPECTED_TYPES =
@ -71,33 +74,53 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
private volatile boolean outputShutdown;
// Lazy init these if we need to splice(...)
private int pipeIn = -1;
private int pipeOut = -1;
private FileDescriptor pipeIn;
private FileDescriptor pipeOut;
/**
* @deprecated Use {@link #AbstractEpollStreamChannel(Channel, Socket)}.
*/
@Deprecated
protected AbstractEpollStreamChannel(Channel parent, int fd) {
this(parent, new Socket(fd));
}
/**
* @deprecated Use {@link #AbstractEpollStreamChannel(Socket, boolean)}.
*/
@Deprecated
protected AbstractEpollStreamChannel(int fd) {
this(new Socket(fd));
}
/**
* @deprecated Use {@link #AbstractEpollStreamChannel(Socket, boolean)}.
*/
@Deprecated
protected AbstractEpollStreamChannel(FileDescriptor fd) {
this(new Socket(fd.intValue()));
}
/**
* @deprecated Use {@link #AbstractEpollStreamChannel(Socket, boolean)}.
*/
@Deprecated
protected AbstractEpollStreamChannel(Socket fd) {
this(fd, fd.getSoError() == 0);
}
protected AbstractEpollStreamChannel(Channel parent, Socket fd) {
super(parent, fd, Native.EPOLLIN, true);
// Add EPOLLRDHUP so we are notified once the remote peer close the connection.
flags |= Native.EPOLLRDHUP;
}
protected AbstractEpollStreamChannel(int fd) {
super(fd, Native.EPOLLIN);
protected AbstractEpollStreamChannel(Socket fd, boolean active) {
super(null, fd, Native.EPOLLIN, active);
// Add EPOLLRDHUP so we are notified once the remote peer close the connection.
flags |= Native.EPOLLRDHUP;
}
protected AbstractEpollStreamChannel(FileDescriptor fd) {
super(null, fd, Native.EPOLLIN, Native.getSoError(fd.intValue()) == 0);
// Add EPOLLRDHUP so we are notified once the remote peer close the connection.
flags |= Native.EPOLLRDHUP;
}
@Override
protected void shutdown(int fd) throws IOException {
Native.shutdown(fd, true, true);
}
@Override
protected AbstractEpollUnsafe newUnsafe() {
return new EpollStreamUnsafe();
@ -262,7 +285,7 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
int offset = 0;
int end = offset + cnt;
for (int i = writeSpinCount - 1; i >= 0; i--) {
long localWrittenBytes = Native.writevAddresses(fd().intValue(), array.memoryAddress(offset), cnt);
long localWrittenBytes = fd().writevAddresses(array.memoryAddress(offset), cnt);
if (localWrittenBytes == 0) {
break;
}
@ -301,7 +324,7 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
int offset = 0;
int end = offset + nioBufferCnt;
for (int i = writeSpinCount - 1; i >= 0; i--) {
long localWrittenBytes = Native.writev(fd().intValue(), nioBuffers, offset, nioBufferCnt);
long localWrittenBytes = fd().writev(nioBuffers, offset, nioBufferCnt);
if (localWrittenBytes == 0) {
break;
}
@ -511,7 +534,7 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
protected void shutdownOutput0(final ChannelPromise promise) {
try {
Native.shutdown(fd().intValue(), false, true);
fd().shutdown(false, true);
outputShutdown = true;
promise.setSuccess();
} catch (Throwable cause) {
@ -558,12 +581,12 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
*/
protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
if (localAddress != null) {
Native.bind(fd().intValue(), localAddress);
fd().bind(localAddress);
}
boolean success = false;
try {
boolean connected = Native.connect(fd().intValue(), remoteAddress);
boolean connected = fd().connect(remoteAddress);
if (!connected) {
setFlag(Native.EPOLLOUT);
}
@ -576,10 +599,10 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
}
}
private void safeClosePipe(int pipe) {
if (pipe != -1) {
private void safeClosePipe(FileDescriptor fd) {
if (fd != null) {
try {
Native.close(pipe);
fd.close();
} catch (IOException e) {
if (logger.isWarnEnabled()) {
logger.warn("Error while closing a pipe", e);
@ -735,7 +758,7 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
* Finish the connect
*/
private boolean doFinishConnect() throws Exception {
if (Native.finishConnect(fd().intValue())) {
if (fd().finishConnect()) {
clearFlag(Native.EPOLLOUT);
return true;
} else {
@ -840,13 +863,13 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
abstract boolean spliceIn(RecvByteBufAllocator.Handle handle) throws IOException;
protected final int spliceIn(int pipeOut, RecvByteBufAllocator.Handle handle) throws IOException {
protected final int spliceIn(FileDescriptor pipeOut, RecvByteBufAllocator.Handle handle) throws IOException {
// calculate the maximum amount of data we are allowed to splice
int length = Math.min(handle.guess(), len);
int splicedIn = 0;
for (;;) {
// Splicing until there is nothing left to splice.
int localSplicedIn = Native.splice(fd().intValue(), -1, pipeOut, -1, length);
int localSplicedIn = Native.splice(fd().intValue(), -1, pipeOut.intValue(), -1, length);
if (localSplicedIn == 0) {
break;
}
@ -885,12 +908,12 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
// We create the pipe on the target channel as this will allow us to just handle pending writes
// later in a correct fashion without get into any ordering issues when spliceTo(...) is called
// on multiple Channels pointing to one target Channel.
int pipeOut = ch.pipeOut;
if (pipeOut == -1) {
FileDescriptor pipeOut = ch.pipeOut;
if (pipeOut == null) {
// Create a new pipe as non was created before.
long fds = Native.pipe();
ch.pipeIn = (int) (fds >> 32);
pipeOut = ch.pipeOut = (int) fds;
FileDescriptor[] pipe = pipe();
ch.pipeIn = pipe[0];
pipeOut = ch.pipeOut = pipe[1];
}
int splicedIn = spliceIn(pipeOut, handle);
@ -946,7 +969,7 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
public boolean spliceOut() throws Exception {
assert ch.eventLoop().inEventLoop();
try {
int splicedOut = Native.splice(ch.pipeIn, -1, ch.fd().intValue(), -1, len);
int splicedOut = Native.splice(ch.pipeIn.intValue(), -1, ch.fd().intValue(), -1, len);
len -= splicedOut;
if (len == 0) {
if (autoRead) {
@ -991,35 +1014,34 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
return true;
}
int pipeIn = -1;
int pipeOut = -1;
try {
long fds = Native.pipe();
pipeIn = (int) (fds >> 32);
pipeOut = (int) fds;
int splicedIn = spliceIn(pipeOut, handle);
if (splicedIn > 0) {
// Integer.MAX_VALUE is a special value which will result in splice forever.
if (len != Integer.MAX_VALUE) {
len -= splicedIn;
}
do {
int splicedOut = Native.splice(pipeIn, -1, fd.intValue(), offset, splicedIn);
splicedIn -= splicedOut;
} while (splicedIn > 0);
if (len == 0) {
promise.setSuccess();
return true;
FileDescriptor[] pipe = pipe();
FileDescriptor pipeIn = pipe[0];
FileDescriptor pipeOut = pipe[1];
try {
int splicedIn = spliceIn(pipeOut, handle);
if (splicedIn > 0) {
// Integer.MAX_VALUE is a special value which will result in splice forever.
if (len != Integer.MAX_VALUE) {
len -= splicedIn;
}
do {
int splicedOut = Native.splice(pipeIn.intValue(), -1, fd.intValue(), offset, splicedIn);
splicedIn -= splicedOut;
} while (splicedIn > 0);
if (len == 0) {
promise.setSuccess();
return true;
}
}
return false;
} finally {
safeClosePipe(pipeIn);
safeClosePipe(pipeOut);
}
return false;
} catch (Throwable cause) {
promise.setFailure(cause);
return true;
} finally {
safeClosePipe(pipeIn);
safeClosePipe(pipeOut);
}
}
}

View File

@ -15,6 +15,8 @@
*/
package io.netty.channel.epoll;
import io.netty.channel.unix.FileDescriptor;
/**
* Tells if <a href="http://netty.io/wiki/native-transports.html">{@code netty-transport-native-epoll}</a> is supported.
*/
@ -24,24 +26,24 @@ public final class Epoll {
static {
Throwable cause = null;
int epollFd = -1;
int eventFd = -1;
FileDescriptor epollFd = null;
FileDescriptor eventFd = null;
try {
epollFd = Native.epollCreate();
eventFd = Native.eventFd();
epollFd = Native.newEpollCreate();
eventFd = Native.newEventFd();
} catch (Throwable t) {
cause = t;
} finally {
if (epollFd != -1) {
if (epollFd != null) {
try {
Native.close(epollFd);
epollFd.close();
} catch (Exception ignore) {
// ignore
}
}
if (eventFd != -1) {
if (eventFd != null) {
try {
Native.close(eventFd);
eventFd.close();
} catch (Exception ignore) {
// ignore
}

View File

@ -30,7 +30,9 @@ import io.netty.channel.RecvByteBufAllocator;
import io.netty.channel.socket.DatagramChannel;
import io.netty.channel.socket.DatagramChannelConfig;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.unix.DatagramSocketAddress;
import io.netty.channel.unix.FileDescriptor;
import io.netty.channel.unix.Socket;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.StringUtil;
@ -45,6 +47,8 @@ import java.nio.channels.NotYetConnectedException;
import java.util.ArrayList;
import java.util.List;
import static io.netty.channel.unix.Socket.newSocketDgram;
/**
* {@link DatagramChannel} implementation that uses linux EPOLL Edge-Triggered Mode for
* maximal performance.
@ -64,20 +68,24 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
private final EpollDatagramChannelConfig config;
public EpollDatagramChannel() {
super(Native.socketDgramFd(), Native.EPOLLIN);
super(newSocketDgram(), Native.EPOLLIN);
config = new EpollDatagramChannelConfig(this);
}
/**
* Create a new {@link EpollDatagramChannel} from the given {@link FileDescriptor}.
* @deprecated Use {@link #EpollDatagramChannel(Socket)}.
*/
@Deprecated
public EpollDatagramChannel(FileDescriptor fd) {
super(null, fd, Native.EPOLLIN, true);
config = new EpollDatagramChannelConfig(this);
this(new Socket(fd.intValue()));
}
public EpollDatagramChannel(Socket fd) {
super(null, fd, Native.EPOLLIN, true);
// As we create an EpollDatagramChannel from a FileDescriptor we should try to obtain the remote and local
// address from it. This is needed as the FileDescriptor may be bound already.
local = Native.localAddress(fd.intValue());
local = fd.localAddress();
config = new EpollDatagramChannelConfig(this);
}
@Override
@ -276,9 +284,8 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
protected void doBind(SocketAddress localAddress) throws Exception {
InetSocketAddress addr = (InetSocketAddress) localAddress;
checkResolvable(addr);
int fd = fd().intValue();
Native.bind(fd, addr);
local = Native.localAddress(fd);
fd().bind(addr);
local = fd().localAddress();
active = true;
}
@ -372,18 +379,18 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
final int writtenBytes;
if (data.hasMemoryAddress()) {
long memoryAddress = data.memoryAddress();
writtenBytes = Native.sendToAddress(fd().intValue(), memoryAddress, data.readerIndex(), data.writerIndex(),
writtenBytes = fd().sendToAddress(memoryAddress, data.readerIndex(), data.writerIndex(),
remoteAddress.getAddress(), remoteAddress.getPort());
} else if (data instanceof CompositeByteBuf) {
IovArray array = IovArrayThreadLocal.get((CompositeByteBuf) data);
int cnt = array.count();
assert cnt != 0;
writtenBytes = Native.sendToAddresses(fd().intValue(), array.memoryAddress(0),
writtenBytes = fd().sendToAddresses(array.memoryAddress(0),
cnt, remoteAddress.getAddress(), remoteAddress.getPort());
} else {
ByteBuffer nioData = data.internalNioBuffer(data.readerIndex(), data.readableBytes());
writtenBytes = Native.sendTo(fd().intValue(), nioData, nioData.position(), nioData.limit(),
writtenBytes = fd().sendTo(nioData, nioData.position(), nioData.limit(),
remoteAddress.getAddress(), remoteAddress.getPort());
}
@ -490,7 +497,7 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
checkResolvable(remoteAddress);
EpollDatagramChannel.this.remote = remoteAddress;
EpollDatagramChannel.this.local = Native.localAddress(fd().intValue());
EpollDatagramChannel.this.local = fd().localAddress();
success = true;
// Regardless if the connection attempt was cancelled, channelActive() event should be triggered,
@ -543,12 +550,11 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
final DatagramSocketAddress remoteAddress;
if (data.hasMemoryAddress()) {
// has a memory address so use optimized call
remoteAddress = Native.recvFromAddress(
fd().intValue(), data.memoryAddress(), data.writerIndex(), data.capacity());
remoteAddress = fd().recvFromAddress(data.memoryAddress(), data.writerIndex(),
data.capacity());
} else {
ByteBuffer nioData = data.internalNioBuffer(data.writerIndex(), data.writableBytes());
remoteAddress = Native.recvFrom(
fd().intValue(), nioData, nioData.position(), nioData.limit());
remoteAddress = fd().recvFrom(nioData, nioData.position(), nioData.limit());
}
if (remoteAddress == null) {
@ -558,7 +564,7 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
}
allocHandle.incMessagesRead(1);
allocHandle.lastBytesRead(remoteAddress.receivedAmount);
allocHandle.lastBytesRead(remoteAddress.receivedAmount());
data.writerIndex(data.writerIndex() + allocHandle.lastBytesRead());
readPending = false;
@ -598,21 +604,4 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
}
}
}
/**
* Act as special {@link InetSocketAddress} to be able to easily pass all needed data from JNI without the need
* to create more objects then needed.
*/
static final class DatagramSocketAddress extends InetSocketAddress {
private static final long serialVersionUID = 1348596211215015739L;
// holds the amount of received bytes
final int receivedAmount;
DatagramSocketAddress(String addr, int port, int receivedAmount) {
super(addr, port);
this.receivedAmount = receivedAmount;
}
}
}

View File

@ -192,23 +192,23 @@ public final class EpollDatagramChannelConfig extends EpollChannelConfig impleme
@Override
public int getSendBufferSize() {
return Native.getSendBufferSize(datagramChannel.fd().intValue());
return datagramChannel.fd().getSendBufferSize();
}
@Override
public EpollDatagramChannelConfig setSendBufferSize(int sendBufferSize) {
Native.setSendBufferSize(datagramChannel.fd().intValue(), sendBufferSize);
datagramChannel.fd().setSendBufferSize(sendBufferSize);
return this;
}
@Override
public int getReceiveBufferSize() {
return Native.getReceiveBufferSize(datagramChannel.fd().intValue());
return datagramChannel.fd().getReceiveBufferSize();
}
@Override
public EpollDatagramChannelConfig setReceiveBufferSize(int receiveBufferSize) {
Native.setReceiveBufferSize(datagramChannel.fd().intValue(), receiveBufferSize);
datagramChannel.fd().setReceiveBufferSize(receiveBufferSize);
return this;
}

View File

@ -22,10 +22,12 @@ import io.netty.channel.ChannelPipeline;
import io.netty.channel.unix.DomainSocketAddress;
import io.netty.channel.unix.DomainSocketChannel;
import io.netty.channel.unix.FileDescriptor;
import io.netty.util.internal.OneTimeTask;
import io.netty.channel.unix.Socket;
import java.net.SocketAddress;
import static io.netty.channel.unix.Socket.newSocketDomain;
public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel implements DomainSocketChannel {
private final EpollDomainSocketChannelConfig config = new EpollDomainSocketChannelConfig(this);
@ -33,22 +35,36 @@ public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel i
private volatile DomainSocketAddress remote;
public EpollDomainSocketChannel() {
super(Native.socketDomainFd());
super(newSocketDomain(), false);
}
/**
* @deprecated Use {@link #EpollDomainSocketChannel(Channel, Socket)}.
*/
@Deprecated
public EpollDomainSocketChannel(Channel parent, FileDescriptor fd) {
super(parent, fd.intValue());
super(parent, new Socket(fd.intValue()));
}
/**
* @deprecated Use {@link #EpollDomainSocketChannel(Socket, boolean)}.
* <p>
* Creates a new {@link EpollDomainSocketChannel} from an existing {@link FileDescriptor}
*/
@Deprecated
public EpollDomainSocketChannel(FileDescriptor fd) {
super(fd);
}
public EpollDomainSocketChannel(Channel parent, Socket fd) {
super(parent, fd);
}
/**
* Creates a new {@link EpollDomainSocketChannel} from an existing {@link FileDescriptor}
*/
public EpollDomainSocketChannel(FileDescriptor fd) {
super(fd);
}
EpollDomainSocketChannel(Channel parent, int fd) {
super(parent, fd);
public EpollDomainSocketChannel(Socket fd, boolean active) {
super(fd, active);
}
@Override
@ -68,7 +84,7 @@ public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel i
@Override
protected void doBind(SocketAddress localAddress) throws Exception {
Native.bind(fd().intValue(), localAddress);
fd().bind(localAddress);
local = (DomainSocketAddress) localAddress;
}

View File

@ -19,6 +19,7 @@ import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.SingleThreadEventLoop;
import io.netty.channel.epoll.AbstractEpollChannel.AbstractEpollUnsafe;
import io.netty.channel.unix.FileDescriptor;
import io.netty.util.collection.IntObjectHashMap;
import io.netty.util.collection.IntObjectMap;
import io.netty.util.internal.PlatformDependent;
@ -48,8 +49,8 @@ final class EpollEventLoop extends SingleThreadEventLoop {
WAKEN_UP_UPDATER = updater;
}
private final int epollFd;
private final int eventFd;
private final FileDescriptor epollFd;
private final FileDescriptor eventFd;
private final IntObjectMap<AbstractEpollChannel> channels = new IntObjectHashMap<AbstractEpollChannel>(4096);
private final boolean allowGrowing;
private final EpollEventArray events;
@ -67,29 +68,29 @@ final class EpollEventLoop extends SingleThreadEventLoop {
events = new EpollEventArray(maxEvents);
}
boolean success = false;
int epollFd = -1;
int eventFd = -1;
FileDescriptor epollFd = null;
FileDescriptor eventFd = null;
try {
this.epollFd = epollFd = Native.epollCreate();
this.eventFd = eventFd = Native.eventFd();
this.epollFd = epollFd = Native.newEpollCreate();
this.eventFd = eventFd = Native.newEventFd();
try {
Native.epollCtlAdd(epollFd, eventFd, Native.EPOLLIN);
Native.epollCtlAdd(epollFd.intValue(), eventFd.intValue(), Native.EPOLLIN);
} catch (IOException e) {
throw new IllegalStateException("Unable to add eventFd filedescriptor to epoll", e);
}
success = true;
} finally {
if (!success) {
if (epollFd != -1) {
if (epollFd != null) {
try {
Native.close(epollFd);
epollFd.close();
} catch (Exception e) {
// ignore
}
}
if (eventFd != -1) {
if (eventFd != null) {
try {
Native.close(eventFd);
eventFd.close();
} catch (Exception e) {
// ignore
}
@ -102,7 +103,7 @@ final class EpollEventLoop extends SingleThreadEventLoop {
protected void wakeup(boolean inEventLoop) {
if (!inEventLoop && WAKEN_UP_UPDATER.compareAndSet(this, 0, 1)) {
// write to the evfd which will then wake-up epoll_wait(...)
Native.eventFdWrite(eventFd, 1L);
Native.eventFdWrite(eventFd.intValue(), 1L);
}
}
@ -112,7 +113,7 @@ final class EpollEventLoop extends SingleThreadEventLoop {
void add(AbstractEpollChannel ch) throws IOException {
assert inEventLoop();
int fd = ch.fd().intValue();
Native.epollCtlAdd(epollFd, fd, ch.flags);
Native.epollCtlAdd(epollFd.intValue(), fd, ch.flags);
channels.put(fd, ch);
}
@ -121,7 +122,7 @@ final class EpollEventLoop extends SingleThreadEventLoop {
*/
void modify(AbstractEpollChannel ch) throws IOException {
assert inEventLoop();
Native.epollCtlMod(epollFd, ch.fd().intValue(), ch.flags);
Native.epollCtlMod(epollFd.intValue(), ch.fd().intValue(), ch.flags);
}
/**
@ -135,7 +136,7 @@ final class EpollEventLoop extends SingleThreadEventLoop {
if (channels.remove(fd) != null) {
// Remove the epoll. This is only needed if it's still open as otherwise it will be automatically
// removed once the file-descriptor is closed.
Native.epollCtlDel(epollFd, ch.fd().intValue());
Native.epollCtlDel(epollFd.intValue(), ch.fd().intValue());
}
}
}
@ -172,7 +173,7 @@ final class EpollEventLoop extends SingleThreadEventLoop {
long timeoutMillis = (selectDeadLineNanos - currentTimeNanos + 500000L) / 1000000L;
if (timeoutMillis <= 0) {
if (selectCnt == 0) {
int ready = Native.epollWait(epollFd, events, 0);
int ready = Native.epollWait(epollFd.intValue(), events, 0);
if (ready > 0) {
return ready;
}
@ -180,7 +181,7 @@ final class EpollEventLoop extends SingleThreadEventLoop {
break;
}
int selectedKeys = Native.epollWait(epollFd, events, (int) timeoutMillis);
int selectedKeys = Native.epollWait(epollFd.intValue(), events, (int) timeoutMillis);
selectCnt ++;
if (selectedKeys != 0 || oldWakenUp || wakenUp == 1 || hasTasks() || hasScheduledTasks()) {
@ -203,7 +204,7 @@ final class EpollEventLoop extends SingleThreadEventLoop {
int ready;
if (hasTasks()) {
// Non blocking just return what is ready directly without block
ready = Native.epollWait(epollFd, events, 0);
ready = Native.epollWait(epollFd.intValue(), events, 0);
} else {
ready = epollWait(oldWakenUp);
@ -236,7 +237,7 @@ final class EpollEventLoop extends SingleThreadEventLoop {
// (OK - no wake-up required).
if (wakenUp == 1) {
Native.eventFdWrite(eventFd, 1L);
Native.eventFdWrite(eventFd.intValue(), 1L);
}
}
@ -282,7 +283,7 @@ final class EpollEventLoop extends SingleThreadEventLoop {
private void closeAll() {
try {
Native.epollWait(epollFd, events, 0);
Native.epollWait(epollFd.intValue(), events, 0);
} catch (IOException ignore) {
// ignore on close
}
@ -300,9 +301,9 @@ final class EpollEventLoop extends SingleThreadEventLoop {
private void processReady(EpollEventArray events, int ready) {
for (int i = 0; i < ready; i ++) {
final int fd = events.fd(i);
if (fd == eventFd) {
if (fd == eventFd.intValue()) {
// consume wakeup event
Native.eventFdRead(eventFd);
Native.eventFdRead(eventFd.intValue());
} else {
final long ev = events.events(i);
@ -346,7 +347,7 @@ final class EpollEventLoop extends SingleThreadEventLoop {
} else {
// We received an event for an fd which we not use anymore. Remove it from the epoll_event set.
try {
Native.epollCtlDel(epollFd, fd);
Native.epollCtlDel(epollFd.intValue(), fd);
} catch (IOException ignore) {
// This can happen but is nothing we need to worry about as we only try to delete
// the fd from the epoll set as we not found it in our mappings. So this call to
@ -362,12 +363,12 @@ final class EpollEventLoop extends SingleThreadEventLoop {
protected void cleanup() {
try {
try {
Native.close(epollFd);
epollFd.close();
} catch (IOException e) {
logger.warn("Failed to close the epoll fd.", e);
}
try {
Native.close(eventFd);
eventFd.close();
} catch (IOException e) {
logger.warn("Failed to close the event fd.", e);
}

View File

@ -16,7 +16,6 @@
package io.netty.channel.epoll;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelOption;
import io.netty.channel.MessageSizeEstimator;
import io.netty.channel.RecvByteBufAllocator;
@ -96,11 +95,11 @@ public class EpollServerChannelConfig extends EpollChannelConfig {
}
public int getReceiveBufferSize() {
return Native.getReceiveBufferSize(channel.fd().intValue());
return channel.fd().getReceiveBufferSize();
}
public EpollServerChannelConfig setReceiveBufferSize(int receiveBufferSize) {
Native.setReceiveBufferSize(channel.fd().intValue(), receiveBufferSize);
channel.fd().setReceiveBufferSize(receiveBufferSize);
return this;
}

View File

@ -19,12 +19,15 @@ import io.netty.channel.Channel;
import io.netty.channel.unix.DomainSocketAddress;
import io.netty.channel.unix.FileDescriptor;
import io.netty.channel.unix.ServerDomainSocketChannel;
import io.netty.channel.unix.Socket;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import java.io.File;
import java.net.SocketAddress;
import static io.netty.channel.unix.Socket.newSocketDomain;
public final class EpollServerDomainSocketChannel extends AbstractEpollServerChannel
implements ServerDomainSocketChannel {
@ -35,19 +38,31 @@ public final class EpollServerDomainSocketChannel extends AbstractEpollServerCha
private volatile DomainSocketAddress local;
public EpollServerDomainSocketChannel() {
super(Native.socketDomainFd());
super(newSocketDomain(), false);
}
/**
* @deprecated Use {@link #EpollServerDomainSocketChannel(Socket, boolean)}.
* Creates a new {@link EpollServerDomainSocketChannel} from an existing {@link FileDescriptor}.
*/
public EpollServerDomainSocketChannel(FileDescriptor fd) {
super(fd);
}
/**
* @deprecated Use {@link #EpollServerDomainSocketChannel(Socket, boolean)}.
*/
public EpollServerDomainSocketChannel(Socket fd) {
super(fd);
}
public EpollServerDomainSocketChannel(Socket fd, boolean active) {
super(fd, active);
}
@Override
protected Channel newChildChannel(int fd, byte[] addr, int offset, int len) throws Exception {
return new EpollDomainSocketChannel(this, fd);
return new EpollDomainSocketChannel(this, new Socket(fd));
}
@Override
@ -57,9 +72,8 @@ public final class EpollServerDomainSocketChannel extends AbstractEpollServerCha
@Override
protected void doBind(SocketAddress localAddress) throws Exception {
int fd = fd().intValue();
Native.bind(fd, localAddress);
Native.listen(fd, config.getBacklog());
fd().bind(localAddress);
fd().listen(config.getBacklog());
local = (DomainSocketAddress) localAddress;
}

View File

@ -19,6 +19,7 @@ import io.netty.channel.Channel;
import io.netty.channel.EventLoop;
import io.netty.channel.socket.ServerSocketChannel;
import io.netty.channel.unix.FileDescriptor;
import io.netty.channel.unix.Socket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
@ -27,6 +28,9 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import static io.netty.channel.unix.NativeInetAddress.address;
import static io.netty.channel.unix.Socket.newSocketStream;
/**
* {@link ServerSocketChannel} implementation that uses linux EPOLL Edge-Triggered Mode for
* maximal performance.
@ -38,20 +42,40 @@ public final class EpollServerSocketChannel extends AbstractEpollServerChannel i
private volatile Collection<InetAddress> tcpMd5SigAddresses = Collections.emptyList();
public EpollServerSocketChannel() {
super(Native.socketStreamFd());
super(newSocketStream(), false);
config = new EpollServerSocketChannelConfig(this);
}
/**
* @deprecated Use {@link #EpollServerSocketChannel(Socket, boolean)}.
* Creates a new {@link EpollServerSocketChannel} from an existing {@link FileDescriptor}.
*/
@Deprecated
public EpollServerSocketChannel(FileDescriptor fd) {
super(fd);
config = new EpollServerSocketChannelConfig(this);
// Must call this constructor to ensure this object's local address is configured correctly.
// The local address can only be obtained from a Socket object.
this(new Socket(fd.intValue()));
}
/**
* @deprecated Use {@link #EpollServerSocketChannel(Socket, boolean)}.
* Creates a new {@link EpollServerSocketChannel} from an existing {@link Socket}.
*/
@Deprecated
public EpollServerSocketChannel(Socket fd) {
super(fd);
// As we create an EpollServerSocketChannel from a FileDescriptor we should try to obtain the remote and local
// address from it. This is needed as the FileDescriptor may be bound already.
local = Native.localAddress(fd.intValue());
local = fd.localAddress();
config = new EpollServerSocketChannelConfig(this);
}
public EpollServerSocketChannel(Socket fd, boolean active) {
super(fd, active);
// As we create an EpollServerSocketChannel from a FileDescriptor we should try to obtain the remote and local
// address from it. This is needed as the FileDescriptor may be bound already.
local = fd.localAddress();
config = new EpollServerSocketChannelConfig(this);
}
@Override
@ -63,13 +87,12 @@ public final class EpollServerSocketChannel extends AbstractEpollServerChannel i
protected void doBind(SocketAddress localAddress) throws Exception {
InetSocketAddress addr = (InetSocketAddress) localAddress;
checkResolvable(addr);
int fd = fd().intValue();
Native.bind(fd, addr);
local = Native.localAddress(fd);
fd().bind(addr);
local = fd().localAddress();
if (Native.IS_SUPPORTING_TCP_FASTOPEN && config.getTcpFastopen() > 0) {
Native.setTcpFastopen(fd, config.getTcpFastopen());
Native.setTcpFastopen(fd().intValue(), config.getTcpFastopen());
}
Native.listen(fd, config.getBacklog());
fd().listen(config.getBacklog());
active = true;
}
@ -95,7 +118,7 @@ public final class EpollServerSocketChannel extends AbstractEpollServerChannel i
@Override
protected Channel newChildChannel(int fd, byte[] address, int offset, int len) throws Exception {
return new EpollSocketChannel(this, fd, Native.address(address, offset, len));
return new EpollSocketChannel(this, new Socket(fd), address(address, offset, len));
}
Collection<InetAddress> tcpMd5SigAddresses() {

View File

@ -22,6 +22,7 @@ import io.netty.channel.EventLoop;
import io.netty.channel.socket.ServerSocketChannel;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.unix.FileDescriptor;
import io.netty.channel.unix.Socket;
import io.netty.util.concurrent.GlobalEventExecutor;
import io.netty.util.internal.OneTimeTask;
@ -33,6 +34,8 @@ import java.util.Collections;
import java.util.Map;
import java.util.concurrent.Executor;
import static io.netty.channel.unix.Socket.newSocketStream;
/**
* {@link SocketChannel} implementation that uses linux EPOLL Edge-Triggered Mode for
* maximal performance.
@ -45,13 +48,13 @@ public final class EpollSocketChannel extends AbstractEpollStreamChannel impleme
private volatile InetSocketAddress remote;
private volatile Collection<InetAddress> tcpMd5SigAddresses = Collections.emptyList();
EpollSocketChannel(Channel parent, int fd, InetSocketAddress remote) {
EpollSocketChannel(Channel parent, Socket fd, InetSocketAddress remote) {
super(parent, fd);
config = new EpollSocketChannelConfig(this);
// Directly cache the remote and local addresses
// See https://github.com/netty/netty/issues/2359
this.remote = remote;
local = Native.localAddress(fd);
local = fd.localAddress();
if (parent instanceof EpollServerSocketChannel) {
tcpMd5SigAddresses = ((EpollServerSocketChannel) parent).tcpMd5SigAddresses();
@ -59,21 +62,33 @@ public final class EpollSocketChannel extends AbstractEpollStreamChannel impleme
}
public EpollSocketChannel() {
super(Native.socketStreamFd());
super(newSocketStream(), false);
config = new EpollSocketChannelConfig(this);
}
/**
* @deprecated Use {@link #EpollSocketChannel(Socket, boolean)}.
*/
@Deprecated
public EpollSocketChannel(FileDescriptor fd) {
super(fd);
// As we create an EpollSocketChannel from a FileDescriptor we should try to obtain the remote and local
// address from it. This is needed as the FileDescriptor may be bound/connected already.
remote = fd().remoteAddress();
local = fd().localAddress();
config = new EpollSocketChannelConfig(this);
}
/**
* Creates a new {@link EpollSocketChannel} from an existing {@link FileDescriptor}.
*/
public EpollSocketChannel(FileDescriptor fd) {
super(fd);
config = new EpollSocketChannelConfig(this);
public EpollSocketChannel(Socket fd, boolean active) {
super(fd, active);
// As we create an EpollSocketChannel from a FileDescriptor we should try to obtain the remote and local
// address from it. This is needed as the FileDescriptor may be bound/connected already.
remote = Native.remoteAddress(fd.intValue());
local = Native.localAddress(fd.intValue());
remote = fd.remoteAddress();
local = fd.localAddress();
config = new EpollSocketChannelConfig(this);
}
/**
@ -111,7 +126,7 @@ public final class EpollSocketChannel extends AbstractEpollStreamChannel impleme
protected SocketAddress remoteAddress0() {
if (remote == null) {
// Remote address not know, try to get it now.
InetSocketAddress address = Native.remoteAddress(fd().intValue());
InetSocketAddress address = fd().remoteAddress();
if (address != null) {
remote = address;
}
@ -123,9 +138,8 @@ public final class EpollSocketChannel extends AbstractEpollStreamChannel impleme
@Override
protected void doBind(SocketAddress local) throws Exception {
InetSocketAddress localAddress = (InetSocketAddress) local;
int fd = fd().intValue();
Native.bind(fd, localAddress);
this.local = Native.localAddress(fd);
fd().bind(localAddress);
this.local = fd().localAddress();
}
@Override
@ -190,7 +204,6 @@ public final class EpollSocketChannel extends AbstractEpollStreamChannel impleme
checkResolvable((InetSocketAddress) localAddress);
}
checkResolvable((InetSocketAddress) remoteAddress);
int fd = fd().intValue();
boolean connected = super.doConnect(remoteAddress, localAddress);
if (connected) {
remote = (InetSocketAddress) remoteAddress;
@ -199,7 +212,7 @@ public final class EpollSocketChannel extends AbstractEpollStreamChannel impleme
// We always need to set the localAddress even if not connected yet
//
// See https://github.com/netty/netty/issues/3463
local = Native.localAddress(fd);
local = fd().localAddress();
return connected;
}

View File

@ -147,17 +147,17 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
@Override
public int getReceiveBufferSize() {
return Native.getReceiveBufferSize(channel.fd().intValue());
return channel.fd().getReceiveBufferSize();
}
@Override
public int getSendBufferSize() {
return Native.getSendBufferSize(channel.fd().intValue());
return channel.fd().getSendBufferSize();
}
@Override
public int getSoLinger() {
return Native.getSoLinger(channel.fd().intValue());
return channel.fd().getSoLinger();
}
@Override
@ -167,7 +167,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
@Override
public boolean isKeepAlive() {
return Native.isKeepAlive(channel.fd().intValue()) == 1;
return channel.fd().isKeepAlive();
}
@Override
@ -177,14 +177,14 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
@Override
public boolean isTcpNoDelay() {
return Native.isTcpNoDelay(channel.fd().intValue()) == 1;
return channel.fd().isTcpNoDelay();
}
/**
* Get the {@code TCP_CORK} option on the socket. See {@code man 7 tcp} for more details.
*/
public boolean isTcpCork() {
return Native.isTcpCork(channel.fd().intValue()) == 1;
return channel.fd().isTcpCork();
}
/**
@ -225,7 +225,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
@Override
public EpollSocketChannelConfig setKeepAlive(boolean keepAlive) {
Native.setKeepAlive(channel.fd().intValue(), keepAlive ? 1 : 0);
channel.fd().setKeepAlive(keepAlive);
return this;
}
@ -237,7 +237,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
@Override
public EpollSocketChannelConfig setReceiveBufferSize(int receiveBufferSize) {
Native.setReceiveBufferSize(channel.fd().intValue(), receiveBufferSize);
channel.fd().setReceiveBufferSize(receiveBufferSize);
return this;
}
@ -249,19 +249,19 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
@Override
public EpollSocketChannelConfig setSendBufferSize(int sendBufferSize) {
Native.setSendBufferSize(channel.fd().intValue(), sendBufferSize);
channel.fd().setSendBufferSize(sendBufferSize);
return this;
}
@Override
public EpollSocketChannelConfig setSoLinger(int soLinger) {
Native.setSoLinger(channel.fd().intValue(), soLinger);
channel.fd().setSoLinger(soLinger);
return this;
}
@Override
public EpollSocketChannelConfig setTcpNoDelay(boolean tcpNoDelay) {
Native.setTcpNoDelay(channel.fd().intValue(), tcpNoDelay ? 1 : 0);
channel.fd().setTcpNoDelay(tcpNoDelay);
return this;
}
@ -269,7 +269,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
* Set the {@code TCP_CORK} option on the socket. See {@code man 7 tcp} for more details.
*/
public EpollSocketChannelConfig setTcpCork(boolean tcpCork) {
Native.setTcpCork(channel.fd().intValue(), tcpCork ? 1 : 0);
channel.fd().setTcpCork(tcpCork);
return this;
}

View File

@ -15,34 +15,31 @@
*/
package io.netty.channel.epoll;
import io.netty.channel.ChannelException;
import io.netty.channel.DefaultFileRegion;
import io.netty.channel.unix.DomainSocketAddress;
import io.netty.util.CharsetUtil;
import io.netty.util.internal.EmptyArrays;
import io.netty.channel.unix.Errors.NativeIoException;
import io.netty.channel.unix.FileDescriptor;
import io.netty.channel.unix.NativeInetAddress;
import io.netty.util.internal.NativeLibraryLoader;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.SystemPropertyUtil;
import java.io.IOException;
import java.net.ConnectException;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.util.Locale;
import static io.netty.channel.unix.Errors.ERRNO_EAGAIN_NEGATIVE;
import static io.netty.channel.unix.Errors.ERRNO_EPIPE_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.newConnectionResetException;
import static io.netty.channel.unix.Errors.newIOException;
/**
* Native helper methods
*
* <strong>Internal usage only!</strong>
*/
public final class Native {
static {
String name = SystemPropertyUtil.get("os.name").toLowerCase(Locale.UK).trim();
if (!name.startsWith("linux")) {
@ -65,95 +62,33 @@ public final class Native {
public static final long SSIZE_MAX = ssizeMax();
public static final int TCP_MD5SIG_MAXKEYLEN = tcpMd5SigMaxKeyLen();
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_ENOTCONN_NEGATIVE = -errnoENOTCONN();
private static final int ERRNO_EBADF_NEGATIVE = -errnoEBADF();
private static final int ERRNO_EPIPE_NEGATIVE = -errnoEPIPE();
private static final int ERRNO_ECONNRESET_NEGATIVE = -errnoECONNRESET();
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;
private static final IOException CONNECTION_RESET_EXCEPTION_SPLICE;
private static final NativeIoException CONNECTION_RESET_EXCEPTION_SENDFILE;
private static final NativeIoException CONNECTION_RESET_EXCEPTION_SENDMMSG;
private static final NativeIoException CONNECTION_RESET_EXCEPTION_SPLICE;
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_READ = newConnectionResetException("syscall:read(...)",
ERRNO_ECONNRESET_NEGATIVE);
CONNECTION_RESET_EXCEPTION_WRITE = newConnectionResetException("syscall:write(...)",
ERRNO_EPIPE_NEGATIVE);
CONNECTION_RESET_EXCEPTION_WRITEV = newConnectionResetException("syscall:writev(...)",
ERRNO_EPIPE_NEGATIVE);
CONNECTION_RESET_EXCEPTION_SENDFILE = newConnectionResetException("syscall:sendfile(...)",
ERRNO_EPIPE_NEGATIVE);
CONNECTION_RESET_EXCEPTION_SENDTO = newConnectionResetException("syscall:sendto(...)",
ERRNO_EPIPE_NEGATIVE);
CONNECTION_RESET_EXCEPTION_SENDMSG = newConnectionResetException("syscall:sendmsg(...)",
ERRNO_EPIPE_NEGATIVE);
CONNECTION_RESET_EXCEPTION_SENDMMSG = newConnectionResetException("syscall:sendmmsg(...)",
ERRNO_EPIPE_NEGATIVE);
CONNECTION_RESET_EXCEPTION_SPLICE = newConnectionResetException("syscall:splice(...)",
ERRNO_EPIPE_NEGATIVE);
CLOSED_CHANNEL_EXCEPTION = new ClosedChannelException();
CLOSED_CHANNEL_EXCEPTION.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
}
private static IOException newConnectionResetException(String method, int errnoNegative) {
IOException exception = newIOException(method, errnoNegative);
exception.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
return exception;
public static FileDescriptor newEventFd() {
return new FileDescriptor(eventFd());
}
public 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 || err == ERRNO_ECONNRESET_NEGATIVE) {
throw resetCause;
}
if (err == ERRNO_EBADF_NEGATIVE || err == ERRNO_ENOTCONN_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();
private static native int eventFd();
public static native void eventFdWrite(int fd, long value);
public static native void eventFdRead(int fd);
public static native int epollCreate();
public static FileDescriptor newEpollCreate() {
return new FileDescriptor(epollCreate());
}
private static native int epollCreate();
public static int epollWait(int efd, EpollEventArray events, int timeout) throws IOException {
int ready = epollWait0(efd, events.memoryAddress(), events.length(), timeout);
if (ready < 0) {
@ -187,26 +122,7 @@ public final class Native {
}
private static native int epollCtlDel0(int efd, final int fd);
private static native int errnoEBADF();
private static native int errnoEPIPE();
private static native int errnoECONNRESET();
private static native int errnoENOTCONN();
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 void close(int fd) throws IOException {
int res = close0(fd);
if (res < 0) {
throw newIOException("close", res);
}
}
private static native int close0(int fd);
public static int splice(int fd, long offIn, int fdOut, long offOut, long len) throws IOException {
int res = splice0(fd, offIn, fdOut, offOut, len);
if (res >= 0) {
@ -217,83 +133,6 @@ public final class Native {
private static native int splice0(int fd, long offIn, int fdOut, long offOut, long len);
public static long pipe() throws IOException {
long res = pipe0();
if (res >= 0) {
return res;
}
throw newIOException("pipe", (int) res);
}
private static native long pipe0();
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);
}
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("writeAddress", 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("writevAddresses", (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("readAddress", 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 {
// Open the file-region as it may be created via the lazy constructor. This is needed as we directly access
@ -310,84 +149,6 @@ public final class Native {
private static native long sendfile0(
int dest, DefaultFileRegion src, long baseOffset, long offset, long length) throws IOException;
public static int sendTo(
int fd, ByteBuffer buf, int pos, int limit, InetAddress addr, int port) throws IOException {
// just duplicate the toNativeInetAddress code here to minimize object creation as this method is expected
// to be called frequently
byte[] address;
int scopeId;
if (addr instanceof Inet6Address) {
address = addr.getAddress();
scopeId = ((Inet6Address) addr).getScopeId();
} else {
// convert to ipv4 mapped ipv6 address;
scopeId = 0;
address = ipv4MappedIpv6Address(addr.getAddress());
}
int res = sendTo0(fd, buf, pos, limit, address, scopeId, port);
if (res >= 0) {
return res;
}
return ioResult("sendTo", res, CONNECTION_RESET_EXCEPTION_SENDTO);
}
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 {
// just duplicate the toNativeInetAddress code here to minimize object creation as this method is expected
// to be called frequently
byte[] address;
int scopeId;
if (addr instanceof Inet6Address) {
address = addr.getAddress();
scopeId = ((Inet6Address) addr).getScopeId();
} else {
// convert to ipv4 mapped ipv6 address;
scopeId = 0;
address = ipv4MappedIpv6Address(addr.getAddress());
}
int res = sendToAddress0(fd, memoryAddress, pos, limit, address, scopeId, port);
if (res >= 0) {
return res;
}
return ioResult("sendToAddress", res, CONNECTION_RESET_EXCEPTION_SENDTO);
}
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 {
// just duplicate the toNativeInetAddress code here to minimize object creation as this method is expected
// to be called frequently
byte[] address;
int scopeId;
if (addr instanceof Inet6Address) {
address = addr.getAddress();
scopeId = ((Inet6Address) addr).getScopeId();
} else {
// convert to ipv4 mapped ipv6 address;
scopeId = 0;
address = ipv4MappedIpv6Address(addr.getAddress());
}
int res = sendToAddresses(fd, memoryAddress, length, address, scopeId, port);
if (res >= 0) {
return res;
}
return ioResult("sendToAddresses", res, CONNECTION_RESET_EXCEPTION_SENDMSG);
}
private static native int sendToAddresses(
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;
public static native EpollDatagramChannel.DatagramSocketAddress recvFromAddress(
int fd, long memoryAddress, int pos, int limit) throws IOException;
public static int sendmmsg(
int fd, NativeDatagramPacketArray.NativeDatagramPacket[] msgs, int offset, int len) throws IOException {
int res = sendmmsg0(fd, msgs, offset, len);
@ -403,188 +164,6 @@ public final class Native {
private static native boolean isSupportingSendmmsg();
private static native boolean isSupportingTcpFastopen();
// socket operations
public static int socketStreamFd() {
int res = socketStream();
if (res < 0) {
throw new ChannelException(newIOException("socketStreamFd", res));
}
return res;
}
public static int socketDgramFd() {
int res = socketDgram();
if (res < 0) {
throw new ChannelException(newIOException("socketDgramFd", res));
}
return res;
}
public static int socketDomainFd() {
int res = socketDomain();
if (res < 0) {
throw new ChannelException(newIOException("socketDomain", res));
}
return res;
}
private static native int socketStream();
private static native int socketDgram();
private static native int socketDomain();
public static void bind(int fd, SocketAddress socketAddress) throws IOException {
if (socketAddress instanceof InetSocketAddress) {
InetSocketAddress addr = (InetSocketAddress) socketAddress;
NativeInetAddress address = toNativeInetAddress(addr.getAddress());
int res = bind(fd, address.address, address.scopeId, addr.getPort());
if (res < 0) {
throw newIOException("bind", res);
}
} else if (socketAddress instanceof DomainSocketAddress) {
DomainSocketAddress addr = (DomainSocketAddress) socketAddress;
int res = bindDomainSocket(fd, addr.path().getBytes(CharsetUtil.UTF_8));
if (res < 0) {
throw newIOException("bind", res);
}
} else {
throw new Error("Unexpected SocketAddress implementation " + socketAddress);
}
}
private static native int bind(int fd, byte[] address, int scopeId, int port);
private static native int bindDomainSocket(int fd, byte[] path);
public static void listen(int fd, int backlog) throws IOException {
int res = listen0(fd, backlog);
if (res < 0) {
throw newIOException("listen", res);
}
}
private static native int listen0(int fd, int backlog);
public static boolean connect(int fd, SocketAddress socketAddress) throws IOException {
int res;
if (socketAddress instanceof InetSocketAddress) {
InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress;
NativeInetAddress address = toNativeInetAddress(inetSocketAddress.getAddress());
res = connect(fd, address.address, address.scopeId, inetSocketAddress.getPort());
} else if (socketAddress instanceof DomainSocketAddress) {
DomainSocketAddress unixDomainSocketAddress = (DomainSocketAddress) socketAddress;
res = connectDomainSocket(fd, unixDomainSocketAddress.path().getBytes(CharsetUtil.UTF_8));
} else {
throw new Error("Unexpected SocketAddress implementation " + socketAddress);
}
if (res < 0) {
if (res == ERRNO_EINPROGRESS_NEGATIVE) {
// connect not complete yet need to wait for EPOLLOUT event
return false;
}
throw newConnectException("connect", res);
}
return true;
}
private static native int connect(int fd, byte[] address, int scopeId, int port);
private static native int connectDomainSocket(int fd, byte[] path);
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 newConnectException("finishConnect", res);
}
return true;
}
private static native int finishConnect0(int fd);
private static ConnectException newConnectException(String method, int err) {
return new ConnectException(method + "() failed: " + ERRORS[-err]);
}
public static InetSocketAddress remoteAddress(int fd) {
byte[] addr = remoteAddress0(fd);
// addr may be null if getpeername failed.
// See https://github.com/netty/netty/issues/3328
if (addr == null) {
return null;
}
return address(addr, 0, addr.length);
}
public static InetSocketAddress localAddress(int fd) {
byte[] addr = localAddress0(fd);
// addr may be null if getpeername failed.
// See https://github.com/netty/netty/issues/3328
if (addr == null) {
return null;
}
return address(addr, 0, addr.length);
}
static InetSocketAddress address(byte[] addr, int offset, int len) {
// The last 4 bytes are always the port
final int port = decodeInt(addr, offset + len - 4);
final InetAddress address;
try {
switch (len) {
// 8 bytes:
// - 4 == ipaddress
// - 4 == port
case 8:
byte[] ipv4 = new byte[4];
System.arraycopy(addr, offset, ipv4, 0, 4);
address = InetAddress.getByAddress(ipv4);
break;
// 24 bytes:
// - 16 == ipaddress
// - 4 == scopeId
// - 4 == port
case 24:
byte[] ipv6 = new byte[16];
System.arraycopy(addr, offset, ipv6, 0, 16);
int scopeId = decodeInt(addr, offset + len - 8);
address = Inet6Address.getByAddress(null, ipv6, scopeId);
break;
default:
throw new Error();
}
return new InetSocketAddress(address, port);
} catch (UnknownHostException e) {
throw new Error("Should never happen", e);
}
}
static int decodeInt(byte[] addr, int index) {
return (addr[index] & 0xff) << 24 |
(addr[index + 1] & 0xff) << 16 |
(addr[index + 2] & 0xff) << 8 |
addr[index + 3] & 0xff;
}
private static native byte[] remoteAddress0(int fd);
private static native byte[] localAddress0(int fd);
public static int accept(int fd, byte[] addr) throws IOException {
int res = accept0(fd, addr);
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, byte[] addr);
public static int recvFd(int fd) throws IOException {
int res = recvFd0(fd);
if (res > 0) {
@ -617,44 +196,22 @@ public final class Native {
private static native int sendFd0(int socketFd, 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);
public static native int getSendBufferSize(int fd);
public static native int isKeepAlive(int fd);
public static native int isReuseAddress(int fd);
public static native int isReusePort(int fd);
public static native int isTcpNoDelay(int fd);
public static native int isTcpCork(int fd);
public static native int getTcpNotSentLowAt(int fd);
public static native int getSoLinger(int fd);
public static native int getTrafficClass(int fd);
public static native int isBroadcast(int fd);
public static native int getTcpKeepIdle(int fd);
public static native int getTcpKeepIntvl(int fd);
public static native int getTcpKeepCnt(int fd);
public static native int getTcpUserTimeout(int milliseconds);
public static native int getSoError(int fd);
public static native int isIpFreeBind(int fd);
public static native void setKeepAlive(int fd, int keepAlive);
public static native void setReceiveBufferSize(int fd, int receiveBufferSize);
public static native void setReuseAddress(int fd, int reuseAddress);
public static native void setReusePort(int fd, int reuseAddress);
public static native void setSendBufferSize(int fd, int sendBufferSize);
public static native void setTcpNoDelay(int fd, int tcpNoDelay);
public static native void setTcpCork(int fd, int tcpCork);
public static native void setTcpFastopen(int fd, int tcpFastopenBacklog);
public static native void setTcpNotSentLowAt(int fd, int tcpNotSentLowAt);
public static native void setSoLinger(int fd, int soLinger);
public static native void setTrafficClass(int fd, int tcpNoDelay);
public static native void setBroadcast(int fd, int broadcast);
public static native void setTcpKeepIdle(int fd, int seconds);
@ -669,43 +226,12 @@ public final class Native {
private static native void tcpInfo0(int fd, int[] array);
public static void setTcpMd5Sig(int fd, InetAddress address, byte[] key) {
final NativeInetAddress a = toNativeInetAddress(address);
setTcpMd5Sig0(fd, a.address, a.scopeId, key);
final NativeInetAddress a = NativeInetAddress.newInstance(address);
setTcpMd5Sig0(fd, a.address(), a.scopeId(), key);
}
private static native void setTcpMd5Sig0(int fd, byte[] address, int scopeId, byte[] key);
private static NativeInetAddress toNativeInetAddress(InetAddress addr) {
byte[] bytes = addr.getAddress();
if (addr instanceof Inet6Address) {
return new NativeInetAddress(bytes, ((Inet6Address) addr).getScopeId());
} else {
// convert to ipv4 mapped ipv6 address;
return new NativeInetAddress(ipv4MappedIpv6Address(bytes));
}
}
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;
NativeInetAddress(byte[] address, int scopeId) {
this.address = address;
this.scopeId = scopeId;
}
NativeInetAddress(byte[] address) {
this(address, 0);
}
}
public static native String kernelVersion();
private static native int iovMax();

View File

@ -15,6 +15,7 @@
*/
package io.netty.channel.epoll;
import static io.netty.channel.unix.NativeInetAddress.ipv4MappedIpv6Address;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelOutboundBuffer;
import io.netty.channel.socket.DatagramPacket;
@ -148,7 +149,7 @@ final class NativeDatagramPacketArray implements ChannelOutboundBuffer.MessagePr
addr = address.getAddress();
scopeId = ((Inet6Address) address).getScopeId();
} else {
addr = Native.ipv4MappedIpv6Address(address.getAddress());
addr = ipv4MappedIpv6Address(address.getAddress());
scopeId = 0;
}
port = recipient.getPort();

View File

@ -0,0 +1,40 @@
/*
* Copyright 2015 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.channel.unix;
import java.net.InetSocketAddress;
/**
* Act as special {@link InetSocketAddress} to be able to easily pass all needed data from JNI without the need
* to create more objects then needed.
* <p>
* <strong>Internal usage only!</strong>
*/
public final class DatagramSocketAddress extends InetSocketAddress {
private static final long serialVersionUID = 3094819287843178401L;
// holds the amount of received bytes
private final int receivedAmount;
DatagramSocketAddress(String addr, int port, int receivedAmount) {
super(addr, port);
this.receivedAmount = receivedAmount;
}
public int receivedAmount() {
return receivedAmount;
}
}

View File

@ -0,0 +1,134 @@
/*
* Copyright 2015 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.channel.unix;
import io.netty.util.internal.EmptyArrays;
import java.io.IOException;
import java.net.ConnectException;
import java.nio.channels.ClosedChannelException;
/**
* <strong>Internal usage only!</strong>
*/
public final class Errors {
// As all our JNI methods return -errno on error we need to compare with the negative errno codes.
public static final int ERRNO_ENOTCONN_NEGATIVE = -errnoENOTCONN();
public static final int ERRNO_EBADF_NEGATIVE = -errnoEBADF();
public static final int ERRNO_EPIPE_NEGATIVE = -errnoEPIPE();
public static final int ERRNO_ECONNRESET_NEGATIVE = -errnoECONNRESET();
public static final int ERRNO_EAGAIN_NEGATIVE = -errnoEAGAIN();
public static final int ERRNO_EWOULDBLOCK_NEGATIVE = -errnoEWOULDBLOCK();
public 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 512 should be more then enough because errno.h only holds < 200 codes.
*/
private static final String[] ERRORS = new String[512];
// Pre-instantiated exceptions which does not need any stacktrace and
// can be thrown multiple times for performance reasons.
static final ClosedChannelException CLOSED_CHANNEL_EXCEPTION;
static final NativeIoException CONNECTION_NOT_CONNECTED_SHUTDOWN_EXCEPTION;
static final NativeIoException CONNECTION_RESET_EXCEPTION_WRITE;
static final NativeIoException CONNECTION_RESET_EXCEPTION_WRITEV;
static final NativeIoException CONNECTION_RESET_EXCEPTION_READ;
static final NativeIoException CONNECTION_RESET_EXCEPTION_SENDTO;
static final NativeIoException CONNECTION_RESET_EXCEPTION_SENDMSG;
/**
* <strong>Internal usage only!</strong>
*/
public static final class NativeIoException extends IOException {
private static final long serialVersionUID = 8222160204268655526L;
private final int expectedErr;
public NativeIoException(String method, int expectedErr) {
super(method);
this.expectedErr = expectedErr;
}
public int expectedErr() {
return expectedErr;
}
}
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_READ = newConnectionResetException("syscall:read(...)",
ERRNO_ECONNRESET_NEGATIVE);
CONNECTION_RESET_EXCEPTION_WRITE = newConnectionResetException("syscall:write(...)",
ERRNO_EPIPE_NEGATIVE);
CONNECTION_RESET_EXCEPTION_WRITEV = newConnectionResetException("syscall:writev(...)",
ERRNO_EPIPE_NEGATIVE);
CONNECTION_RESET_EXCEPTION_SENDTO = newConnectionResetException("syscall:sendto(...)",
ERRNO_EPIPE_NEGATIVE);
CONNECTION_RESET_EXCEPTION_SENDMSG = newConnectionResetException("syscall:sendmsg(...)",
ERRNO_EPIPE_NEGATIVE);
CONNECTION_NOT_CONNECTED_SHUTDOWN_EXCEPTION = newConnectionResetException("syscall:shutdown(...)",
ERRNO_ENOTCONN_NEGATIVE);
CLOSED_CHANNEL_EXCEPTION = new ClosedChannelException();
CLOSED_CHANNEL_EXCEPTION.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
}
static ConnectException newConnectException(String method, int err) {
return new ConnectException(method + "() failed: " + ERRORS[-err]);
}
public static NativeIoException newConnectionResetException(String method, int errnoNegative) {
NativeIoException exception = newIOException(method, errnoNegative);
exception.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
return exception;
}
public static NativeIoException newIOException(String method, int err) {
return new NativeIoException(method + "() failed: " + ERRORS[-err], err);
}
public static int ioResult(String method, int err, NativeIoException resetCause) throws IOException {
// network stack saturated... try again later
if (err == ERRNO_EAGAIN_NEGATIVE || err == ERRNO_EWOULDBLOCK_NEGATIVE) {
return 0;
}
if (err == resetCause.expectedErr()) {
throw resetCause;
}
if (err == ERRNO_EBADF_NEGATIVE || err == ERRNO_ENOTCONN_NEGATIVE) {
throw CLOSED_CHANNEL_EXCEPTION;
}
// TODO: We could even go further and use a pre-instantiated IOException for the other error codes, but for
// all other errors it may be better to just include a stack trace.
throw newIOException(method, err);
}
private static native int errnoEBADF();
private static native int errnoEPIPE();
private static native int errnoECONNRESET();
private static native int errnoENOTCONN();
private static native int errnoEAGAIN();
private static native int errnoEWOULDBLOCK();
private static native int errnoEINPROGRESS();
private static native String strError(int err);
private Errors() { }
}

View File

@ -15,11 +15,15 @@
*/
package io.netty.channel.unix;
import io.netty.channel.epoll.Native;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import static io.netty.channel.unix.Errors.CONNECTION_RESET_EXCEPTION_READ;
import static io.netty.channel.unix.Errors.CONNECTION_RESET_EXCEPTION_WRITE;
import static io.netty.channel.unix.Errors.CONNECTION_RESET_EXCEPTION_WRITEV;
import static io.netty.channel.unix.Errors.ioResult;
import static io.netty.channel.unix.Errors.newIOException;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
/**
@ -27,7 +31,6 @@ import static io.netty.util.internal.ObjectUtil.checkNotNull;
* {@link FileDescriptor} for it.
*/
public class FileDescriptor {
private final int fd;
private volatile boolean open = true;
@ -50,7 +53,10 @@ public class FileDescriptor {
*/
public void close() throws IOException {
open = false;
close(fd);
int res = close(fd);
if (res < 0) {
throw newIOException("close", res);
}
}
/**
@ -60,6 +66,60 @@ public class FileDescriptor {
return open;
}
public final int write(ByteBuffer buf, int pos, int limit) throws IOException {
int res = write(fd, buf, pos, limit);
if (res >= 0) {
return res;
}
return ioResult("write", res, CONNECTION_RESET_EXCEPTION_WRITE);
}
public final int writeAddress(long address, int pos, int limit) throws IOException {
int res = writeAddress(fd, address, pos, limit);
if (res >= 0) {
return res;
}
return ioResult("writeAddress", res, CONNECTION_RESET_EXCEPTION_WRITE);
}
public final long writev(ByteBuffer[] buffers, int offset, int length) throws IOException {
long res = writev(fd, buffers, offset, length);
if (res >= 0) {
return res;
}
return ioResult("writev", (int) res, CONNECTION_RESET_EXCEPTION_WRITEV);
}
public final long writevAddresses(long memoryAddress, int length) throws IOException {
long res = writevAddresses(fd, memoryAddress, length);
if (res >= 0) {
return res;
}
return ioResult("writevAddresses", (int) res, CONNECTION_RESET_EXCEPTION_WRITEV);
}
public final int read(ByteBuffer buf, int pos, int limit) throws IOException {
int res = read(fd, buf, pos, limit);
if (res > 0) {
return res;
}
if (res == 0) {
return -1;
}
return ioResult("read", res, CONNECTION_RESET_EXCEPTION_READ);
}
public final int readAddress(long address, int pos, int limit) throws IOException {
int res = readAddress(fd, address, pos, limit);
if (res > 0) {
return res;
}
if (res == 0) {
return -1;
}
return ioResult("readAddress", res, CONNECTION_RESET_EXCEPTION_READ);
}
@Override
public String toString() {
return "FileDescriptor{" +
@ -84,8 +144,6 @@ public class FileDescriptor {
return fd;
}
private static native int close(int fd);
/**
* Open a new {@link FileDescriptor} for the given path.
*/
@ -93,7 +151,7 @@ public class FileDescriptor {
checkNotNull(path, "path");
int res = open(path);
if (res < 0) {
throw Native.newIOException("open", res);
throw newIOException("open", res);
}
return new FileDescriptor(res);
}
@ -105,5 +163,27 @@ public class FileDescriptor {
return from(checkNotNull(file, "file").getPath());
}
/**
* @return [0] = read end, [1] = write end
*/
public static FileDescriptor[] pipe() throws IOException {
long res = newPipe();
if (res < 0) {
throw newIOException("newPipe", (int) res);
}
return new FileDescriptor[]{new FileDescriptor((int) (res >>> 32)), new FileDescriptor((int) res)};
}
private static native int open(String path);
private static native int close(int fd);
private static native int write(int fd, ByteBuffer buf, int pos, int limit);
private static native int writeAddress(int fd, long address, int pos, int limit);
private static native long writev(int fd, ByteBuffer[] buffers, int offset, int length);
private static native long writevAddresses(int fd, long memoryAddress, int length);
private static native int read(int fd, ByteBuffer buf, int pos, int limit);
private static native int readAddress(int fd, long address, int pos, int limit);
private static native long newPipe();
}

View File

@ -0,0 +1,107 @@
/*
* Copyright 2015 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.channel.unix;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
/**
* <strong>Internal usage only!</strong>
*/
public final class NativeInetAddress {
private static final byte[] IPV4_MAPPED_IPV6_PREFIX = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0xff, (byte) 0xff };
final byte[] address;
final int scopeId;
public static NativeInetAddress newInstance(InetAddress addr) {
byte[] bytes = addr.getAddress();
if (addr instanceof Inet6Address) {
return new NativeInetAddress(bytes, ((Inet6Address) addr).getScopeId());
} else {
// convert to ipv4 mapped ipv6 address;
return new NativeInetAddress(ipv4MappedIpv6Address(bytes));
}
}
public NativeInetAddress(byte[] address, int scopeId) {
this.address = address;
this.scopeId = scopeId;
}
public NativeInetAddress(byte[] address) {
this(address, 0);
}
public byte[] address() {
return address;
}
public int scopeId() {
return scopeId;
}
public 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;
}
public static InetSocketAddress address(byte[] addr, int offset, int len) {
// The last 4 bytes are always the port
final int port = decodeInt(addr, offset + len - 4);
final InetAddress address;
try {
switch (len) {
// 8 bytes:
// - 4 == ipaddress
// - 4 == port
case 8:
byte[] ipv4 = new byte[4];
System.arraycopy(addr, offset, ipv4, 0, 4);
address = InetAddress.getByAddress(ipv4);
break;
// 24 bytes:
// - 16 == ipaddress
// - 4 == scopeId
// - 4 == port
case 24:
byte[] ipv6 = new byte[16];
System.arraycopy(addr, offset, ipv6, 0, 16);
int scopeId = decodeInt(addr, offset + len - 8);
address = Inet6Address.getByAddress(null, ipv6, scopeId);
break;
default:
throw new Error();
}
return new InetSocketAddress(address, port);
} catch (UnknownHostException e) {
throw new Error("Should never happen", e);
}
}
static int decodeInt(byte[] addr, int index) {
return (addr[index] & 0xff) << 24 |
(addr[index + 1] & 0xff) << 16 |
(addr[index + 2] & 0xff) << 8 |
addr[index + 3] & 0xff;
}
}

View File

@ -0,0 +1,359 @@
/*
* Copyright 2015 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.channel.unix;
import io.netty.channel.ChannelException;
import io.netty.util.CharsetUtil;
import java.io.IOException;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import static io.netty.channel.unix.Errors.CONNECTION_NOT_CONNECTED_SHUTDOWN_EXCEPTION;
import static io.netty.channel.unix.Errors.CONNECTION_RESET_EXCEPTION_SENDMSG;
import static io.netty.channel.unix.Errors.CONNECTION_RESET_EXCEPTION_SENDTO;
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_EWOULDBLOCK_NEGATIVE;
import static io.netty.channel.unix.Errors.ioResult;
import static io.netty.channel.unix.Errors.newConnectException;
import static io.netty.channel.unix.Errors.newIOException;
import static io.netty.channel.unix.NativeInetAddress.address;
import static io.netty.channel.unix.NativeInetAddress.ipv4MappedIpv6Address;
/**
* Provides a JNI bridge to native socket operations.
* <strong>Internal usage only!</strong>
*/
public final class Socket extends FileDescriptor {
private volatile boolean inputShutdown;
private volatile boolean outputShutdown;
public Socket(int fd) {
super(fd);
}
public void shutdown(boolean read, boolean write) throws IOException {
inputShutdown = read || inputShutdown;
outputShutdown = write || outputShutdown;
int res = shutdown(intValue(), read, write);
if (res < 0) {
ioResult("shutdown", res, CONNECTION_NOT_CONNECTED_SHUTDOWN_EXCEPTION);
}
}
public boolean isShutdown() {
return isInputShutdown() && isOutputShutdown();
}
public boolean isInputShutdown() {
return inputShutdown;
}
public boolean isOutputShutdown() {
return outputShutdown;
}
public int sendTo(ByteBuffer buf, int pos, int limit, InetAddress addr, int port) throws IOException {
// just duplicate the toNativeInetAddress code here to minimize object creation as this method is expected
// to be called frequently
byte[] address;
int scopeId;
if (addr instanceof Inet6Address) {
address = addr.getAddress();
scopeId = ((Inet6Address) addr).getScopeId();
} else {
// convert to ipv4 mapped ipv6 address;
scopeId = 0;
address = ipv4MappedIpv6Address(addr.getAddress());
}
int res = sendTo(intValue(), buf, pos, limit, address, scopeId, port);
if (res >= 0) {
return res;
}
return ioResult("sendTo", res, CONNECTION_RESET_EXCEPTION_SENDTO);
}
public int sendToAddress(long memoryAddress, int pos, int limit, InetAddress addr, int port)
throws IOException {
// just duplicate the toNativeInetAddress code here to minimize object creation as this method is expected
// to be called frequently
byte[] address;
int scopeId;
if (addr instanceof Inet6Address) {
address = addr.getAddress();
scopeId = ((Inet6Address) addr).getScopeId();
} else {
// convert to ipv4 mapped ipv6 address;
scopeId = 0;
address = ipv4MappedIpv6Address(addr.getAddress());
}
int res = sendToAddress(intValue(), memoryAddress, pos, limit, address, scopeId, port);
if (res >= 0) {
return res;
}
return ioResult("sendToAddress", res, CONNECTION_RESET_EXCEPTION_SENDTO);
}
public int sendToAddresses(long memoryAddress, int length, InetAddress addr, int port) throws IOException {
// just duplicate the toNativeInetAddress code here to minimize object creation as this method is expected
// to be called frequently
byte[] address;
int scopeId;
if (addr instanceof Inet6Address) {
address = addr.getAddress();
scopeId = ((Inet6Address) addr).getScopeId();
} else {
// convert to ipv4 mapped ipv6 address;
scopeId = 0;
address = ipv4MappedIpv6Address(addr.getAddress());
}
int res = sendToAddresses(intValue(), memoryAddress, length, address, scopeId, port);
if (res >= 0) {
return res;
}
return ioResult("sendToAddresses", res, CONNECTION_RESET_EXCEPTION_SENDMSG);
}
public DatagramSocketAddress recvFrom(ByteBuffer buf, int pos, int limit) throws IOException {
return recvFrom(intValue(), buf, pos, limit);
}
public DatagramSocketAddress recvFromAddress(long memoryAddress, int pos, int limit) throws IOException {
return recvFromAddress(intValue(), memoryAddress, pos, limit);
}
public boolean connect(SocketAddress socketAddress) throws IOException {
int res;
if (socketAddress instanceof InetSocketAddress) {
InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress;
NativeInetAddress address = NativeInetAddress.newInstance(inetSocketAddress.getAddress());
res = connect(intValue(), address.address, address.scopeId, inetSocketAddress.getPort());
} else if (socketAddress instanceof DomainSocketAddress) {
DomainSocketAddress unixDomainSocketAddress = (DomainSocketAddress) socketAddress;
res = connectDomainSocket(intValue(), unixDomainSocketAddress.path().getBytes(CharsetUtil.UTF_8));
} else {
throw new Error("Unexpected SocketAddress implementation " + socketAddress);
}
if (res < 0) {
if (res == ERRNO_EINPROGRESS_NEGATIVE) {
// connect not complete yet need to wait for EPOLLOUT event
return false;
}
throw newConnectException("connect", res);
}
return true;
}
public boolean finishConnect() throws IOException {
int res = finishConnect(intValue());
if (res < 0) {
if (res == ERRNO_EINPROGRESS_NEGATIVE) {
// connect still in progress
return false;
}
throw newConnectException("finishConnect", res);
}
return true;
}
public void bind(SocketAddress socketAddress) throws IOException {
if (socketAddress instanceof InetSocketAddress) {
InetSocketAddress addr = (InetSocketAddress) socketAddress;
NativeInetAddress address = NativeInetAddress.newInstance(addr.getAddress());
int res = bind(intValue(), address.address, address.scopeId, addr.getPort());
if (res < 0) {
throw newIOException("bind", res);
}
} else if (socketAddress instanceof DomainSocketAddress) {
DomainSocketAddress addr = (DomainSocketAddress) socketAddress;
int res = bindDomainSocket(intValue(), addr.path().getBytes(CharsetUtil.UTF_8));
if (res < 0) {
throw newIOException("bind", res);
}
} else {
throw new Error("Unexpected SocketAddress implementation " + socketAddress);
}
}
public void listen(int backlog) throws IOException {
int res = listen(intValue(), backlog);
if (res < 0) {
throw newIOException("listen", res);
}
}
public int accept(byte[] addr) throws IOException {
int res = accept(intValue(), addr);
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);
}
public InetSocketAddress remoteAddress() {
byte[] addr = remoteAddress(intValue());
// addr may be null if getpeername failed.
// See https://github.com/netty/netty/issues/3328
if (addr == null) {
return null;
}
return address(addr, 0, addr.length);
}
public InetSocketAddress localAddress() {
byte[] addr = localAddress(intValue());
// addr may be null if getpeername failed.
// See https://github.com/netty/netty/issues/3328
if (addr == null) {
return null;
}
return address(addr, 0, addr.length);
}
public int getReceiveBufferSize() {
return getReceiveBufferSize(intValue());
}
public int getSendBufferSize() {
return getSendBufferSize(intValue());
}
public boolean isKeepAlive() {
return isKeepAlive(intValue()) != 0;
}
public boolean isTcpNoDelay() {
return isTcpNoDelay(intValue()) != 0;
}
public boolean isTcpCork() {
return isTcpCork(intValue()) != 0;
}
public int getSoLinger() {
return getSoLinger(intValue());
}
public int getSoError() {
return getSoError(intValue());
}
public void setKeepAlive(boolean keepAlive) {
setKeepAlive(intValue(), keepAlive ? 1 : 0);
}
public void setReceiveBufferSize(int receiveBufferSize) {
setReceiveBufferSize(intValue(), receiveBufferSize);
}
public void setSendBufferSize(int sendBufferSize) {
setSendBufferSize(intValue(), sendBufferSize);
}
public void setTcpNoDelay(boolean tcpNoDelay) {
setTcpNoDelay(intValue(), tcpNoDelay ? 1 : 0);
}
public void setTcpCork(boolean tcpCork) {
setTcpCork(intValue(), tcpCork ? 1 : 0);
}
public void setSoLinger(int soLinger) {
setSoLinger(intValue(), soLinger);
}
@Override
public String toString() {
return "Socket{" +
"fd=" + intValue() +
'}';
}
public static Socket newSocketStream() {
int res = newSocketStreamFd();
if (res < 0) {
throw new ChannelException(newIOException("newSocketStream", res));
}
return new Socket(res);
}
public static Socket newSocketDgram() {
int res = newSocketDgramFd();
if (res < 0) {
throw new ChannelException(newIOException("newSocketDgram", res));
}
return new Socket(res);
}
public static Socket newSocketDomain() {
int res = newSocketDomainFd();
if (res < 0) {
throw new ChannelException(newIOException("newSocketDomain", res));
}
return new Socket(res);
}
private static native int shutdown(int fd, boolean read, boolean write);
private static native int connect(int fd, byte[] address, int scopeId, int port);
private static native int connectDomainSocket(int fd, byte[] path);
private static native int finishConnect(int fd);
private static native int bind(int fd, byte[] address, int scopeId, int port);
private static native int bindDomainSocket(int fd, byte[] path);
private static native int listen(int fd, int backlog);
private static native int accept(int fd, byte[] addr);
private static native byte[] remoteAddress(int fd);
private static native byte[] localAddress(int fd);
private static native int sendTo(
int fd, ByteBuffer buf, int pos, int limit, byte[] address, int scopeId, int port);
private static native int sendToAddress(
int fd, long memoryAddress, int pos, int limit, byte[] address, int scopeId, int port);
private static native int sendToAddresses(
int fd, long memoryAddress, int length, byte[] address, int scopeId, int port);
private static native DatagramSocketAddress recvFrom(
int fd, ByteBuffer buf, int pos, int limit) throws IOException;
private static native DatagramSocketAddress recvFromAddress(
int fd, long memoryAddress, int pos, int limit) throws IOException;
private static native int newSocketStreamFd();
private static native int newSocketDgramFd();
private static native int newSocketDomainFd();
private static native int getReceiveBufferSize(int fd);
private static native int getSendBufferSize(int fd);
private static native int isKeepAlive(int fd);
private static native int isTcpNoDelay(int fd);
private static native int isTcpCork(int fd);
private static native int getSoLinger(int fd);
private static native int getSoError(int fd);
private static native void setKeepAlive(int fd, int keepAlive);
private static native void setReceiveBufferSize(int fd, int receiveBufferSize);
private static native void setSendBufferSize(int fd, int sendBufferSize);
private static native void setTcpNoDelay(int fd, int tcpNoDelay);
private static native void setTcpCork(int fd, int tcpCork);
private static native void setSoLinger(int fd, int soLinger);
}

View File

@ -145,7 +145,6 @@ public class EpollSocketChannelTest {
Bootstrap b = new Bootstrap();
b.group(group);
b.channel(channelClass);
b.option(ChannelOption.SO_KEEPALIVE, true);
b.remoteAddress(serverChannel.localAddress());
b.handler(new MyInitializer());
clientChannel = b.connect().syncUninterruptibly().channel();

View File

@ -23,6 +23,8 @@ import java.net.Inet6Address;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import static io.netty.channel.unix.NativeInetAddress.address;
public class NativeTest {
@Test
@ -32,7 +34,7 @@ public class NativeTest {
ByteBuffer buffer = ByteBuffer.wrap(bytes);
buffer.put(inetAddress.getAddress().getAddress());
buffer.putInt(inetAddress.getPort());
Assert.assertEquals(inetAddress, Native.address(buffer.array(), 0, bytes.length));
Assert.assertEquals(inetAddress, address(buffer.array(), 0, bytes.length));
}
@Test
@ -44,6 +46,6 @@ public class NativeTest {
buffer.put(address.getAddress());
buffer.putInt(address.getScopeId());
buffer.putInt(inetAddress.getPort());
Assert.assertEquals(inetAddress, Native.address(buffer.array(), 0, bytes.length));
Assert.assertEquals(inetAddress, address(buffer.array(), 0, bytes.length));
}
}