2014-02-15 22:26:36 +01:00
|
|
|
/*
|
|
|
|
* Copyright 2013 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.
|
|
|
|
*/
|
2014-09-12 21:30:50 +02:00
|
|
|
#define _GNU_SOURCE
|
2014-02-15 22:26:36 +01:00
|
|
|
#include <jni.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <sys/epoll.h>
|
|
|
|
#include <sys/eventfd.h>
|
|
|
|
#include <sys/sendfile.h>
|
|
|
|
#include <netinet/tcp.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <fcntl.h>
|
2014-04-11 15:54:31 +02:00
|
|
|
#include <sys/utsname.h>
|
2014-02-15 22:26:36 +01:00
|
|
|
#include "io_netty_channel_epoll_Native.h"
|
|
|
|
|
|
|
|
// optional
|
2014-12-30 04:20:43 +01:00
|
|
|
extern int accept4(int sockFd, struct sockaddr* addr, socklen_t* addrlen, int flags) __attribute__((weak));
|
2014-04-04 07:45:23 +02:00
|
|
|
extern int epoll_create1(int flags) __attribute__((weak));
|
2014-09-12 21:30:50 +02:00
|
|
|
|
|
|
|
#ifdef IO_NETTY_SENDMMSG_NOT_FOUND
|
2014-12-30 04:20:43 +01:00
|
|
|
extern int sendmmsg(int sockfd, struct mmsghdr* msgvec, unsigned int vlen, unsigned int flags) __attribute__((weak));
|
2014-09-03 14:54:44 +02:00
|
|
|
|
2014-09-12 21:30:50 +02:00
|
|
|
#ifndef __USE_GNU
|
2014-09-03 14:54:44 +02:00
|
|
|
struct mmsghdr {
|
|
|
|
struct msghdr msg_hdr; /* Message header */
|
|
|
|
unsigned int msg_len; /* Number of bytes transmitted */
|
|
|
|
};
|
2014-09-12 21:30:50 +02:00
|
|
|
#endif
|
|
|
|
#endif
|
2014-02-15 22:26:36 +01:00
|
|
|
|
|
|
|
// Those are initialized in the init(...) method and cached for performance reasons
|
2014-02-17 07:18:39 +01:00
|
|
|
jmethodID updatePosId = NULL;
|
2014-02-15 22:26:36 +01:00
|
|
|
jmethodID posId = NULL;
|
|
|
|
jmethodID limitId = NULL;
|
|
|
|
jfieldID posFieldId = NULL;
|
|
|
|
jfieldID limitFieldId = NULL;
|
|
|
|
jfieldID fileChannelFieldId = NULL;
|
|
|
|
jfieldID transferedFieldId = NULL;
|
|
|
|
jfieldID fdFieldId = NULL;
|
2014-09-03 14:54:44 +02:00
|
|
|
jfieldID fileDescriptorFieldId = NULL;
|
|
|
|
|
|
|
|
jfieldID packetAddrFieldId = NULL;
|
|
|
|
jfieldID packetScopeIdFieldId = NULL;
|
|
|
|
jfieldID packetPortFieldId = NULL;
|
|
|
|
jfieldID packetMemoryAddressFieldId = NULL;
|
|
|
|
jfieldID packetCountFieldId = NULL;
|
|
|
|
|
2014-02-15 22:26:36 +01:00
|
|
|
jmethodID inetSocketAddrMethodId = NULL;
|
2014-04-11 21:08:26 +02:00
|
|
|
jmethodID datagramSocketAddrMethodId = NULL;
|
2014-02-15 22:26:36 +01:00
|
|
|
jclass runtimeExceptionClass = NULL;
|
|
|
|
jclass ioExceptionClass = NULL;
|
|
|
|
jclass closedChannelExceptionClass = NULL;
|
|
|
|
jmethodID closedChannelExceptionMethodId = NULL;
|
|
|
|
jclass inetSocketAddressClass = NULL;
|
2014-04-11 21:08:26 +02:00
|
|
|
jclass datagramSocketAddressClass = NULL;
|
2014-09-03 14:54:44 +02:00
|
|
|
jclass nativeDatagramPacketClass = NULL;
|
2014-04-11 21:08:26 +02:00
|
|
|
|
2014-02-15 22:26:36 +01:00
|
|
|
static int socketType;
|
2014-12-30 04:20:43 +01:00
|
|
|
static const char* ip4prefix = "::ffff:";
|
2014-02-15 22:26:36 +01:00
|
|
|
|
|
|
|
// util methods
|
2014-12-30 04:20:43 +01:00
|
|
|
void throwRuntimeException(JNIEnv* env, char* message) {
|
2014-02-15 22:26:36 +01:00
|
|
|
(*env)->ThrowNew(env, runtimeExceptionClass, message);
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
void throwIOException(JNIEnv* env, char* message) {
|
2014-02-15 22:26:36 +01:00
|
|
|
(*env)->ThrowNew(env, ioExceptionClass, message);
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
void throwClosedChannelException(JNIEnv* env) {
|
2014-02-15 22:26:36 +01:00
|
|
|
jobject exception = (*env)->NewObject(env, closedChannelExceptionClass, closedChannelExceptionMethodId);
|
|
|
|
(*env)->Throw(env, exception);
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:48:37 +01:00
|
|
|
void throwOutOfMemoryError(JNIEnv* env) {
|
2014-02-15 22:26:36 +01:00
|
|
|
jclass exceptionClass = (*env)->FindClass(env, "java/lang/OutOfMemoryError");
|
2014-12-30 04:48:37 +01:00
|
|
|
(*env)->ThrowNew(env, exceptionClass, "");
|
2014-02-15 22:26:36 +01:00
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
char* exceptionMessage(char* msg, int error) {
|
|
|
|
char* err = strerror(error);
|
|
|
|
char* result = malloc(strlen(msg) + strlen(err) + 1);
|
2014-02-15 22:26:36 +01:00
|
|
|
strcpy(result, msg);
|
|
|
|
strcat(result, err);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
jint epollCtl(JNIEnv* env, jint efd, int op, jint fd, jint flags, jint id) {
|
2014-02-15 22:26:36 +01:00
|
|
|
uint32_t events = EPOLLET;
|
|
|
|
|
|
|
|
if (flags & EPOLL_ACCEPT) {
|
|
|
|
events |= EPOLLIN;
|
|
|
|
}
|
|
|
|
if (flags & EPOLL_READ) {
|
|
|
|
events |= EPOLLIN | EPOLLRDHUP;
|
|
|
|
}
|
|
|
|
if (flags & EPOLL_WRITE) {
|
|
|
|
events |= EPOLLOUT;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct epoll_event ev = {
|
|
|
|
.events = events,
|
|
|
|
// encode the id into the events
|
|
|
|
.data.u64 = (((uint64_t) id) << 32L)
|
|
|
|
};
|
|
|
|
|
|
|
|
return epoll_ctl(efd, op, fd, &ev);
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
jint getOption(JNIEnv* env, jint fd, int level, int optname, void* optval, socklen_t optlen) {
|
2014-02-15 22:26:36 +01:00
|
|
|
int code;
|
2014-03-09 13:41:14 +01:00
|
|
|
code = getsockopt(fd, level, optname, optval, &optlen);
|
2014-02-15 22:26:36 +01:00
|
|
|
if (code == 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
int err = errno;
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, exceptionMessage("getsockopt() failed: ", err));
|
2014-02-15 22:26:36 +01:00
|
|
|
return code;
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
int setOption(JNIEnv* env, jint fd, int level, int optname, const void* optval, socklen_t len) {
|
2014-02-15 22:26:36 +01:00
|
|
|
int rc = setsockopt(fd, level, optname, optval, len);
|
|
|
|
if (rc < 0) {
|
|
|
|
int err = errno;
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, exceptionMessage("setsockopt() failed: ", err));
|
2014-02-15 22:26:36 +01:00
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
jobject createInetSocketAddress(JNIEnv* env, struct sockaddr_storage addr) {
|
2014-02-15 22:26:36 +01:00
|
|
|
char ipstr[INET6_ADDRSTRLEN];
|
|
|
|
int port;
|
2014-09-07 13:38:00 +02:00
|
|
|
jstring ipString;
|
2014-02-15 22:26:36 +01:00
|
|
|
if (addr.ss_family == AF_INET) {
|
2014-12-30 04:20:43 +01:00
|
|
|
struct sockaddr_in* s = (struct sockaddr_in*) &addr;
|
2014-02-15 22:26:36 +01:00
|
|
|
port = ntohs(s->sin_port);
|
|
|
|
inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr);
|
2014-09-07 13:38:00 +02:00
|
|
|
ipString = (*env)->NewStringUTF(env, ipstr);
|
2014-02-15 22:26:36 +01:00
|
|
|
} else {
|
2014-12-30 04:20:43 +01:00
|
|
|
struct sockaddr_in6* s = (struct sockaddr_in6*) &addr;
|
2014-02-15 22:26:36 +01:00
|
|
|
port = ntohs(s->sin6_port);
|
|
|
|
inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof ipstr);
|
2014-09-07 13:38:00 +02:00
|
|
|
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);
|
|
|
|
}
|
2014-02-15 22:26:36 +01:00
|
|
|
}
|
2014-02-17 11:59:28 +01:00
|
|
|
jobject socketAddr = (*env)->NewObject(env, inetSocketAddressClass, inetSocketAddrMethodId, ipString, port);
|
2014-02-15 22:26:36 +01:00
|
|
|
return socketAddr;
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
jbyteArray createInetSocketAddressArray(JNIEnv* env, struct sockaddr_storage addr) {
|
2014-10-22 15:25:30 +02:00
|
|
|
int port;
|
|
|
|
if (addr.ss_family == AF_INET) {
|
2014-12-30 04:20:43 +01:00
|
|
|
struct sockaddr_in* s = (struct sockaddr_in*) &addr;
|
2014-10-22 15:25:30 +02:00
|
|
|
port = ntohs(s->sin_port);
|
|
|
|
|
|
|
|
// Encode address and port into the array
|
|
|
|
unsigned char a[8];
|
|
|
|
a[0] = s->sin_addr.s_addr >> 24;
|
|
|
|
a[1] = s->sin_addr.s_addr >> 16;
|
|
|
|
a[2] = s->sin_addr.s_addr >> 8;
|
|
|
|
a[3] = s->sin_addr.s_addr;
|
|
|
|
a[4] = port >> 24;
|
|
|
|
a[5] = port >> 16;
|
|
|
|
a[6] = port >> 8;
|
|
|
|
a[7] = port;
|
|
|
|
|
|
|
|
jbyteArray bArray = (*env)->NewByteArray(env, 8);
|
|
|
|
(*env)->SetByteArrayRegion(env, bArray, 0, 8, (jbyte*) &a);
|
|
|
|
|
|
|
|
return bArray;
|
|
|
|
} else {
|
2014-12-30 04:20:43 +01:00
|
|
|
struct sockaddr_in6* s = (struct sockaddr_in6*) &addr;
|
2014-10-22 15:25:30 +02:00
|
|
|
port = ntohs(s->sin6_port);
|
|
|
|
|
|
|
|
if (s->sin6_addr.s6_addr[0] == 0x00 && s->sin6_addr.s6_addr[1] == 0x00 && s->sin6_addr.s6_addr[2] == 0x00 && s->sin6_addr.s6_addr[3] == 0x00 && s->sin6_addr.s6_addr[4] == 0x00
|
|
|
|
&& 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
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
jbyteArray bArray = (*env)->NewByteArray(env, 8);
|
|
|
|
// we only need the last 4 bytes for mapped address
|
|
|
|
(*env)->SetByteArrayRegion(env, bArray, 0, 4, (jbyte*) &(s->sin6_addr.s6_addr[12]));
|
|
|
|
(*env)->SetByteArrayRegion(env, bArray, 4, 4, (jbyte*) &a);
|
|
|
|
|
|
|
|
return bArray;
|
|
|
|
} 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;
|
|
|
|
|
|
|
|
jbyteArray bArray = (*env)->NewByteArray(env, 24);
|
|
|
|
(*env)->SetByteArrayRegion(env, bArray, 0, 16, (jbyte*) &(s->sin6_addr.s6_addr));
|
|
|
|
(*env)->SetByteArrayRegion(env, bArray, 16, 8, (jbyte*) &a);
|
|
|
|
return bArray;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
jobject createDatagramSocketAddress(JNIEnv* env, struct sockaddr_storage addr, int len) {
|
2014-04-11 21:08:26 +02:00
|
|
|
char ipstr[INET6_ADDRSTRLEN];
|
|
|
|
int port;
|
2014-09-07 13:38:00 +02:00
|
|
|
jstring ipString;
|
2014-04-11 21:08:26 +02:00
|
|
|
if (addr.ss_family == AF_INET) {
|
2014-12-30 04:20:43 +01:00
|
|
|
struct sockaddr_in* s = (struct sockaddr_in*) &addr;
|
2014-04-11 21:08:26 +02:00
|
|
|
port = ntohs(s->sin_port);
|
|
|
|
inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr);
|
2014-09-07 13:38:00 +02:00
|
|
|
ipString = (*env)->NewStringUTF(env, ipstr);
|
2014-04-11 21:08:26 +02:00
|
|
|
} else {
|
2014-12-30 04:20:43 +01:00
|
|
|
struct sockaddr_in6* s = (struct sockaddr_in6*) &addr;
|
2014-04-11 21:08:26 +02:00
|
|
|
port = ntohs(s->sin6_port);
|
|
|
|
inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof ipstr);
|
2014-09-07 13:38:00 +02:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2014-04-11 21:08:26 +02:00
|
|
|
}
|
|
|
|
jobject socketAddr = (*env)->NewObject(env, datagramSocketAddressClass, datagramSocketAddrMethodId, ipString, port, len);
|
|
|
|
return socketAddr;
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
int init_sockaddr(JNIEnv* env, jbyteArray address, jint scopeId, jint jport, struct sockaddr_storage* addr) {
|
2014-02-15 22:26:36 +01:00
|
|
|
uint16_t port = htons((uint16_t) jport);
|
2014-07-21 07:00:46 +02:00
|
|
|
// 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 ?!?!?
|
2014-12-30 04:48:37 +01:00
|
|
|
throwOutOfMemoryError(env);
|
2014-07-21 07:00:46 +02:00
|
|
|
}
|
2014-02-15 22:26:36 +01:00
|
|
|
if (socketType == AF_INET6) {
|
2014-12-30 04:20:43 +01:00
|
|
|
struct sockaddr_in6* ip6addr = (struct sockaddr_in6*) addr;
|
2014-02-15 22:26:36 +01:00
|
|
|
ip6addr->sin6_family = AF_INET6;
|
|
|
|
ip6addr->sin6_port = port;
|
|
|
|
|
|
|
|
if (scopeId != 0) {
|
|
|
|
ip6addr->sin6_scope_id = (uint32_t) scopeId;
|
|
|
|
}
|
2014-12-30 04:20:43 +01:00
|
|
|
memcpy(&(ip6addr->sin6_addr.s6_addr), addressBytes, 16);
|
2014-02-15 22:26:36 +01:00
|
|
|
} else {
|
2014-12-30 04:20:43 +01:00
|
|
|
struct sockaddr_in* ipaddr = (struct sockaddr_in*) addr;
|
2014-02-15 22:26:36 +01:00
|
|
|
ipaddr->sin_family = AF_INET;
|
|
|
|
ipaddr->sin_port = port;
|
2014-12-30 04:20:43 +01:00
|
|
|
memcpy(&(ipaddr->sin_addr.s_addr), addressBytes + 12, 4);
|
2014-02-15 22:26:36 +01:00
|
|
|
}
|
|
|
|
|
2014-07-21 07:00:46 +02:00
|
|
|
(*env)->ReleasePrimitiveArrayCritical(env, address, addressBytes, JNI_ABORT);
|
|
|
|
return 0;
|
2014-02-15 22:26:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int socket_type() {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2014-04-11 21:08:26 +02:00
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
int init_in_addr(JNIEnv* env, jbyteArray address, struct in_addr* addr) {
|
2014-07-21 07:00:46 +02:00
|
|
|
// 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 ?!?!?
|
2014-12-30 04:48:37 +01:00
|
|
|
throwOutOfMemoryError(env);
|
2014-07-21 07:00:46 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2014-04-11 21:08:26 +02:00
|
|
|
if (socketType == AF_INET6) {
|
|
|
|
memcpy(addr, addressBytes, 16);
|
|
|
|
} else {
|
|
|
|
memcpy(addr, addressBytes + 12, 4);
|
|
|
|
}
|
2014-07-21 07:00:46 +02:00
|
|
|
(*env)->ReleasePrimitiveArrayCritical(env, address, addressBytes, JNI_ABORT);
|
|
|
|
return 0;
|
2014-04-11 21:08:26 +02:00
|
|
|
}
|
2014-02-15 22:26:36 +01:00
|
|
|
// util methods end
|
|
|
|
|
|
|
|
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
|
|
|
JNIEnv* env;
|
2014-12-30 04:20:43 +01:00
|
|
|
if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) {
|
2014-02-15 22:26:36 +01:00
|
|
|
return JNI_ERR;
|
|
|
|
} else {
|
|
|
|
// 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!
|
2014-12-30 04:48:37 +01:00
|
|
|
throwOutOfMemoryError(env);
|
2014-02-15 22:26:36 +01:00
|
|
|
return JNI_ERR;
|
|
|
|
}
|
|
|
|
closedChannelExceptionMethodId = (*env)->GetMethodID(env, closedChannelExceptionClass, "<init>", "()V");
|
|
|
|
if (closedChannelExceptionMethodId == NULL) {
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, "failed to get method ID: ClosedChannelException.<init>()");
|
2014-02-15 22:26:36 +01:00
|
|
|
return JNI_ERR;
|
|
|
|
}
|
|
|
|
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!
|
2014-12-30 04:48:37 +01:00
|
|
|
throwOutOfMemoryError(env);
|
2014-02-15 22:26:36 +01:00
|
|
|
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!
|
2014-12-30 04:48:37 +01:00
|
|
|
throwOutOfMemoryError(env);
|
2014-02-15 22:26:36 +01:00
|
|
|
return JNI_ERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
jclass localInetSocketAddressClass = (*env)->FindClass(env, "java/net/InetSocketAddress");
|
|
|
|
if (localIoExceptionClass == NULL) {
|
|
|
|
// pending exception...
|
|
|
|
return JNI_ERR;
|
|
|
|
}
|
|
|
|
inetSocketAddressClass = (jclass) (*env)->NewGlobalRef(env, localInetSocketAddressClass);
|
|
|
|
if (inetSocketAddressClass == NULL) {
|
|
|
|
// out-of-memory!
|
2014-12-30 04:48:37 +01:00
|
|
|
throwOutOfMemoryError(env);
|
2014-02-15 22:26:36 +01:00
|
|
|
return JNI_ERR;
|
|
|
|
}
|
|
|
|
|
2014-04-11 21:08:26 +02:00
|
|
|
jclass localDatagramSocketAddressClass = (*env)->FindClass(env, "io/netty/channel/epoll/EpollDatagramChannel$DatagramSocketAddress");
|
|
|
|
if (localDatagramSocketAddressClass == NULL) {
|
|
|
|
// pending exception...
|
|
|
|
return JNI_ERR;
|
|
|
|
}
|
|
|
|
datagramSocketAddressClass = (jclass) (*env)->NewGlobalRef(env, localDatagramSocketAddressClass);
|
|
|
|
if (datagramSocketAddressClass == NULL) {
|
|
|
|
// out-of-memory!
|
2014-12-30 04:48:37 +01:00
|
|
|
throwOutOfMemoryError(env);
|
2014-04-11 21:08:26 +02:00
|
|
|
return JNI_ERR;
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
void* mem = malloc(1);
|
2014-02-15 22:26:36 +01:00
|
|
|
if (mem == NULL) {
|
2014-12-30 04:48:37 +01:00
|
|
|
throwOutOfMemoryError(env);
|
2014-02-15 22:26:36 +01:00
|
|
|
return JNI_ERR;
|
|
|
|
}
|
|
|
|
jobject directBuffer = (*env)->NewDirectByteBuffer(env, mem, 1);
|
|
|
|
if (directBuffer == NULL) {
|
2014-09-22 15:07:40 +02:00
|
|
|
free(mem);
|
|
|
|
|
2014-12-30 04:48:37 +01:00
|
|
|
throwOutOfMemoryError(env);
|
2014-02-15 22:26:36 +01:00
|
|
|
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) {
|
2014-09-22 15:07:40 +02:00
|
|
|
free(mem);
|
|
|
|
|
2014-02-15 22:26:36 +01:00
|
|
|
// position method was not found.. something is wrong so bail out
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, "failed to get method ID: ByteBuffer.position()");
|
2014-02-15 22:26:36 +01:00
|
|
|
return JNI_ERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
limitId = (*env)->GetMethodID(env, cls, "limit", "()I");
|
|
|
|
if (limitId == NULL) {
|
2014-09-22 15:07:40 +02:00
|
|
|
free(mem);
|
|
|
|
|
2014-02-15 22:26:36 +01:00
|
|
|
// limit method was not found.. something is wrong so bail out
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, "failed to get method ID: ByteBuffer.limit()");
|
2014-02-15 22:26:36 +01:00
|
|
|
return JNI_ERR;
|
|
|
|
}
|
2014-02-17 07:18:39 +01:00
|
|
|
updatePosId = (*env)->GetMethodID(env, cls, "position", "(I)Ljava/nio/Buffer;");
|
|
|
|
if (updatePosId == NULL) {
|
2014-09-22 15:07:40 +02:00
|
|
|
free(mem);
|
|
|
|
|
2014-02-17 07:18:39 +01:00
|
|
|
// position method was not found.. something is wrong so bail out
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, "failed to fet method ID: ByteBuffer.position(int)");
|
2014-02-17 07:18:39 +01:00
|
|
|
return JNI_ERR;
|
|
|
|
}
|
2014-02-15 22:26:36 +01:00
|
|
|
// 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);
|
|
|
|
}
|
2014-09-22 15:07:40 +02:00
|
|
|
|
|
|
|
free(mem);
|
|
|
|
|
2014-02-15 22:26:36 +01:00
|
|
|
jclass fileRegionCls = (*env)->FindClass(env, "io/netty/channel/DefaultFileRegion");
|
|
|
|
if (fileRegionCls == NULL) {
|
|
|
|
// pending exception...
|
|
|
|
return JNI_ERR;
|
|
|
|
}
|
|
|
|
fileChannelFieldId = (*env)->GetFieldID(env, fileRegionCls, "file", "Ljava/nio/channels/FileChannel;");
|
|
|
|
if (fileChannelFieldId == NULL) {
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, "failed to get field ID: DefaultFileRegion.file");
|
2014-02-15 22:26:36 +01:00
|
|
|
return JNI_ERR;
|
|
|
|
}
|
|
|
|
transferedFieldId = (*env)->GetFieldID(env, fileRegionCls, "transfered", "J");
|
|
|
|
if (transferedFieldId == NULL) {
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, "failed to get field ID: DefaultFileRegion.transfered");
|
2014-02-15 22:26:36 +01:00
|
|
|
return JNI_ERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
jclass fileChannelCls = (*env)->FindClass(env, "sun/nio/ch/FileChannelImpl");
|
|
|
|
if (fileChannelCls == NULL) {
|
|
|
|
// pending exception...
|
|
|
|
return JNI_ERR;
|
|
|
|
}
|
|
|
|
fileDescriptorFieldId = (*env)->GetFieldID(env, fileChannelCls, "fd", "Ljava/io/FileDescriptor;");
|
|
|
|
if (fileDescriptorFieldId == NULL) {
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, "failed to get field ID: FileChannelImpl.fd");
|
2014-02-15 22:26:36 +01:00
|
|
|
return JNI_ERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
jclass fileDescriptorCls = (*env)->FindClass(env, "java/io/FileDescriptor");
|
|
|
|
if (fileDescriptorCls == NULL) {
|
|
|
|
// pending exception...
|
|
|
|
return JNI_ERR;
|
|
|
|
}
|
|
|
|
fdFieldId = (*env)->GetFieldID(env, fileDescriptorCls, "fd", "I");
|
|
|
|
if (fdFieldId == NULL) {
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, "failed to get field ID: FileDescriptor.fd");
|
2014-02-15 22:26:36 +01:00
|
|
|
return JNI_ERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
inetSocketAddrMethodId = (*env)->GetMethodID(env, inetSocketAddressClass, "<init>", "(Ljava/lang/String;I)V");
|
|
|
|
if (inetSocketAddrMethodId == NULL) {
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, "failed to get method ID: InetSocketAddress.<init>(String, int)");
|
2014-02-15 22:26:36 +01:00
|
|
|
return JNI_ERR;
|
|
|
|
}
|
|
|
|
socketType = socket_type();
|
2014-02-18 10:08:20 +01:00
|
|
|
|
2014-04-11 21:08:26 +02:00
|
|
|
datagramSocketAddrMethodId = (*env)->GetMethodID(env, datagramSocketAddressClass, "<init>", "(Ljava/lang/String;II)V");
|
|
|
|
if (datagramSocketAddrMethodId == NULL) {
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, "failed to get method ID: DatagramSocketAddress.<init>(String, int, int)");
|
2014-04-11 21:08:26 +02:00
|
|
|
return JNI_ERR;
|
|
|
|
}
|
2014-09-03 14:54:44 +02:00
|
|
|
jclass nativeDatagramPacketCls = (*env)->FindClass(env, "io/netty/channel/epoll/NativeDatagramPacketArray$NativeDatagramPacket");
|
|
|
|
if (nativeDatagramPacketCls == NULL) {
|
|
|
|
// pending exception...
|
|
|
|
return JNI_ERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
packetAddrFieldId = (*env)->GetFieldID(env, nativeDatagramPacketCls, "addr", "[B");
|
|
|
|
if (packetAddrFieldId == NULL) {
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, "failed to get field ID: NativeDatagramPacket.addr");
|
2014-09-03 14:54:44 +02:00
|
|
|
return JNI_ERR;
|
|
|
|
}
|
|
|
|
packetScopeIdFieldId = (*env)->GetFieldID(env, nativeDatagramPacketCls, "scopeId", "I");
|
|
|
|
if (packetScopeIdFieldId == NULL) {
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, "failed to get field ID: NativeDatagramPacket.scopeId");
|
2014-09-03 14:54:44 +02:00
|
|
|
return JNI_ERR;
|
|
|
|
}
|
|
|
|
packetPortFieldId = (*env)->GetFieldID(env, nativeDatagramPacketCls, "port", "I");
|
|
|
|
if (packetPortFieldId == NULL) {
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, "failed to get field ID: NativeDatagramPacket.port");
|
2014-09-03 14:54:44 +02:00
|
|
|
return JNI_ERR;
|
|
|
|
}
|
|
|
|
packetMemoryAddressFieldId = (*env)->GetFieldID(env, nativeDatagramPacketCls, "memoryAddress", "J");
|
|
|
|
if (packetMemoryAddressFieldId == NULL) {
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, "failed to get field ID: NativeDatagramPacket.memoryAddress");
|
2014-09-03 14:54:44 +02:00
|
|
|
return JNI_ERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
packetCountFieldId = (*env)->GetFieldID(env, nativeDatagramPacketCls, "count", "I");
|
|
|
|
if (packetCountFieldId == NULL) {
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, "failed to get field ID: NativeDatagramPacket.count");
|
2014-09-03 14:54:44 +02:00
|
|
|
return JNI_ERR;
|
|
|
|
}
|
2014-02-15 22:26:36 +01:00
|
|
|
return JNI_VERSION_1_6;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
void JNI_OnUnload(JavaVM* vm, void* reserved) {
|
2014-02-15 22:26:36 +01:00
|
|
|
JNIEnv* env;
|
2014-12-30 04:20:43 +01:00
|
|
|
if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) {
|
2014-02-15 22:26:36 +01:00
|
|
|
// Something is wrong but nothing we can do about this :(
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
// delete global references so the GC can collect them
|
|
|
|
if (runtimeExceptionClass != NULL) {
|
|
|
|
(*env)->DeleteGlobalRef(env, runtimeExceptionClass);
|
|
|
|
}
|
|
|
|
if (ioExceptionClass != NULL) {
|
|
|
|
(*env)->DeleteGlobalRef(env, ioExceptionClass);
|
|
|
|
}
|
|
|
|
if (closedChannelExceptionClass != NULL) {
|
|
|
|
(*env)->DeleteGlobalRef(env, closedChannelExceptionClass);
|
|
|
|
}
|
|
|
|
if (inetSocketAddressClass != NULL) {
|
|
|
|
(*env)->DeleteGlobalRef(env, inetSocketAddressClass);
|
|
|
|
}
|
2014-04-11 21:08:26 +02:00
|
|
|
if (datagramSocketAddressClass != NULL) {
|
|
|
|
(*env)->DeleteGlobalRef(env, datagramSocketAddressClass);
|
|
|
|
}
|
2014-02-15 22:26:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_eventFd(JNIEnv* env, jclass clazz) {
|
2014-02-15 22:26:36 +01:00
|
|
|
jint eventFD = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
|
|
|
|
|
|
|
|
if (eventFD < 0) {
|
|
|
|
int err = errno;
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, exceptionMessage("eventfd() failed: ", err));
|
2014-02-15 22:26:36 +01:00
|
|
|
}
|
|
|
|
return eventFD;
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_eventFdWrite(JNIEnv* env, jclass clazz, jint fd, jlong value) {
|
|
|
|
jint eventFD = eventfd_write(fd, (eventfd_t) value);
|
2014-02-15 22:26:36 +01:00
|
|
|
|
|
|
|
if (eventFD < 0) {
|
|
|
|
int err = errno;
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, exceptionMessage("eventfd_write() failed: ", err));
|
2014-02-15 22:26:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_eventFdRead(JNIEnv* env, jclass clazz, jint fd) {
|
2014-02-15 22:26:36 +01:00
|
|
|
uint64_t eventfd_t;
|
|
|
|
|
|
|
|
if (eventfd_read(fd, &eventfd_t) != 0) {
|
|
|
|
// something is serious wrong
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, "eventfd_read() failed");
|
2014-02-15 22:26:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_epollCreate(JNIEnv* env, jclass clazz) {
|
2014-04-04 07:45:23 +02:00
|
|
|
jint efd;
|
|
|
|
if (epoll_create1) {
|
|
|
|
efd = epoll_create1(EPOLL_CLOEXEC);
|
|
|
|
} else {
|
|
|
|
// size will be ignored anyway but must be positive
|
|
|
|
efd = epoll_create(126);
|
|
|
|
}
|
2014-02-15 22:26:36 +01:00
|
|
|
if (efd < 0) {
|
|
|
|
int err = errno;
|
2014-07-28 22:31:26 +02:00
|
|
|
if (epoll_create1) {
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, exceptionMessage("epoll_create1() failed: ", err));
|
2014-07-28 22:31:26 +02:00
|
|
|
} else {
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, exceptionMessage("epoll_create() failed: ", err));
|
2014-07-28 22:31:26 +02:00
|
|
|
}
|
|
|
|
return efd;
|
2014-02-15 22:26:36 +01:00
|
|
|
}
|
2014-04-04 07:45:23 +02:00
|
|
|
if (!epoll_create1) {
|
|
|
|
if (fcntl(efd, F_SETFD, FD_CLOEXEC) < 0) {
|
|
|
|
int err = errno;
|
|
|
|
close(efd);
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, exceptionMessage("fcntl() failed: ", err));
|
2014-04-04 07:45:23 +02:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
}
|
2014-02-15 22:26:36 +01:00
|
|
|
return efd;
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_epollWait(JNIEnv* env, jclass clazz, jint efd, jlongArray events, jint timeout) {
|
2014-02-15 22:26:36 +01:00
|
|
|
int len = (*env)->GetArrayLength(env, events);
|
|
|
|
struct epoll_event ev[len];
|
|
|
|
int ready;
|
|
|
|
int err;
|
|
|
|
do {
|
|
|
|
ready = epoll_wait(efd, ev, len, timeout);
|
|
|
|
// was interrupted try again.
|
2014-12-30 04:20:43 +01:00
|
|
|
} while (ready == -1 && ((err = errno) == EINTR));
|
2014-02-15 22:26:36 +01:00
|
|
|
|
|
|
|
if (ready < 0) {
|
2014-12-30 04:48:37 +01:00
|
|
|
throwIOException(env, exceptionMessage("epoll_wait() failed: ", err));
|
2014-02-15 22:26:36 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (ready == 0) {
|
|
|
|
// nothing ready for process
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
jboolean isCopy;
|
2014-07-21 07:00:46 +02:00
|
|
|
// 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!
|
2014-12-30 04:20:43 +01:00
|
|
|
jlong* elements = (*env)->GetPrimitiveArrayCritical(env, events, &isCopy);
|
2014-02-15 22:26:36 +01:00
|
|
|
if (elements == NULL) {
|
|
|
|
// No memory left ?!?!?
|
2014-12-30 04:48:37 +01:00
|
|
|
throwOutOfMemoryError(env);
|
2014-02-15 22:26:36 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < ready; i++) {
|
|
|
|
// store the ready ops and id
|
|
|
|
elements[i] = (jlong) ev[i].data.u64;
|
|
|
|
if (ev[i].events & EPOLLIN) {
|
|
|
|
elements[i] |= EPOLL_READ;
|
|
|
|
}
|
|
|
|
if (ev[i].events & EPOLLRDHUP) {
|
|
|
|
elements[i] |= EPOLL_RDHUP;
|
|
|
|
}
|
|
|
|
if (ev[i].events & EPOLLOUT) {
|
|
|
|
elements[i] |= EPOLL_WRITE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
jint mode;
|
|
|
|
// release again to prevent memory leak
|
|
|
|
if (isCopy) {
|
|
|
|
mode = 0;
|
|
|
|
} else {
|
|
|
|
// was just pinned so use JNI_ABORT to eliminate not needed copy.
|
|
|
|
mode = JNI_ABORT;
|
|
|
|
}
|
2014-07-21 07:00:46 +02:00
|
|
|
(*env)->ReleasePrimitiveArrayCritical(env, events, elements, mode);
|
2014-02-15 22:26:36 +01:00
|
|
|
|
|
|
|
return ready;
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_epollCtlAdd(JNIEnv* env, jclass clazz, jint efd, jint fd, jint flags, jint id) {
|
2014-02-15 22:26:36 +01:00
|
|
|
if (epollCtl(env, efd, EPOLL_CTL_ADD, fd, flags, id) < 0) {
|
|
|
|
int err = errno;
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, exceptionMessage("epoll_ctl() failed: ", err));
|
2014-02-15 22:26:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_epollCtlMod(JNIEnv* env, jclass clazz, jint efd, jint fd, jint flags, jint id) {
|
2014-02-15 22:26:36 +01:00
|
|
|
if (epollCtl(env, efd, EPOLL_CTL_MOD, fd, flags, id) < 0) {
|
|
|
|
int err = errno;
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, exceptionMessage("epoll_ctl() failed: ", err));
|
2014-02-15 22:26:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_epollCtlDel(JNIEnv* env, jclass clazz, jint efd, jint fd) {
|
2014-02-15 22:26:36 +01:00
|
|
|
// Create an empty event to workaround a bug in older kernels which can not handle NULL.
|
|
|
|
struct epoll_event event = { 0 };
|
|
|
|
if (epoll_ctl(efd, EPOLL_CTL_DEL, fd, &event) < 0) {
|
|
|
|
int err = errno;
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, exceptionMessage("epoll_ctl() failed: ", err));
|
2014-02-15 22:26:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
jint _write(JNIEnv* env, jclass clazz, jint fd, void* buffer, jint pos, jint limit) {
|
2014-02-15 22:26:36 +01:00
|
|
|
ssize_t res;
|
|
|
|
int err;
|
|
|
|
do {
|
|
|
|
res = write(fd, buffer + pos, (size_t) (limit - pos));
|
|
|
|
// keep on writing if it was interrupted
|
2014-12-30 04:20:43 +01:00
|
|
|
} while (res == -1 && ((err = errno) == EINTR));
|
2014-02-15 22:26:36 +01:00
|
|
|
|
|
|
|
if (res < 0) {
|
2014-12-30 04:20:43 +01:00
|
|
|
return -err;
|
2014-02-15 22:26:36 +01:00
|
|
|
}
|
|
|
|
return (jint) res;
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_write0(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit) {
|
|
|
|
void* buffer = (*env)->GetDirectBufferAddress(env, jbuffer);
|
2014-02-17 15:19:01 +01:00
|
|
|
if (buffer == NULL) {
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, "failed to get direct buffer address");
|
2014-02-17 15:19:01 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2014-12-30 04:20:43 +01:00
|
|
|
return _write(env, clazz, fd, buffer, pos, limit);
|
2014-02-17 15:19:01 +01:00
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_writeAddress0(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit) {
|
|
|
|
return _write(env, clazz, fd, (void*) address, pos, limit);
|
2014-02-17 15:19:01 +01:00
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
jint _sendTo(JNIEnv* env, jint fd, void* buffer, jint pos, jint limit ,jbyteArray address, jint scopeId, jint port) {
|
2014-04-11 21:08:26 +02:00
|
|
|
struct sockaddr_storage addr;
|
2014-07-21 07:00:46 +02:00
|
|
|
if (init_sockaddr(env, address, scopeId, port, &addr) == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
2014-04-11 21:08:26 +02:00
|
|
|
|
|
|
|
ssize_t res;
|
|
|
|
int err;
|
|
|
|
do {
|
2014-12-30 04:20:43 +01:00
|
|
|
res = sendto(fd, buffer + pos, (size_t) (limit - pos), 0, (struct sockaddr*) &addr, sizeof(struct sockaddr_storage));
|
2014-04-11 21:08:26 +02:00
|
|
|
// keep on writing if it was interrupted
|
2014-12-30 04:20:43 +01:00
|
|
|
} while (res == -1 && ((err = errno) == EINTR));
|
2014-04-11 21:08:26 +02:00
|
|
|
|
|
|
|
if (res < 0) {
|
2014-12-30 04:20:43 +01:00
|
|
|
return -err;
|
2014-04-11 21:08:26 +02:00
|
|
|
}
|
|
|
|
return (jint) res;
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_sendTo0(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit, jbyteArray address, jint scopeId, jint port) {
|
|
|
|
void* buffer = (*env)->GetDirectBufferAddress(env, jbuffer);
|
2014-04-11 21:08:26 +02:00
|
|
|
if (buffer == NULL) {
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, "failed to get direct buffer address");
|
2014-04-11 21:08:26 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2014-12-30 04:20:43 +01:00
|
|
|
return _sendTo(env, fd, buffer, pos, limit, address, scopeId, port);
|
2014-04-11 21:08:26 +02:00
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_sendToAddress0(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint pos, jint limit ,jbyteArray address, jint scopeId, jint port) {
|
|
|
|
return _sendTo(env, fd, (void*) memoryAddress, pos, limit, address, scopeId, port);
|
2014-04-11 21:08:26 +02:00
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_sendToAddresses(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint length, jbyteArray address, jint scopeId, jint port) {
|
2014-08-25 08:47:00 +02:00
|
|
|
struct sockaddr_storage addr;
|
|
|
|
|
|
|
|
if (init_sockaddr(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);
|
2014-12-30 04:20:43 +01:00
|
|
|
m.msg_iov = (struct iovec*) memoryAddress;
|
2014-08-25 08:47:00 +02:00
|
|
|
m.msg_iovlen = length;
|
|
|
|
|
|
|
|
ssize_t res;
|
|
|
|
int err;
|
|
|
|
do {
|
|
|
|
res = sendmsg(fd, &m, 0);
|
|
|
|
// keep on writing if it was interrupted
|
2014-12-30 04:20:43 +01:00
|
|
|
} while (res == -1 && ((err = errno) == EINTR));
|
2014-08-25 08:47:00 +02:00
|
|
|
|
|
|
|
if (res < 0) {
|
2014-12-30 04:20:43 +01:00
|
|
|
return -err;
|
2014-08-25 08:47:00 +02:00
|
|
|
}
|
|
|
|
return (jint) res;
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_sendmmsg0(JNIEnv* env, jclass clazz, jint fd, jobjectArray packets, jint offset, jint len) {
|
2014-09-03 14:54:44 +02:00
|
|
|
struct mmsghdr msg[len];
|
|
|
|
int i;
|
|
|
|
|
|
|
|
memset(msg, 0, sizeof(msg));
|
|
|
|
|
|
|
|
for (i = 0; i < len; i++) {
|
|
|
|
struct sockaddr_storage addr;
|
|
|
|
|
|
|
|
jobject packet = (*env)->GetObjectArrayElement(env, packets, i + offset);
|
|
|
|
jbyteArray address = (jbyteArray) (*env)->GetObjectField(env, packet, packetAddrFieldId);
|
|
|
|
jint scopeId = (*env)->GetIntField(env, packet, packetScopeIdFieldId);
|
|
|
|
jint port = (*env)->GetIntField(env, packet, packetPortFieldId);
|
|
|
|
|
|
|
|
if (init_sockaddr(env, address, scopeId, port, &addr) == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
msg[i].msg_hdr.msg_name = &addr;
|
|
|
|
msg[i].msg_hdr.msg_namelen = sizeof(addr);
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
msg[i].msg_hdr.msg_iov = (struct iovec*) (*env)->GetLongField(env, packet, packetMemoryAddressFieldId);
|
2014-09-03 14:54:44 +02:00
|
|
|
msg[i].msg_hdr.msg_iovlen = (*env)->GetIntField(env, packet, packetCountFieldId);;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t res;
|
|
|
|
int err;
|
|
|
|
do {
|
|
|
|
res = sendmmsg(fd, msg, len, 0);
|
|
|
|
// keep on writing if it was interrupted
|
2014-12-30 04:20:43 +01:00
|
|
|
} while (res == -1 && ((err = errno) == EINTR));
|
2014-09-03 14:54:44 +02:00
|
|
|
|
|
|
|
if (res < 0) {
|
2014-12-30 04:20:43 +01:00
|
|
|
return -err;
|
2014-09-03 14:54:44 +02:00
|
|
|
}
|
|
|
|
return (jint) res;
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
jobject recvFrom0(JNIEnv* env, jint fd, void* buffer, jint pos, jint limit) {
|
2014-04-11 21:08:26 +02:00
|
|
|
struct sockaddr_storage addr;
|
|
|
|
socklen_t addrlen = sizeof(addr);
|
|
|
|
ssize_t res;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
do {
|
2014-12-30 04:20:43 +01:00
|
|
|
res = recvfrom(fd, buffer + pos, (size_t) (limit - pos), 0, (struct sockaddr*) &addr, &addrlen);
|
2014-04-11 21:08:26 +02:00
|
|
|
// 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) {
|
|
|
|
throwClosedChannelException(env);
|
|
|
|
return NULL;
|
|
|
|
}
|
2014-12-30 04:48:37 +01:00
|
|
|
throwIOException(env, exceptionMessage("recvfrom() failed: ", err));
|
2014-04-11 21:08:26 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return createDatagramSocketAddress(env, addr, res);
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jobject JNICALL Java_io_netty_channel_epoll_Native_recvFrom(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit) {
|
|
|
|
void* buffer = (*env)->GetDirectBufferAddress(env, jbuffer);
|
2014-04-11 21:08:26 +02:00
|
|
|
if (buffer == NULL) {
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, "failed to get direct buffer address");
|
2014-04-11 21:08:26 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return recvFrom0(env, fd, buffer, pos, limit);
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jobject JNICALL Java_io_netty_channel_epoll_Native_recvFromAddress(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit) {
|
2014-04-11 21:08:26 +02:00
|
|
|
return recvFrom0(env, fd, (void*) address, pos, limit);
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
jlong _writev(JNIEnv* env, jclass clazz, jint fd, struct iovec* iov, jint length) {
|
2014-02-18 10:08:20 +01:00
|
|
|
ssize_t res;
|
|
|
|
int err;
|
|
|
|
do {
|
|
|
|
res = writev(fd, iov, length);
|
|
|
|
// keep on writing if it was interrupted
|
2014-12-30 04:20:43 +01:00
|
|
|
} while (res == -1 && ((err = errno) == EINTR));
|
2014-02-18 10:08:20 +01:00
|
|
|
|
|
|
|
if (res < 0) {
|
2014-12-30 04:20:43 +01:00
|
|
|
return -err;
|
2014-02-18 10:08:20 +01:00
|
|
|
}
|
|
|
|
return (jlong) res;
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jlong JNICALL Java_io_netty_channel_epoll_Native_writev0(JNIEnv* env, jclass clazz, jint fd, jobjectArray buffers, jint offset, jint length) {
|
2014-07-17 16:00:53 +02:00
|
|
|
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);
|
2014-07-09 12:18:48 +02:00
|
|
|
}
|
2014-07-17 16:00:53 +02:00
|
|
|
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);
|
2014-07-09 12:18:48 +02:00
|
|
|
}
|
2014-12-30 04:20:43 +01:00
|
|
|
void* buffer = (*env)->GetDirectBufferAddress(env, bufObj);
|
2014-07-17 16:00:53 +02:00
|
|
|
if (buffer == NULL) {
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, "failed to get direct buffer address");
|
2014-07-17 16:00:53 +02:00
|
|
|
return -1;
|
2014-07-09 12:18:48 +02:00
|
|
|
}
|
2014-07-17 16:00:53 +02:00
|
|
|
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);
|
2014-07-09 12:18:48 +02:00
|
|
|
}
|
2014-12-30 04:20:43 +01:00
|
|
|
return _writev(env, clazz, fd, iov, length);
|
2014-02-15 22:26:36 +01:00
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jlong JNICALL Java_io_netty_channel_epoll_Native_writevAddresses0(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint length) {
|
|
|
|
struct iovec* iov = (struct iovec*) memoryAddress;
|
|
|
|
return _writev(env, clazz, fd, iov, length);
|
2014-02-18 10:08:20 +01:00
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
jint _read(JNIEnv* env, jclass clazz, jint fd, void* buffer, jint pos, jint limit) {
|
2014-02-15 22:26:36 +01:00
|
|
|
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) {
|
2014-12-30 04:20:43 +01:00
|
|
|
return -err;
|
2014-02-15 22:26:36 +01:00
|
|
|
}
|
|
|
|
return (jint) res;
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_read0(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit) {
|
|
|
|
void* buffer = (*env)->GetDirectBufferAddress(env, jbuffer);
|
2014-02-17 15:19:01 +01:00
|
|
|
if (buffer == NULL) {
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, "failed to get direct buffer address");
|
2014-02-17 15:19:01 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2014-12-30 04:20:43 +01:00
|
|
|
return _read(env, clazz, fd, buffer, pos, limit);
|
2014-02-17 15:19:01 +01:00
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_readAddress0(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit) {
|
|
|
|
return _read(env, clazz, fd, (void*) address, pos, limit);
|
2014-02-17 15:19:01 +01:00
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_close0(JNIEnv* env, jclass clazz, jint fd) {
|
2014-02-15 22:26:36 +01:00
|
|
|
if (close(fd) < 0) {
|
2014-12-30 04:20:43 +01:00
|
|
|
return -errno;
|
2014-02-15 22:26:36 +01:00
|
|
|
}
|
2014-12-30 04:20:43 +01:00
|
|
|
return 0;
|
2014-02-15 22:26:36 +01:00
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_shutdown0(JNIEnv* env, jclass clazz, jint fd, jboolean read, jboolean write) {
|
2014-02-15 22:26:36 +01:00
|
|
|
int mode;
|
|
|
|
if (read && write) {
|
|
|
|
mode = SHUT_RDWR;
|
|
|
|
} else if (read) {
|
|
|
|
mode = SHUT_RD;
|
|
|
|
} else if (write) {
|
|
|
|
mode = SHUT_WR;
|
|
|
|
}
|
|
|
|
if (shutdown(fd, mode) < 0) {
|
2014-12-30 04:20:43 +01:00
|
|
|
return -errno;
|
2014-02-15 22:26:36 +01:00
|
|
|
}
|
2014-12-30 04:20:43 +01:00
|
|
|
return 0;
|
2014-02-15 22:26:36 +01:00
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
jint socket0(JNIEnv* env, jclass clazz, int type) {
|
2014-02-15 22:26:36 +01:00
|
|
|
// TODO: Maybe also respect -Djava.net.preferIPv4Stack=true
|
2014-04-11 21:08:26 +02:00
|
|
|
int fd = socket(socketType, type | SOCK_NONBLOCK, 0);
|
2014-02-15 22:26:36 +01:00
|
|
|
if (fd == -1) {
|
2014-12-30 04:20:43 +01:00
|
|
|
return -errno;
|
|
|
|
} else if (socketType == AF_INET6) {
|
2014-02-15 22:26:36 +01:00
|
|
|
// Allow to listen /connect ipv4 and ipv6
|
|
|
|
int optval = 0;
|
|
|
|
if (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;
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_socketDgram(JNIEnv* env, jclass clazz) {
|
2014-04-11 21:08:26 +02:00
|
|
|
return socket0(env, clazz, SOCK_DGRAM);
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_socketStream(JNIEnv* env, jclass clazz) {
|
2014-04-11 21:08:26 +02:00
|
|
|
return socket0(env, clazz, SOCK_STREAM);
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_bind(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port) {
|
2014-02-15 22:26:36 +01:00
|
|
|
struct sockaddr_storage addr;
|
2014-07-21 07:00:46 +02:00
|
|
|
if (init_sockaddr(env, address, scopeId, port, &addr) == -1) {
|
2014-12-30 04:20:43 +01:00
|
|
|
return -1;
|
2014-07-21 07:00:46 +02:00
|
|
|
}
|
2014-02-15 22:26:36 +01:00
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
if (bind(fd, (struct sockaddr*) &addr, sizeof(addr)) == -1) {
|
|
|
|
return -errno;
|
2014-02-15 22:26:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_listen0(JNIEnv* env, jclass clazz, jint fd, jint backlog) {
|
|
|
|
if (listen(fd, backlog) == -1) {
|
|
|
|
return -errno;
|
2014-02-15 22:26:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_connect(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port) {
|
2014-02-15 22:26:36 +01:00
|
|
|
struct sockaddr_storage addr;
|
2014-07-21 07:00:46 +02:00
|
|
|
if (init_sockaddr(env, address, scopeId, port, &addr) == -1) {
|
2014-12-30 04:20:43 +01:00
|
|
|
// A runtime exception was thrown
|
2014-07-21 07:00:46 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2014-02-15 22:26:36 +01:00
|
|
|
|
|
|
|
int res;
|
|
|
|
int err;
|
|
|
|
do {
|
2014-12-30 04:20:43 +01:00
|
|
|
res = connect(fd, (struct sockaddr*) &addr, sizeof(addr));
|
2014-02-15 22:26:36 +01:00
|
|
|
} while (res == -1 && ((err = errno) == EINTR));
|
|
|
|
|
|
|
|
if (res < 0) {
|
2014-12-30 04:20:43 +01:00
|
|
|
return -err;
|
2014-02-15 22:26:36 +01:00
|
|
|
}
|
2014-12-30 04:20:43 +01:00
|
|
|
return 0;
|
2014-02-15 22:26:36 +01:00
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_finishConnect0(JNIEnv* env, jclass clazz, jint fd) {
|
2014-03-09 13:41:14 +01:00
|
|
|
// connect may be done
|
|
|
|
// return true if connection finished successfully
|
|
|
|
// return false if connection is still in progress
|
|
|
|
// throw exception if connection failed
|
2014-02-15 22:26:36 +01:00
|
|
|
int optval;
|
2014-03-09 13:41:14 +01:00
|
|
|
int res = getOption(env, fd, SOL_SOCKET, SO_ERROR, &optval, sizeof(optval));
|
|
|
|
if (res != 0) {
|
|
|
|
// getOption failed
|
2014-12-30 04:20:43 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (optval == 0) {
|
2014-03-09 13:41:14 +01:00
|
|
|
// connect succeeded
|
2014-12-30 04:20:43 +01:00
|
|
|
return 0;
|
2014-02-15 22:26:36 +01:00
|
|
|
}
|
2014-12-30 04:20:43 +01:00
|
|
|
return -optval;
|
2014-02-15 22:26:36 +01:00
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_accept0(JNIEnv* env, jclass clazz, jint fd) {
|
2014-02-15 22:26:36 +01:00
|
|
|
jint socketFd;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (accept4) {
|
|
|
|
socketFd = accept4(fd, NULL, 0, SOCK_NONBLOCK | SOCK_CLOEXEC);
|
|
|
|
} else {
|
|
|
|
socketFd = accept(fd, NULL, 0);
|
|
|
|
}
|
|
|
|
} while (socketFd == -1 && ((err = errno) == EINTR));
|
|
|
|
|
|
|
|
if (socketFd == -1) {
|
2014-12-30 04:20:43 +01:00
|
|
|
return -err;
|
2014-02-15 22:26:36 +01:00
|
|
|
}
|
|
|
|
if (accept4) {
|
|
|
|
return socketFd;
|
|
|
|
} else {
|
|
|
|
// accept4 was not present so need two more sys-calls ...
|
|
|
|
if (fcntl(socketFd, F_SETFD, FD_CLOEXEC) == -1) {
|
2014-12-30 04:20:43 +01:00
|
|
|
return -errno;
|
2014-02-15 22:26:36 +01:00
|
|
|
}
|
|
|
|
if (fcntl(socketFd, F_SETFL, O_NONBLOCK) == -1) {
|
2014-12-30 04:20:43 +01:00
|
|
|
return -errno;
|
2014-02-15 22:26:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return socketFd;
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jlong JNICALL Java_io_netty_channel_epoll_Native_sendfile0(JNIEnv* env, jclass clazz, jint fd, jobject fileRegion, jlong base_off, jlong off, jlong len) {
|
2014-02-15 22:26:36 +01:00
|
|
|
jobject fileChannel = (*env)->GetObjectField(env, fileRegion, fileChannelFieldId);
|
|
|
|
if (fileChannel == NULL) {
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, "failed to get DefaultFileRegion.file");
|
2014-02-15 22:26:36 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
jobject fileDescriptor = (*env)->GetObjectField(env, fileChannel, fileDescriptorFieldId);
|
|
|
|
if (fileDescriptor == NULL) {
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, "failed to get FileChannelImpl.fd");
|
2014-02-15 22:26:36 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
jint srcFd = (*env)->GetIntField(env, fileDescriptor, fdFieldId);
|
|
|
|
if (srcFd == -1) {
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, "failed to get FileDescriptor.fd");
|
2014-02-15 22:26:36 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
ssize_t res;
|
2014-08-14 01:50:18 +02:00
|
|
|
off_t offset = base_off + off;
|
2014-02-15 22:26:36 +01:00
|
|
|
int err;
|
|
|
|
do {
|
|
|
|
res = sendfile(fd, srcFd, &offset, (size_t) len);
|
|
|
|
} while (res == -1 && ((err = errno) == EINTR));
|
|
|
|
if (res < 0) {
|
2014-12-30 04:20:43 +01:00
|
|
|
return -err;
|
2014-02-15 22:26:36 +01:00
|
|
|
}
|
|
|
|
if (res > 0) {
|
|
|
|
// update the transfered field in DefaultFileRegion
|
|
|
|
(*env)->SetLongField(env, fileRegion, transferedFieldId, off + res);
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jbyteArray JNICALL Java_io_netty_channel_epoll_Native_remoteAddress0(JNIEnv* env, jclass clazz, jint fd) {
|
2014-02-15 22:26:36 +01:00
|
|
|
socklen_t len;
|
|
|
|
struct sockaddr_storage addr;
|
|
|
|
|
|
|
|
len = sizeof addr;
|
2014-12-30 04:20:43 +01:00
|
|
|
if (getpeername(fd, (struct sockaddr*) &addr, &len) == -1) {
|
2014-02-15 22:26:36 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
2014-10-22 15:25:30 +02:00
|
|
|
return createInetSocketAddressArray(env, addr);
|
2014-02-15 22:26:36 +01:00
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jbyteArray JNICALL Java_io_netty_channel_epoll_Native_localAddress0(JNIEnv* env, jclass clazz, jint fd) {
|
2014-02-15 22:26:36 +01:00
|
|
|
socklen_t len;
|
|
|
|
struct sockaddr_storage addr;
|
|
|
|
|
|
|
|
len = sizeof addr;
|
2014-12-30 04:20:43 +01:00
|
|
|
if (getsockname(fd, (struct sockaddr*) &addr, &len) == -1) {
|
2014-02-15 22:26:36 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
2014-10-22 15:25:30 +02:00
|
|
|
return createInetSocketAddressArray(env, addr);
|
2014-02-15 22:26:36 +01:00
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setReuseAddress(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
2014-02-15 22:26:36 +01:00
|
|
|
setOption(env, fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setReusePort(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
2014-04-11 15:54:31 +02:00
|
|
|
setOption(env, fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setTcpNoDelay(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
2014-02-15 22:26:36 +01:00
|
|
|
setOption(env, fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval));
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setReceiveBufferSize(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
2014-02-15 22:26:36 +01:00
|
|
|
setOption(env, fd, SOL_SOCKET, SO_RCVBUF, &optval, sizeof(optval));
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setSendBufferSize(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
2014-02-15 22:26:36 +01:00
|
|
|
setOption(env, fd, SOL_SOCKET, SO_SNDBUF, &optval, sizeof(optval));
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setKeepAlive(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
2014-02-15 22:26:36 +01:00
|
|
|
setOption(env, fd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setTcpCork(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
2014-02-15 22:26:36 +01:00
|
|
|
setOption(env, fd, SOL_TCP, TCP_CORK, &optval, sizeof(optval));
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setSoLinger(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
2014-02-15 22:26:36 +01:00
|
|
|
setOption(env, fd, IPPROTO_IP, IP_TOS, &optval, sizeof(optval));
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setTrafficClass(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
2014-02-15 22:26:36 +01:00
|
|
|
struct linger solinger;
|
|
|
|
if (optval < 0) {
|
|
|
|
solinger.l_onoff = 0;
|
|
|
|
solinger.l_linger = 0;
|
|
|
|
} else {
|
|
|
|
solinger.l_onoff = 1;
|
|
|
|
solinger.l_linger = optval;
|
|
|
|
}
|
|
|
|
setOption(env, fd, SOL_SOCKET, SO_LINGER, &solinger, sizeof(solinger));
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setBroadcast(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
2014-04-11 21:08:26 +02:00
|
|
|
setOption(env, fd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval));
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setTcpKeepIdle(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
2014-04-17 21:41:04 +02:00
|
|
|
setOption(env, fd, SOL_TCP, TCP_KEEPIDLE, &optval, sizeof(optval));
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setTcpKeepIntvl(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
2014-04-17 21:41:04 +02:00
|
|
|
setOption(env, fd, SOL_TCP, TCP_KEEPINTVL, &optval, sizeof(optval));
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT void Java_io_netty_channel_epoll_Native_setTcpKeepCnt(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
2014-04-17 21:41:04 +02:00
|
|
|
setOption(env, fd, SOL_TCP, TCP_KEEPCNT, &optval, sizeof(optval));
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_isReuseAddresss(JNIEnv* env, jclass clazz, jint fd) {
|
2014-02-15 22:26:36 +01:00
|
|
|
int optval;
|
|
|
|
if (getOption(env, fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return optval;
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_isReusePort(JNIEnv* env, jclass clazz, jint fd) {
|
2014-04-11 15:54:31 +02:00
|
|
|
int optval;
|
|
|
|
if (getOption(env, fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)) == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return optval;
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_isTcpNoDelay(JNIEnv* env, jclass clazz, jint fd) {
|
2014-02-15 22:26:36 +01:00
|
|
|
int optval;
|
|
|
|
if (getOption(env, fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval)) == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return optval;
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getReceiveBufferSize(JNIEnv* env, jclass clazz, jint fd) {
|
2014-02-15 22:26:36 +01:00
|
|
|
int optval;
|
|
|
|
if (getOption(env, fd, SOL_SOCKET, SO_RCVBUF, &optval, sizeof(optval)) == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return optval;
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getSendBufferSize(JNIEnv* env, jclass clazz, jint fd) {
|
2014-02-15 22:26:36 +01:00
|
|
|
int optval;
|
|
|
|
if (getOption(env, fd, SOL_SOCKET, SO_SNDBUF, &optval, sizeof(optval)) == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return optval;
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_isTcpCork(JNIEnv* env, jclass clazz, jint fd) {
|
2014-02-15 22:26:36 +01:00
|
|
|
int optval;
|
|
|
|
if (getOption(env, fd, SOL_TCP, TCP_CORK, &optval, sizeof(optval)) == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return optval;
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getSoLinger(JNIEnv* env, jclass clazz, jint fd) {
|
2014-02-15 22:26:36 +01:00
|
|
|
struct linger optval;
|
|
|
|
if (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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getTrafficClass(JNIEnv* env, jclass clazz, jint fd) {
|
2014-02-15 22:26:36 +01:00
|
|
|
int optval;
|
|
|
|
if (getOption(env, fd, IPPROTO_IP, IP_TOS, &optval, sizeof(optval)) == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return optval;
|
|
|
|
}
|
2014-04-11 15:54:31 +02:00
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_isBroadcast(JNIEnv* env, jclass clazz, jint fd) {
|
2014-04-11 21:08:26 +02:00
|
|
|
int optval;
|
|
|
|
if (getOption(env, fd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return optval;
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getTcpKeepIdle(JNIEnv* env, jclass clazz, jint fd) {
|
2014-04-17 21:41:04 +02:00
|
|
|
int optval;
|
|
|
|
if (getOption(env, fd, SOL_TCP, TCP_KEEPIDLE, &optval, sizeof(optval)) == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return optval;
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getTcpKeepIntvl(JNIEnv* env, jclass clazz, jint fd) {
|
2014-04-17 21:41:04 +02:00
|
|
|
int optval;
|
|
|
|
if (getOption(env, fd, SOL_TCP, TCP_KEEPINTVL, &optval, sizeof(optval)) == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return optval;
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getTcpKeepCnt(JNIEnv* env, jclass clazz, jint fd) {
|
2014-04-17 21:41:04 +02:00
|
|
|
int optval;
|
|
|
|
if (getOption(env, fd, SOL_TCP, TCP_KEEPCNT, &optval, sizeof(optval)) == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return optval;
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jstring JNICALL Java_io_netty_channel_epoll_Native_kernelVersion(JNIEnv* env, jclass clazz) {
|
2014-04-11 15:54:31 +02:00
|
|
|
struct utsname name;
|
|
|
|
|
|
|
|
int res = uname(&name);
|
|
|
|
if (res == 0) {
|
|
|
|
return (*env)->NewStringUTF(env, name.release);
|
|
|
|
}
|
|
|
|
int err = errno;
|
2014-12-30 04:48:37 +01:00
|
|
|
throwRuntimeException(env, exceptionMessage("uname() failed: ", err));
|
2014-04-11 15:54:31 +02:00
|
|
|
return NULL;
|
2014-04-17 21:41:04 +02:00
|
|
|
}
|
2014-07-17 16:00:53 +02:00
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_iovMax(JNIEnv* env, jclass clazz) {
|
2014-07-17 16:00:53 +02:00
|
|
|
return IOV_MAX;
|
|
|
|
}
|
2014-09-03 14:54:44 +02:00
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_uioMaxIov(JNIEnv* env, jclass clazz) {
|
2014-09-03 14:54:44 +02:00
|
|
|
return UIO_MAXIOV;
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jboolean JNICALL Java_io_netty_channel_epoll_Native_isSupportingSendmmsg(JNIEnv* env, jclass clazz) {
|
2014-09-03 14:54:44 +02:00
|
|
|
if (sendmmsg) {
|
|
|
|
return JNI_TRUE;
|
|
|
|
}
|
|
|
|
return JNI_FALSE;
|
|
|
|
}
|
|
|
|
|
2014-12-30 04:20:43 +01:00
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_errnoEBADF(JNIEnv* env, jclass clazz) {
|
|
|
|
return EBADF;
|
|
|
|
}
|
|
|
|
|
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_errnoEPIPE(JNIEnv* env, jclass clazz) {
|
|
|
|
return EPIPE;
|
|
|
|
}
|
|
|
|
|
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_errnoEAGAIN(JNIEnv* env, jclass clazz) {
|
|
|
|
return EAGAIN;
|
|
|
|
}
|
|
|
|
|
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_errnoEWOULDBLOCK(JNIEnv* env, jclass clazz) {
|
|
|
|
return EWOULDBLOCK;
|
|
|
|
}
|
|
|
|
|
|
|
|
JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_errnoEINPROGRESS(JNIEnv* env, jclass clazz) {
|
|
|
|
return EINPROGRESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
JNIEXPORT jstring JNICALL Java_io_netty_channel_epoll_Native_strError(JNIEnv* env, jclass clazz, jint error) {
|
|
|
|
char* err = strerror(error);
|
|
|
|
return (*env)->NewStringUTF(env, err);
|
|
|
|
}
|