update to netty 4.1.105
This commit is contained in:
parent
07c6dd718a
commit
dde0ad8a11
140 changed files with 9811 additions and 88 deletions
17
build.gradle
17
build.gradle
|
@ -4,6 +4,7 @@ plugins {
|
|||
id 'signing'
|
||||
id "io.github.gradle-nexus.publish-plugin" version "2.0.0-rc-1"
|
||||
id "com.google.osdetector" version "1.7.3"
|
||||
id 'org.xbib.gradle.plugin.c' version '3.1.0'
|
||||
}
|
||||
|
||||
wrapper {
|
||||
|
@ -11,8 +12,6 @@ wrapper {
|
|||
distributionType = Wrapper.DistributionType.BIN
|
||||
}
|
||||
|
||||
apply plugin: 'com.google.osdetector'
|
||||
|
||||
ext {
|
||||
user = 'joerg'
|
||||
name = 'netty'
|
||||
|
@ -30,11 +29,17 @@ ext {
|
|||
organizationUrl = 'https://xbib.org'
|
||||
}
|
||||
|
||||
apply plugin: 'com.google.osdetector'
|
||||
|
||||
subprojects {
|
||||
apply from: rootProject.file('gradle/repositories/maven.gradle')
|
||||
apply from: rootProject.file('gradle/compile/java.gradle')
|
||||
apply from: rootProject.file('gradle/test/junit5.gradle')
|
||||
apply from: rootProject.file('gradle/publish/maven.gradle')
|
||||
if (it.name.endsWith('-native')) {
|
||||
apply from: rootProject.file('gradle/compile/c.gradle')
|
||||
} else {
|
||||
apply from: rootProject.file('gradle/repositories/maven.gradle')
|
||||
apply from: rootProject.file('gradle/compile/java.gradle')
|
||||
apply from: rootProject.file('gradle/test/junit5.gradle')
|
||||
apply from: rootProject.file('gradle/publish/maven.gradle')
|
||||
}
|
||||
}
|
||||
apply from: rootProject.file('gradle/publish/sonatype.gradle')
|
||||
apply from: rootProject.file('gradle/publish/forgejo.gradle')
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
group = org.xbib.netty
|
||||
name = netty
|
||||
version = 4.1.104
|
||||
version = 4.1.105.0
|
||||
|
|
3
gradle/compile/c.gradle
Normal file
3
gradle/compile/c.gradle
Normal file
|
@ -0,0 +1,3 @@
|
|||
|
||||
apply plugin: 'base'
|
||||
apply plugin: 'org.xbib.gradle.plugin.c'
|
|
@ -1,21 +1,12 @@
|
|||
|
||||
apply plugin: 'com.google.osdetector'
|
||||
|
||||
dependencies {
|
||||
testImplementation project(':netty-channel-unix')
|
||||
testImplementation project(':netty-channel-epoll')
|
||||
testImplementation project(':netty-testsuite')
|
||||
testImplementation project(':netty-handler')
|
||||
testImplementation testLibs.assertj
|
||||
testImplementation testLibs.rerunner.jupiter
|
||||
testRuntimeOnly project(path: ':netty-tcnative-boringssl-static', configuration: osdetector.classifier)
|
||||
}
|
||||
|
||||
task nettyEpollLinuxX8664(type: Jar) {
|
||||
archiveBaseName.set('netty-channel-epoll-native')
|
||||
destinationDirectory.set(project.layout.buildDirectory.dir('libs'))
|
||||
archiveBaseName.set(project.name + '-' + project.version)
|
||||
archiveExtension.set('jar')
|
||||
archiveClassifier.set('linux-x86_64')
|
||||
version rootProject.version
|
||||
from (sourceSets.main.output) {
|
||||
from (project.layout.projectDirectory.dir('src/main/resources')) {
|
||||
include 'META-INF/native/libnetty_transport_native_epoll_x86_64.so'
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +16,13 @@ configurations {
|
|||
'linux-x86_64' {
|
||||
canBeConsumed = true
|
||||
canBeResolved = false
|
||||
extendsFrom runtimeOnly
|
||||
attributes {
|
||||
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.LIBRARY))
|
||||
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
|
||||
attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EXTERNAL))
|
||||
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, JavaVersion.current().majorVersion.toInteger())
|
||||
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements, 'linux-x86_64'))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
909
netty-channel-epoll-native/src/main/c/netty_epoll_linuxsocket.c
Normal file
909
netty-channel-epoll-native/src/main/c/netty_epoll_linuxsocket.c
Normal file
|
@ -0,0 +1,909 @@
|
|||
/*
|
||||
* Copyright 2016 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:
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Since glibc 2.8, the _GNU_SOURCE feature test macro must be defined
|
||||
* (before including any header files) in order to obtain the
|
||||
* definition of the ucred structure. See <a href=https://linux.die.net/man/7/unix>
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/udp.h> // SOL_UDP
|
||||
#include <sys/sendfile.h>
|
||||
#include <linux/tcp.h> // TCP_NOTSENT_LOWAT is a linux specific define
|
||||
#include "netty_epoll_linuxsocket.h"
|
||||
#include "netty_epoll_vmsocket.h"
|
||||
#include "netty_unix_errors.h"
|
||||
#include "netty_unix_filedescriptor.h"
|
||||
#include "netty_unix_jni.h"
|
||||
#include "netty_unix_socket.h"
|
||||
#include "netty_unix_util.h"
|
||||
|
||||
#define LINUXSOCKET_CLASSNAME "io/netty/channel/epoll/LinuxSocket"
|
||||
|
||||
// TCP_FASTOPEN is defined in linux 3.7. We define this here so older kernels can compile.
|
||||
#ifndef TCP_FASTOPEN
|
||||
#define TCP_FASTOPEN 23
|
||||
#endif
|
||||
|
||||
// TCP_FASTOPEN_CONNECT is defined in linux 4.11. We define this here so older kernels can compile.
|
||||
#ifndef TCP_FASTOPEN_CONNECT
|
||||
#define TCP_FASTOPEN_CONNECT 30
|
||||
#endif
|
||||
|
||||
// TCP_NOTSENT_LOWAT is defined in linux 3.12. We define this here so older kernels can compile.
|
||||
#ifndef TCP_NOTSENT_LOWAT
|
||||
#define TCP_NOTSENT_LOWAT 25
|
||||
#endif
|
||||
|
||||
// SO_BUSY_POLL is defined in linux 3.11. We define this here so older kernels can compile.
|
||||
#ifndef SO_BUSY_POLL
|
||||
#define SO_BUSY_POLL 46
|
||||
#endif
|
||||
|
||||
// UDP_GRO is defined in linux 5. We define this here so older kernels can compile.
|
||||
#ifndef UDP_GRO
|
||||
#define UDP_GRO 104
|
||||
#endif
|
||||
|
||||
static jweak peerCredentialsClassWeak = NULL;
|
||||
static jmethodID peerCredentialsMethodId = NULL;
|
||||
|
||||
static jfieldID fileChannelFieldId = NULL;
|
||||
static jfieldID transferredFieldId = NULL;
|
||||
static jfieldID fdFieldId = NULL;
|
||||
static jfieldID fileDescriptorFieldId = NULL;
|
||||
|
||||
// JNI Registered Methods Begin
|
||||
static jint netty_epoll_linuxsocket_newVSockStreamFd(JNIEnv* env, jclass clazz) {
|
||||
int fd = netty_unix_socket_nonBlockingSocket(AF_VSOCK, SOCK_STREAM, 0);
|
||||
if (fd == -1) {
|
||||
return -errno;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
static jint netty_epoll_linuxsocket_bindVSock(JNIEnv* env, jclass clazz, jint fd, jint cid, jint port) {
|
||||
struct sockaddr_vm addr;
|
||||
memset(&addr, 0, sizeof(struct sockaddr_vm));
|
||||
|
||||
addr.svm_family = AF_VSOCK;
|
||||
addr.svm_port = port;
|
||||
addr.svm_cid = cid;
|
||||
|
||||
int res = bind(fd, (struct sockaddr*) &addr, sizeof(struct sockaddr_vm));
|
||||
|
||||
if (res == -1) {
|
||||
return -errno;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static jint netty_epoll_linuxsocket_connectVSock(JNIEnv* env, jclass clazz, jint fd, jint cid, jint port) {
|
||||
struct sockaddr_vm addr;
|
||||
memset(&addr, 0, sizeof(struct sockaddr_vm));
|
||||
addr.svm_family = AF_VSOCK;
|
||||
addr.svm_port = port;
|
||||
addr.svm_cid = cid;
|
||||
|
||||
int res;
|
||||
int err;
|
||||
do {
|
||||
res = connect(fd, (struct sockaddr*) &addr, sizeof(struct sockaddr_vm));
|
||||
} while (res == -1 && ((err = errno) == EINTR));
|
||||
|
||||
if (res == -1) {
|
||||
return -errno;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static jbyteArray createVSockAddressArray(JNIEnv* env, const struct sockaddr_vm* addr) {
|
||||
jbyteArray bArray = (*env)->NewByteArray(env, 8);
|
||||
if (bArray == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned int cid = (addr->svm_cid);
|
||||
unsigned int port = (addr->svm_port);
|
||||
|
||||
unsigned char a[4];
|
||||
a[0] = cid >> 24;
|
||||
a[1] = cid >> 16;
|
||||
a[2] = cid >> 8;
|
||||
a[3] = cid;
|
||||
(*env)->SetByteArrayRegion(env, bArray, 0, 4, (jbyte*) &a);
|
||||
|
||||
a[0] = port >> 24;
|
||||
a[1] = port >> 16;
|
||||
a[2] = port >> 8;
|
||||
a[3] = port;
|
||||
(*env)->SetByteArrayRegion(env, bArray, 4, 4, (jbyte*) &a);
|
||||
return bArray;
|
||||
}
|
||||
|
||||
static jbyteArray netty_epoll_linuxsocket_remoteVSockAddress(JNIEnv* env, jclass clazz, jint fd) {
|
||||
struct sockaddr_vm addr = { 0 };
|
||||
socklen_t len = sizeof(addr);
|
||||
if (getpeername(fd, (struct sockaddr*) &addr, &len) == -1) {
|
||||
return NULL;
|
||||
}
|
||||
return createVSockAddressArray(env, &addr);
|
||||
}
|
||||
|
||||
static jbyteArray netty_epoll_linuxsocket_localVSockAddress(JNIEnv* env, jclass clazz, jint fd) {
|
||||
struct sockaddr_vm addr = { 0 };
|
||||
socklen_t len = sizeof(addr);
|
||||
if (getsockname(fd, (struct sockaddr*) &addr, &len) == -1) {
|
||||
return NULL;
|
||||
}
|
||||
return createVSockAddressArray(env, &addr);
|
||||
}
|
||||
|
||||
static void netty_epoll_linuxsocket_setTimeToLive(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
||||
netty_unix_socket_setOption(env, fd, IPPROTO_IP, IP_TTL, &optval, sizeof(optval));
|
||||
}
|
||||
|
||||
static void netty_epoll_linuxsocket_setIpMulticastLoop(JNIEnv* env, jclass clazz, jint fd, jboolean ipv6, jint optval) {
|
||||
if (ipv6 == JNI_TRUE) {
|
||||
u_int val = (u_int) optval;
|
||||
netty_unix_socket_setOption(env, fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, sizeof(val));
|
||||
} else {
|
||||
u_char val = (u_char) optval;
|
||||
netty_unix_socket_setOption(env, fd, IPPROTO_IP, IP_MULTICAST_LOOP, &val, sizeof(val));
|
||||
}
|
||||
}
|
||||
|
||||
static void netty_epoll_linuxsocket_setInterface(JNIEnv* env, jclass clazz, jint fd, jboolean ipv6, jbyteArray interfaceAddress, jint scopeId, jint interfaceIndex) {
|
||||
struct sockaddr_storage interfaceAddr;
|
||||
socklen_t interfaceAddrSize;
|
||||
struct sockaddr_in* interfaceIpAddr;
|
||||
|
||||
memset(&interfaceAddr, 0, sizeof(interfaceAddr));
|
||||
|
||||
if (ipv6 == JNI_TRUE) {
|
||||
if (interfaceIndex == -1) {
|
||||
netty_unix_errors_throwIOException(env, "Unable to find network index");
|
||||
return;
|
||||
}
|
||||
netty_unix_socket_setOption(env, fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &interfaceIndex, sizeof(interfaceIndex));
|
||||
} else {
|
||||
if (netty_unix_socket_initSockaddr(env, ipv6, interfaceAddress, scopeId, 0, &interfaceAddr, &interfaceAddrSize) == -1) {
|
||||
netty_unix_errors_throwIOException(env, "Could not init sockaddr");
|
||||
return;
|
||||
}
|
||||
|
||||
interfaceIpAddr = (struct sockaddr_in*) &interfaceAddr;
|
||||
netty_unix_socket_setOption(env, fd, IPPROTO_IP, IP_MULTICAST_IF, &interfaceIpAddr->sin_addr, sizeof(interfaceIpAddr->sin_addr));
|
||||
}
|
||||
}
|
||||
|
||||
static void netty_epoll_linuxsocket_setTcpCork(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
||||
netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_CORK, &optval, sizeof(optval));
|
||||
}
|
||||
|
||||
static void netty_epoll_linuxsocket_setTcpQuickAck(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
||||
netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_QUICKACK, &optval, sizeof(optval));
|
||||
}
|
||||
|
||||
static void netty_epoll_linuxsocket_setTcpDeferAccept(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
||||
netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &optval, sizeof(optval));
|
||||
}
|
||||
|
||||
static void netty_epoll_linuxsocket_setTcpNotSentLowAt(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
||||
netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_NOTSENT_LOWAT, &optval, sizeof(optval));
|
||||
}
|
||||
|
||||
static void netty_epoll_linuxsocket_setTcpFastOpen(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
||||
netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_FASTOPEN, &optval, sizeof(optval));
|
||||
}
|
||||
|
||||
static void netty_epoll_linuxsocket_setTcpKeepIdle(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
||||
netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_KEEPIDLE, &optval, sizeof(optval));
|
||||
}
|
||||
|
||||
static void netty_epoll_linuxsocket_setTcpKeepIntvl(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
||||
netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_KEEPINTVL, &optval, sizeof(optval));
|
||||
}
|
||||
|
||||
static void netty_epoll_linuxsocket_setTcpKeepCnt(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
||||
netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_KEEPCNT, &optval, sizeof(optval));
|
||||
}
|
||||
|
||||
static void netty_epoll_linuxsocket_setTcpUserTimeout(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
||||
netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &optval, sizeof(optval));
|
||||
}
|
||||
|
||||
static void netty_epoll_linuxsocket_setIpFreeBind(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
||||
netty_unix_socket_setOption(env, fd, IPPROTO_IP, IP_FREEBIND, &optval, sizeof(optval));
|
||||
}
|
||||
|
||||
static void netty_epoll_linuxsocket_setIpTransparent(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
||||
netty_unix_socket_setOption(env, fd, SOL_IP, IP_TRANSPARENT, &optval, sizeof(optval));
|
||||
}
|
||||
|
||||
static void netty_epoll_linuxsocket_setIpRecvOrigDestAddr(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
||||
netty_unix_socket_setOption(env, fd, IPPROTO_IP, IP_RECVORIGDSTADDR, &optval, sizeof(optval));
|
||||
}
|
||||
|
||||
static void netty_epoll_linuxsocket_setSoBusyPoll(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
||||
netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_BUSY_POLL, &optval, sizeof(optval));
|
||||
}
|
||||
|
||||
static void netty_epoll_linuxsocket_joinGroup(JNIEnv* env, jclass clazz, jint fd, jboolean ipv6, jbyteArray groupAddress, jbyteArray interfaceAddress, jint scopeId, jint interfaceIndex) {
|
||||
struct sockaddr_storage groupAddr;
|
||||
socklen_t groupAddrSize;
|
||||
struct sockaddr_storage interfaceAddr;
|
||||
socklen_t interfaceAddrSize;
|
||||
struct sockaddr_in* groupIpAddr;
|
||||
struct sockaddr_in* interfaceIpAddr;
|
||||
struct ip_mreq mreq;
|
||||
|
||||
struct sockaddr_in6* groupIp6Addr;
|
||||
struct ipv6_mreq mreq6;
|
||||
|
||||
memset(&groupAddr, 0, sizeof(groupAddr));
|
||||
memset(&interfaceAddr, 0, sizeof(interfaceAddr));
|
||||
|
||||
if (netty_unix_socket_initSockaddr(env, ipv6, groupAddress, scopeId, 0, &groupAddr, &groupAddrSize) == -1) {
|
||||
netty_unix_errors_throwIOException(env, "Could not init sockaddr for groupAddress");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (groupAddr.ss_family) {
|
||||
case AF_INET:
|
||||
if (netty_unix_socket_initSockaddr(env, ipv6, interfaceAddress, scopeId, 0, &interfaceAddr, &interfaceAddrSize) == -1) {
|
||||
netty_unix_errors_throwIOException(env, "Could not init sockaddr for interfaceAddr");
|
||||
return;
|
||||
}
|
||||
|
||||
interfaceIpAddr = (struct sockaddr_in*) &interfaceAddr;
|
||||
groupIpAddr = (struct sockaddr_in*) &groupAddr;
|
||||
|
||||
memcpy(&mreq.imr_multiaddr, &groupIpAddr->sin_addr, sizeof(groupIpAddr->sin_addr));
|
||||
memcpy(&mreq.imr_interface, &interfaceIpAddr->sin_addr, sizeof(interfaceIpAddr->sin_addr));
|
||||
netty_unix_socket_setOption(env, fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
|
||||
break;
|
||||
case AF_INET6:
|
||||
if (interfaceIndex == -1) {
|
||||
netty_unix_errors_throwIOException(env, "Unable to find network index");
|
||||
return;
|
||||
}
|
||||
mreq6.ipv6mr_interface = interfaceIndex;
|
||||
|
||||
groupIp6Addr = (struct sockaddr_in6*) &groupAddr;
|
||||
memcpy(&mreq6.ipv6mr_multiaddr, &groupIp6Addr->sin6_addr, sizeof(groupIp6Addr->sin6_addr));
|
||||
netty_unix_socket_setOption(env, fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6, sizeof(mreq6));
|
||||
break;
|
||||
default:
|
||||
netty_unix_errors_throwIOException(env, "Address family not supported");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void netty_epoll_linuxsocket_joinSsmGroup(JNIEnv* env, jclass clazz, jint fd, jboolean ipv6, jbyteArray groupAddress, jbyteArray interfaceAddress, jint scopeId, jint interfaceIndex, jbyteArray sourceAddress) {
|
||||
struct sockaddr_storage groupAddr;
|
||||
socklen_t groupAddrSize;
|
||||
struct sockaddr_storage interfaceAddr;
|
||||
socklen_t interfaceAddrSize;
|
||||
struct sockaddr_storage sourceAddr;
|
||||
socklen_t sourceAddrSize;
|
||||
struct sockaddr_in* groupIpAddr;
|
||||
struct sockaddr_in* interfaceIpAddr;
|
||||
struct sockaddr_in* sourceIpAddr;
|
||||
struct ip_mreq_source mreq;
|
||||
|
||||
struct group_source_req mreq6;
|
||||
|
||||
memset(&groupAddr, 0, sizeof(groupAddr));
|
||||
memset(&sourceAddr, 0, sizeof(sourceAddr));
|
||||
memset(&interfaceAddr, 0, sizeof(interfaceAddr));
|
||||
|
||||
if (netty_unix_socket_initSockaddr(env, ipv6, groupAddress, scopeId, 0, &groupAddr, &groupAddrSize) == -1) {
|
||||
netty_unix_errors_throwIOException(env, "Could not init sockaddr for groupAddress");
|
||||
return;
|
||||
}
|
||||
|
||||
if (netty_unix_socket_initSockaddr(env, ipv6, sourceAddress, scopeId, 0, &sourceAddr, &sourceAddrSize) == -1) {
|
||||
netty_unix_errors_throwIOException(env, "Could not init sockaddr for sourceAddress");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (groupAddr.ss_family) {
|
||||
case AF_INET:
|
||||
if (netty_unix_socket_initSockaddr(env, ipv6, interfaceAddress, scopeId, 0, &interfaceAddr, &interfaceAddrSize) == -1) {
|
||||
netty_unix_errors_throwIOException(env, "Could not init sockaddr for interfaceAddress");
|
||||
return;
|
||||
}
|
||||
interfaceIpAddr = (struct sockaddr_in*) &interfaceAddr;
|
||||
groupIpAddr = (struct sockaddr_in*) &groupAddr;
|
||||
sourceIpAddr = (struct sockaddr_in*) &sourceAddr;
|
||||
memcpy(&mreq.imr_multiaddr, &groupIpAddr->sin_addr, sizeof(groupIpAddr->sin_addr));
|
||||
memcpy(&mreq.imr_interface, &interfaceIpAddr->sin_addr, sizeof(interfaceIpAddr->sin_addr));
|
||||
memcpy(&mreq.imr_sourceaddr, &sourceIpAddr->sin_addr, sizeof(sourceIpAddr->sin_addr));
|
||||
netty_unix_socket_setOption(env, fd, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, &mreq, sizeof(mreq));
|
||||
break;
|
||||
case AF_INET6:
|
||||
if (interfaceIndex == -1) {
|
||||
netty_unix_errors_throwIOException(env, "Unable to find network index");
|
||||
return;
|
||||
}
|
||||
mreq6.gsr_group = groupAddr;
|
||||
mreq6.gsr_interface = interfaceIndex;
|
||||
mreq6.gsr_source = sourceAddr;
|
||||
netty_unix_socket_setOption(env, fd, IPPROTO_IPV6, MCAST_JOIN_SOURCE_GROUP, &mreq6, sizeof(mreq6));
|
||||
break;
|
||||
default:
|
||||
netty_unix_errors_throwIOException(env, "Address family not supported");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void netty_epoll_linuxsocket_leaveGroup(JNIEnv* env, jclass clazz, jint fd, jboolean ipv6, jbyteArray groupAddress, jbyteArray interfaceAddress, jint scopeId, jint interfaceIndex) {
|
||||
struct sockaddr_storage groupAddr;
|
||||
socklen_t groupAddrSize;
|
||||
|
||||
struct sockaddr_storage interfaceAddr;
|
||||
socklen_t interfaceAddrSize;
|
||||
struct sockaddr_in* groupIpAddr;
|
||||
struct sockaddr_in* interfaceIpAddr;
|
||||
struct ip_mreq mreq;
|
||||
|
||||
struct sockaddr_in6* groupIp6Addr;
|
||||
struct ipv6_mreq mreq6;
|
||||
|
||||
memset(&groupAddr, 0, sizeof(groupAddr));
|
||||
memset(&interfaceAddr, 0, sizeof(interfaceAddr));
|
||||
|
||||
if (netty_unix_socket_initSockaddr(env, ipv6, groupAddress, scopeId, 0, &groupAddr, &groupAddrSize) == -1) {
|
||||
netty_unix_errors_throwIOException(env, "Could not init sockaddr for groupAddress");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (groupAddr.ss_family) {
|
||||
case AF_INET:
|
||||
if (netty_unix_socket_initSockaddr(env, ipv6, interfaceAddress, scopeId, 0, &interfaceAddr, &interfaceAddrSize) == -1) {
|
||||
netty_unix_errors_throwIOException(env, "Could not init sockaddr for interfaceAddress");
|
||||
return;
|
||||
}
|
||||
interfaceIpAddr = (struct sockaddr_in*) &interfaceAddr;
|
||||
groupIpAddr = (struct sockaddr_in*) &groupAddr;
|
||||
|
||||
memcpy(&mreq.imr_multiaddr, &groupIpAddr->sin_addr, sizeof(groupIpAddr->sin_addr));
|
||||
memcpy(&mreq.imr_interface, &interfaceIpAddr->sin_addr, sizeof(interfaceIpAddr->sin_addr));
|
||||
netty_unix_socket_setOption(env, fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
|
||||
break;
|
||||
case AF_INET6:
|
||||
if (interfaceIndex == -1) {
|
||||
netty_unix_errors_throwIOException(env, "Unable to find network index");
|
||||
return;
|
||||
}
|
||||
mreq6.ipv6mr_interface = interfaceIndex;
|
||||
|
||||
groupIp6Addr = (struct sockaddr_in6*) &groupAddr;
|
||||
memcpy(&mreq6.ipv6mr_multiaddr, &groupIp6Addr->sin6_addr, sizeof(groupIp6Addr->sin6_addr));
|
||||
netty_unix_socket_setOption(env, fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq6, sizeof(mreq6));
|
||||
break;
|
||||
default:
|
||||
netty_unix_errors_throwIOException(env, "Address family not supported");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void netty_epoll_linuxsocket_leaveSsmGroup(JNIEnv* env, jclass clazz, jint fd, jboolean ipv6, jbyteArray groupAddress, jbyteArray interfaceAddress, jint scopeId, jint interfaceIndex, jbyteArray sourceAddress) {
|
||||
struct sockaddr_storage groupAddr;
|
||||
socklen_t groupAddrSize;
|
||||
struct sockaddr_storage interfaceAddr;
|
||||
socklen_t interfaceAddrSize;
|
||||
struct sockaddr_storage sourceAddr;
|
||||
socklen_t sourceAddrSize;
|
||||
struct sockaddr_in* groupIpAddr;
|
||||
struct sockaddr_in* interfaceIpAddr;
|
||||
struct sockaddr_in* sourceIpAddr;
|
||||
|
||||
struct ip_mreq_source mreq;
|
||||
struct group_source_req mreq6;
|
||||
|
||||
memset(&groupAddr, 0, sizeof(groupAddr));
|
||||
memset(&sourceAddr, 0, sizeof(sourceAddr));
|
||||
memset(&interfaceAddr, 0, sizeof(interfaceAddr));
|
||||
|
||||
|
||||
if (netty_unix_socket_initSockaddr(env, ipv6, groupAddress, scopeId, 0, &groupAddr, &groupAddrSize) == -1) {
|
||||
netty_unix_errors_throwIOException(env, "Could not init sockaddr for groupAddress");
|
||||
return;
|
||||
}
|
||||
|
||||
if (netty_unix_socket_initSockaddr(env, ipv6, sourceAddress, scopeId, 0, &sourceAddr, &sourceAddrSize) == -1) {
|
||||
netty_unix_errors_throwIOException(env, "Could not init sockaddr for sourceAddress");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (groupAddr.ss_family) {
|
||||
case AF_INET:
|
||||
if (netty_unix_socket_initSockaddr(env, ipv6, interfaceAddress, scopeId, 0, &interfaceAddr, &interfaceAddrSize) == -1) {
|
||||
netty_unix_errors_throwIOException(env, "Could not init sockaddr for interfaceAddress");
|
||||
return;
|
||||
}
|
||||
interfaceIpAddr = (struct sockaddr_in*) &interfaceAddr;
|
||||
|
||||
groupIpAddr = (struct sockaddr_in*) &groupAddr;
|
||||
sourceIpAddr = (struct sockaddr_in*) &sourceAddr;
|
||||
memcpy(&mreq.imr_multiaddr, &groupIpAddr->sin_addr, sizeof(groupIpAddr->sin_addr));
|
||||
memcpy(&mreq.imr_interface, &interfaceIpAddr->sin_addr, sizeof(interfaceIpAddr->sin_addr));
|
||||
memcpy(&mreq.imr_sourceaddr, &sourceIpAddr->sin_addr, sizeof(sourceIpAddr->sin_addr));
|
||||
netty_unix_socket_setOption(env, fd, IPPROTO_IP, IP_DROP_SOURCE_MEMBERSHIP, &mreq, sizeof(mreq));
|
||||
break;
|
||||
case AF_INET6:
|
||||
if (interfaceIndex == -1) {
|
||||
netty_unix_errors_throwIOException(env, "Unable to find network index");
|
||||
return;
|
||||
}
|
||||
|
||||
mreq6.gsr_group = groupAddr;
|
||||
mreq6.gsr_interface = interfaceIndex;
|
||||
mreq6.gsr_source = sourceAddr;
|
||||
netty_unix_socket_setOption(env, fd, IPPROTO_IPV6, MCAST_LEAVE_SOURCE_GROUP, &mreq6, sizeof(mreq6));
|
||||
break;
|
||||
default:
|
||||
netty_unix_errors_throwIOException(env, "Address family not supported");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void netty_epoll_linuxsocket_setTcpMd5Sig(JNIEnv* env, jclass clazz, jint fd, jboolean ipv6, jbyteArray address, jint scopeId, jbyteArray key) {
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t addrSize;
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
|
||||
if (netty_unix_socket_initSockaddr(env, ipv6, address, scopeId, 0, &addr, &addrSize) == -1) {
|
||||
netty_unix_errors_throwIOException(env, "Could not init sockaddr");
|
||||
return;
|
||||
}
|
||||
|
||||
struct tcp_md5sig md5sig;
|
||||
memset(&md5sig, 0, sizeof(md5sig));
|
||||
md5sig.tcpm_addr.ss_family = addr.ss_family;
|
||||
|
||||
struct sockaddr_in* ipaddr;
|
||||
struct sockaddr_in6* ip6addr;
|
||||
|
||||
switch (addr.ss_family) {
|
||||
case AF_INET:
|
||||
ipaddr = (struct sockaddr_in*) &addr;
|
||||
memcpy(&((struct sockaddr_in *) &md5sig.tcpm_addr)->sin_addr, &ipaddr->sin_addr, sizeof(ipaddr->sin_addr));
|
||||
break;
|
||||
case AF_INET6:
|
||||
ip6addr = (struct sockaddr_in6*) &addr;
|
||||
memcpy(&((struct sockaddr_in6 *) &md5sig.tcpm_addr)->sin6_addr, &ip6addr->sin6_addr, sizeof(ip6addr->sin6_addr));
|
||||
break;
|
||||
}
|
||||
|
||||
if (key != NULL) {
|
||||
md5sig.tcpm_keylen = (*env)->GetArrayLength(env, key);
|
||||
(*env)->GetByteArrayRegion(env, key, 0, md5sig.tcpm_keylen, (void *) &md5sig.tcpm_key);
|
||||
if ((*env)->ExceptionCheck(env) == JNI_TRUE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_MD5SIG, &md5sig, sizeof(md5sig)) < 0) {
|
||||
netty_unix_errors_throwIOExceptionErrorNo(env, "setsockopt() failed: ", errno);
|
||||
}
|
||||
}
|
||||
|
||||
static int netty_epoll_linuxsocket_getInterface(JNIEnv* env, jclass clazz, jint fd, jboolean ipv6) {
|
||||
if (ipv6 == JNI_TRUE) {
|
||||
int optval;
|
||||
if (netty_unix_socket_getOption(env, fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &optval, sizeof(optval)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return optval;
|
||||
} else {
|
||||
struct in_addr optval;
|
||||
if (netty_unix_socket_getOption(env, fd, IPPROTO_IP, IP_MULTICAST_IF, &optval, sizeof(optval)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ntohl(optval.s_addr);
|
||||
}
|
||||
}
|
||||
|
||||
static jint netty_epoll_linuxsocket_getTimeToLive(JNIEnv* env, jclass clazz, jint fd) {
|
||||
int optval;
|
||||
if (netty_unix_socket_getOption(env, fd, IPPROTO_IP, IP_TTL, &optval, sizeof(optval)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return optval;
|
||||
}
|
||||
|
||||
|
||||
static jint netty_epoll_linuxsocket_getIpMulticastLoop(JNIEnv* env, jclass clazz, jint fd, jboolean ipv6) {
|
||||
if (ipv6 == JNI_TRUE) {
|
||||
u_int optval;
|
||||
if (netty_unix_socket_getOption(env, fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &optval, sizeof(optval)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return (jint) optval;
|
||||
} else {
|
||||
u_char optval;
|
||||
if (netty_unix_socket_getOption(env, fd, IPPROTO_IP, IP_MULTICAST_LOOP, &optval, sizeof(optval)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return (jint) optval;
|
||||
}
|
||||
}
|
||||
|
||||
static jint netty_epoll_linuxsocket_getTcpKeepIdle(JNIEnv* env, jclass clazz, jint fd) {
|
||||
int optval;
|
||||
if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_KEEPIDLE, &optval, sizeof(optval)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return optval;
|
||||
}
|
||||
|
||||
static jint netty_epoll_linuxsocket_getTcpKeepIntvl(JNIEnv* env, jclass clazz, jint fd) {
|
||||
int optval;
|
||||
if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_KEEPINTVL, &optval, sizeof(optval)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return optval;
|
||||
}
|
||||
|
||||
static jint netty_epoll_linuxsocket_getTcpKeepCnt(JNIEnv* env, jclass clazz, jint fd) {
|
||||
int optval;
|
||||
if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_KEEPCNT, &optval, sizeof(optval)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return optval;
|
||||
}
|
||||
|
||||
static jint netty_epoll_linuxsocket_getTcpUserTimeout(JNIEnv* env, jclass clazz, jint fd) {
|
||||
int optval;
|
||||
if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &optval, sizeof(optval)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return optval;
|
||||
}
|
||||
|
||||
static jint netty_epoll_linuxsocket_isIpFreeBind(JNIEnv* env, jclass clazz, jint fd) {
|
||||
int optval;
|
||||
if (netty_unix_socket_getOption(env, fd, IPPROTO_IP, IP_FREEBIND, &optval, sizeof(optval)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return optval;
|
||||
}
|
||||
|
||||
static jint netty_epoll_linuxsocket_isIpTransparent(JNIEnv* env, jclass clazz, jint fd) {
|
||||
int optval;
|
||||
if (netty_unix_socket_getOption(env, fd, SOL_IP, IP_TRANSPARENT, &optval, sizeof(optval)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return optval;
|
||||
}
|
||||
|
||||
static jint netty_epoll_linuxsocket_isIpRecvOrigDestAddr(JNIEnv* env, jclass clazz, jint fd) {
|
||||
int optval;
|
||||
if (netty_unix_socket_getOption(env, fd, IPPROTO_IP, IP_RECVORIGDSTADDR, &optval, sizeof(optval)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return optval;
|
||||
}
|
||||
|
||||
static void netty_epoll_linuxsocket_getTcpInfo(JNIEnv* env, jclass clazz, jint fd, jlongArray array) {
|
||||
struct tcp_info tcp_info;
|
||||
if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_INFO, &tcp_info, sizeof(tcp_info)) == -1) {
|
||||
return;
|
||||
}
|
||||
jlong cArray[32];
|
||||
// Expand to 64 bits, then cast away unsigned-ness.
|
||||
cArray[0] = (jlong) (uint64_t) tcp_info.tcpi_state;
|
||||
cArray[1] = (jlong) (uint64_t) tcp_info.tcpi_ca_state;
|
||||
cArray[2] = (jlong) (uint64_t) tcp_info.tcpi_retransmits;
|
||||
cArray[3] = (jlong) (uint64_t) tcp_info.tcpi_probes;
|
||||
cArray[4] = (jlong) (uint64_t) tcp_info.tcpi_backoff;
|
||||
cArray[5] = (jlong) (uint64_t) tcp_info.tcpi_options;
|
||||
cArray[6] = (jlong) (uint64_t) tcp_info.tcpi_snd_wscale;
|
||||
cArray[7] = (jlong) (uint64_t) tcp_info.tcpi_rcv_wscale;
|
||||
cArray[8] = (jlong) (uint64_t) tcp_info.tcpi_rto;
|
||||
cArray[9] = (jlong) (uint64_t) tcp_info.tcpi_ato;
|
||||
cArray[10] = (jlong) (uint64_t) tcp_info.tcpi_snd_mss;
|
||||
cArray[11] = (jlong) (uint64_t) tcp_info.tcpi_rcv_mss;
|
||||
cArray[12] = (jlong) (uint64_t) tcp_info.tcpi_unacked;
|
||||
cArray[13] = (jlong) (uint64_t) tcp_info.tcpi_sacked;
|
||||
cArray[14] = (jlong) (uint64_t) tcp_info.tcpi_lost;
|
||||
cArray[15] = (jlong) (uint64_t) tcp_info.tcpi_retrans;
|
||||
cArray[16] = (jlong) (uint64_t) tcp_info.tcpi_fackets;
|
||||
cArray[17] = (jlong) (uint64_t) tcp_info.tcpi_last_data_sent;
|
||||
cArray[18] = (jlong) (uint64_t) tcp_info.tcpi_last_ack_sent;
|
||||
cArray[19] = (jlong) (uint64_t) tcp_info.tcpi_last_data_recv;
|
||||
cArray[20] = (jlong) (uint64_t) tcp_info.tcpi_last_ack_recv;
|
||||
cArray[21] = (jlong) (uint64_t) tcp_info.tcpi_pmtu;
|
||||
cArray[22] = (jlong) (uint64_t) tcp_info.tcpi_rcv_ssthresh;
|
||||
cArray[23] = (jlong) (uint64_t) tcp_info.tcpi_rtt;
|
||||
cArray[24] = (jlong) (uint64_t) tcp_info.tcpi_rttvar;
|
||||
cArray[25] = (jlong) (uint64_t) tcp_info.tcpi_snd_ssthresh;
|
||||
cArray[26] = (jlong) (uint64_t) tcp_info.tcpi_snd_cwnd;
|
||||
cArray[27] = (jlong) (uint64_t) tcp_info.tcpi_advmss;
|
||||
cArray[28] = (jlong) (uint64_t) tcp_info.tcpi_reordering;
|
||||
cArray[29] = (jlong) (uint64_t) tcp_info.tcpi_rcv_rtt;
|
||||
cArray[30] = (jlong) (uint64_t) tcp_info.tcpi_rcv_space;
|
||||
cArray[31] = (jlong) (uint64_t) tcp_info.tcpi_total_retrans;
|
||||
|
||||
(*env)->SetLongArrayRegion(env, array, 0, 32, cArray);
|
||||
}
|
||||
|
||||
static jint netty_epoll_linuxsocket_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;
|
||||
}
|
||||
|
||||
static jint netty_epoll_linuxsocket_getSoBusyPoll(JNIEnv* env, jclass clazz, jint fd) {
|
||||
int optval;
|
||||
if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_BUSY_POLL, &optval, sizeof(optval)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return optval;
|
||||
}
|
||||
|
||||
static jint netty_epoll_linuxsocket_getTcpDeferAccept(JNIEnv* env, jclass clazz, jint fd) {
|
||||
int optval;
|
||||
if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &optval, sizeof(optval)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return optval;
|
||||
}
|
||||
|
||||
static jint netty_epoll_linuxsocket_isTcpQuickAck(JNIEnv* env, jclass clazz, jint fd) {
|
||||
int optval;
|
||||
if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_QUICKACK, &optval, sizeof(optval)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return optval;
|
||||
}
|
||||
|
||||
static jint netty_epoll_linuxsocket_getTcpNotSentLowAt(JNIEnv* env, jclass clazz, jint fd) {
|
||||
int optval;
|
||||
if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_NOTSENT_LOWAT, &optval, sizeof(optval)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return optval;
|
||||
}
|
||||
|
||||
static jobject netty_epoll_linuxsocket_getPeerCredentials(JNIEnv *env, jclass clazz, jint fd) {
|
||||
struct ucred credentials;
|
||||
jclass peerCredentialsClass = NULL;
|
||||
if(netty_unix_socket_getOption(env,fd, SOL_SOCKET, SO_PEERCRED, &credentials, sizeof (credentials)) == -1) {
|
||||
return NULL;
|
||||
}
|
||||
jintArray gids = (*env)->NewIntArray(env, 1);
|
||||
(*env)->SetIntArrayRegion(env, gids, 0, 1, (jint*) &credentials.gid);
|
||||
|
||||
NETTY_JNI_UTIL_NEW_LOCAL_FROM_WEAK(env, peerCredentialsClass, peerCredentialsClassWeak, error);
|
||||
|
||||
jobject creds = (*env)->NewObject(env, peerCredentialsClass, peerCredentialsMethodId, credentials.pid, credentials.uid, gids);
|
||||
|
||||
NETTY_JNI_UTIL_DELETE_LOCAL(env, peerCredentialsClass);
|
||||
return creds;
|
||||
error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static jint netty_epoll_linuxsocket_isUdpGro(JNIEnv* env, jclass clazz, jint fd) {
|
||||
int optval;
|
||||
if (netty_unix_socket_getOption(env, fd, SOL_UDP, UDP_GRO, &optval, sizeof(optval)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return optval;
|
||||
}
|
||||
|
||||
static void netty_epoll_linuxsocket_setUdpGro(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
||||
netty_unix_socket_setOption(env, fd, SOL_UDP, UDP_GRO, &optval, sizeof(optval));
|
||||
}
|
||||
|
||||
|
||||
static jlong netty_epoll_linuxsocket_sendFile(JNIEnv* env, jclass clazz, jint fd, jobject fileRegion, jlong base_off, jlong off, jlong len) {
|
||||
jobject fileChannel = (*env)->GetObjectField(env, fileRegion, fileChannelFieldId);
|
||||
if (fileChannel == NULL) {
|
||||
netty_unix_errors_throwRuntimeException(env, "failed to get DefaultFileRegion.file");
|
||||
return -1;
|
||||
}
|
||||
jobject fileDescriptor = (*env)->GetObjectField(env, fileChannel, fileDescriptorFieldId);
|
||||
if (fileDescriptor == NULL) {
|
||||
netty_unix_errors_throwRuntimeException(env, "failed to get FileChannelImpl.fd");
|
||||
return -1;
|
||||
}
|
||||
jint srcFd = (*env)->GetIntField(env, fileDescriptor, fdFieldId);
|
||||
if (srcFd == -1) {
|
||||
netty_unix_errors_throwRuntimeException(env, "failed to get FileDescriptor.fd");
|
||||
return -1;
|
||||
}
|
||||
ssize_t res;
|
||||
off_t offset = base_off + off;
|
||||
int err;
|
||||
do {
|
||||
res = sendfile(fd, srcFd, &offset, (size_t) len);
|
||||
} while (res == -1 && ((err = errno) == EINTR));
|
||||
if (res < 0) {
|
||||
return -err;
|
||||
}
|
||||
if (res > 0) {
|
||||
// update the transferred field in DefaultFileRegion
|
||||
(*env)->SetLongField(env, fileRegion, transferredFieldId, off + res);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// JNI Registered Methods End
|
||||
|
||||
// JNI Method Registration Table Begin
|
||||
static const JNINativeMethod fixed_method_table[] = {
|
||||
{ "newVSockStreamFd", "()I", (void *) netty_epoll_linuxsocket_newVSockStreamFd },
|
||||
{ "bindVSock", "(III)I", (void *) netty_epoll_linuxsocket_bindVSock },
|
||||
{ "connectVSock", "(III)I", (void *) netty_epoll_linuxsocket_connectVSock },
|
||||
{ "remoteVSockAddress", "(I)[B", (void *) netty_epoll_linuxsocket_remoteVSockAddress },
|
||||
{ "localVSockAddress", "(I)[B", (void *) netty_epoll_linuxsocket_localVSockAddress },
|
||||
{ "setTimeToLive", "(II)V", (void *) netty_epoll_linuxsocket_setTimeToLive },
|
||||
{ "getTimeToLive", "(I)I", (void *) netty_epoll_linuxsocket_getTimeToLive },
|
||||
{ "setInterface", "(IZ[BII)V", (void *) netty_epoll_linuxsocket_setInterface },
|
||||
{ "getInterface", "(IZ)I", (void *) netty_epoll_linuxsocket_getInterface },
|
||||
{ "setIpMulticastLoop", "(IZI)V", (void * ) netty_epoll_linuxsocket_setIpMulticastLoop },
|
||||
{ "getIpMulticastLoop", "(IZ)I", (void * ) netty_epoll_linuxsocket_getIpMulticastLoop },
|
||||
{ "setTcpCork", "(II)V", (void *) netty_epoll_linuxsocket_setTcpCork },
|
||||
{ "setSoBusyPoll", "(II)V", (void *) netty_epoll_linuxsocket_setSoBusyPoll },
|
||||
{ "setTcpQuickAck", "(II)V", (void *) netty_epoll_linuxsocket_setTcpQuickAck },
|
||||
{ "setTcpDeferAccept", "(II)V", (void *) netty_epoll_linuxsocket_setTcpDeferAccept },
|
||||
{ "setTcpNotSentLowAt", "(II)V", (void *) netty_epoll_linuxsocket_setTcpNotSentLowAt },
|
||||
{ "isTcpCork", "(I)I", (void *) netty_epoll_linuxsocket_isTcpCork },
|
||||
{ "getSoBusyPoll", "(I)I", (void *) netty_epoll_linuxsocket_getSoBusyPoll },
|
||||
{ "getTcpDeferAccept", "(I)I", (void *) netty_epoll_linuxsocket_getTcpDeferAccept },
|
||||
{ "getTcpNotSentLowAt", "(I)I", (void *) netty_epoll_linuxsocket_getTcpNotSentLowAt },
|
||||
{ "isTcpQuickAck", "(I)I", (void *) netty_epoll_linuxsocket_isTcpQuickAck },
|
||||
{ "setTcpFastOpen", "(II)V", (void *) netty_epoll_linuxsocket_setTcpFastOpen },
|
||||
{ "setTcpKeepIdle", "(II)V", (void *) netty_epoll_linuxsocket_setTcpKeepIdle },
|
||||
{ "setTcpKeepIntvl", "(II)V", (void *) netty_epoll_linuxsocket_setTcpKeepIntvl },
|
||||
{ "setTcpKeepCnt", "(II)V", (void *) netty_epoll_linuxsocket_setTcpKeepCnt },
|
||||
{ "setTcpUserTimeout", "(II)V", (void *) netty_epoll_linuxsocket_setTcpUserTimeout },
|
||||
{ "setIpFreeBind", "(II)V", (void *) netty_epoll_linuxsocket_setIpFreeBind },
|
||||
{ "setIpTransparent", "(II)V", (void *) netty_epoll_linuxsocket_setIpTransparent },
|
||||
{ "setIpRecvOrigDestAddr", "(II)V", (void *) netty_epoll_linuxsocket_setIpRecvOrigDestAddr },
|
||||
{ "getTcpKeepIdle", "(I)I", (void *) netty_epoll_linuxsocket_getTcpKeepIdle },
|
||||
{ "getTcpKeepIntvl", "(I)I", (void *) netty_epoll_linuxsocket_getTcpKeepIntvl },
|
||||
{ "getTcpKeepCnt", "(I)I", (void *) netty_epoll_linuxsocket_getTcpKeepCnt },
|
||||
{ "getTcpUserTimeout", "(I)I", (void *) netty_epoll_linuxsocket_getTcpUserTimeout },
|
||||
{ "isIpFreeBind", "(I)I", (void *) netty_epoll_linuxsocket_isIpFreeBind },
|
||||
{ "isIpTransparent", "(I)I", (void *) netty_epoll_linuxsocket_isIpTransparent },
|
||||
{ "isIpRecvOrigDestAddr", "(I)I", (void *) netty_epoll_linuxsocket_isIpRecvOrigDestAddr },
|
||||
{ "getTcpInfo", "(I[J)V", (void *) netty_epoll_linuxsocket_getTcpInfo },
|
||||
{ "setTcpMd5Sig", "(IZ[BI[B)V", (void *) netty_epoll_linuxsocket_setTcpMd5Sig },
|
||||
{ "joinGroup", "(IZ[B[BII)V", (void *) netty_epoll_linuxsocket_joinGroup },
|
||||
{ "joinSsmGroup", "(IZ[B[BII[B)V", (void *) netty_epoll_linuxsocket_joinSsmGroup },
|
||||
{ "leaveGroup", "(IZ[B[BII)V", (void *) netty_epoll_linuxsocket_leaveGroup },
|
||||
{ "leaveSsmGroup", "(IZ[B[BII[B)V", (void *) netty_epoll_linuxsocket_leaveSsmGroup },
|
||||
{ "isUdpGro", "(I)I", (void *) netty_epoll_linuxsocket_isUdpGro },
|
||||
{ "setUdpGro", "(II)V", (void *) netty_epoll_linuxsocket_setUdpGro }
|
||||
|
||||
// "sendFile" has a dynamic signature
|
||||
};
|
||||
|
||||
static const jint fixed_method_table_size = sizeof(fixed_method_table) / sizeof(fixed_method_table[0]);
|
||||
|
||||
static jint dynamicMethodsTableSize() {
|
||||
return fixed_method_table_size + 2; // 2 is for the dynamic method signatures.
|
||||
}
|
||||
|
||||
static JNINativeMethod* createDynamicMethodsTable(const char* packagePrefix) {
|
||||
char* dynamicTypeName = NULL;
|
||||
size_t size = sizeof(JNINativeMethod) * dynamicMethodsTableSize();
|
||||
JNINativeMethod* dynamicMethods = malloc(size);
|
||||
if (dynamicMethods == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
memset(dynamicMethods, 0, size);
|
||||
memcpy(dynamicMethods, fixed_method_table, sizeof(fixed_method_table));
|
||||
|
||||
JNINativeMethod* dynamicMethod = &dynamicMethods[fixed_method_table_size];
|
||||
NETTY_JNI_UTIL_PREPEND(packagePrefix, "io/netty/channel/unix/PeerCredentials;", dynamicTypeName, error);
|
||||
NETTY_JNI_UTIL_PREPEND("(I)L", dynamicTypeName, dynamicMethod->signature, error);
|
||||
dynamicMethod->name = "getPeerCredentials";
|
||||
dynamicMethod->fnPtr = (void *) netty_epoll_linuxsocket_getPeerCredentials;
|
||||
netty_jni_util_free_dynamic_name(&dynamicTypeName);
|
||||
|
||||
++dynamicMethod;
|
||||
NETTY_JNI_UTIL_PREPEND(packagePrefix, "io/netty/channel/DefaultFileRegion;JJJ)J", dynamicTypeName, error);
|
||||
NETTY_JNI_UTIL_PREPEND("(IL", dynamicTypeName, dynamicMethod->signature, error);
|
||||
dynamicMethod->name = "sendFile";
|
||||
dynamicMethod->fnPtr = (void *) netty_epoll_linuxsocket_sendFile;
|
||||
netty_jni_util_free_dynamic_name(&dynamicTypeName);
|
||||
return dynamicMethods;
|
||||
error:
|
||||
free(dynamicTypeName);
|
||||
netty_jni_util_free_dynamic_methods_table(dynamicMethods, fixed_method_table_size, dynamicMethodsTableSize());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// JNI Method Registration Table End
|
||||
|
||||
// IMPORTANT: If you add any NETTY_JNI_UTIL_LOAD_CLASS or NETTY_JNI_UTIL_FIND_CLASS calls you also need to update
|
||||
// Native to reflect that.
|
||||
jint netty_epoll_linuxsocket_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) {
|
||||
int ret = JNI_ERR;
|
||||
char* nettyClassName = NULL;
|
||||
jclass fileRegionCls = NULL;
|
||||
jclass fileChannelCls = NULL;
|
||||
jclass fileDescriptorCls = NULL;
|
||||
jclass peerCredentialsClass = NULL;
|
||||
// Register the methods which are not referenced by static member variables
|
||||
JNINativeMethod* dynamicMethods = createDynamicMethodsTable(packagePrefix);
|
||||
if (dynamicMethods == NULL) {
|
||||
goto done;
|
||||
}
|
||||
if (netty_jni_util_register_natives(env,
|
||||
packagePrefix,
|
||||
LINUXSOCKET_CLASSNAME,
|
||||
dynamicMethods,
|
||||
dynamicMethodsTableSize()) != 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
NETTY_JNI_UTIL_PREPEND(packagePrefix, "io/netty/channel/unix/PeerCredentials", nettyClassName, done);
|
||||
|
||||
NETTY_JNI_UTIL_LOAD_CLASS_WEAK(env, peerCredentialsClassWeak, nettyClassName, done);
|
||||
NETTY_JNI_UTIL_NEW_LOCAL_FROM_WEAK(env, peerCredentialsClass, peerCredentialsClassWeak, done);
|
||||
|
||||
netty_jni_util_free_dynamic_name(&nettyClassName);
|
||||
|
||||
NETTY_JNI_UTIL_GET_METHOD(env, peerCredentialsClass, peerCredentialsMethodId, "<init>", "(II[I)V", done);
|
||||
|
||||
NETTY_JNI_UTIL_PREPEND(packagePrefix, "io/netty/channel/DefaultFileRegion", nettyClassName, done);
|
||||
NETTY_JNI_UTIL_FIND_CLASS(env, fileRegionCls, nettyClassName, done);
|
||||
netty_jni_util_free_dynamic_name(&nettyClassName);
|
||||
|
||||
NETTY_JNI_UTIL_GET_FIELD(env, fileRegionCls, fileChannelFieldId, "file", "Ljava/nio/channels/FileChannel;", done);
|
||||
NETTY_JNI_UTIL_GET_FIELD(env, fileRegionCls, transferredFieldId, "transferred", "J", done);
|
||||
|
||||
NETTY_JNI_UTIL_FIND_CLASS(env, fileChannelCls, "sun/nio/ch/FileChannelImpl", done);
|
||||
NETTY_JNI_UTIL_GET_FIELD(env, fileChannelCls, fileDescriptorFieldId, "fd", "Ljava/io/FileDescriptor;", done);
|
||||
|
||||
NETTY_JNI_UTIL_FIND_CLASS(env, fileDescriptorCls, "java/io/FileDescriptor", done);
|
||||
NETTY_JNI_UTIL_TRY_GET_FIELD(env, fileDescriptorCls, fdFieldId, "fd", "I");
|
||||
if (fdFieldId == NULL) {
|
||||
// Android uses a different field name, let's try it.
|
||||
NETTY_JNI_UTIL_GET_FIELD(env, fileDescriptorCls, fdFieldId, "descriptor", "I", done);
|
||||
}
|
||||
ret = NETTY_JNI_UTIL_JNI_VERSION;
|
||||
done:
|
||||
netty_jni_util_free_dynamic_methods_table(dynamicMethods, fixed_method_table_size, dynamicMethodsTableSize());
|
||||
free(nettyClassName);
|
||||
|
||||
NETTY_JNI_UTIL_DELETE_LOCAL(env, peerCredentialsClass);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void netty_epoll_linuxsocket_JNI_OnUnLoad(JNIEnv* env, const char* packagePrefix) {
|
||||
NETTY_JNI_UTIL_UNLOAD_CLASS_WEAK(env, peerCredentialsClassWeak);
|
||||
|
||||
netty_jni_util_unregister_natives(env, packagePrefix, LINUXSOCKET_CLASSNAME);
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2016 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:
|
||||
*
|
||||
* https://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_EPOLL_LINUXSOCKET_H_
|
||||
#define NETTY_EPOLL_LINUXSOCKET_H_
|
||||
|
||||
#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_epoll_linuxsocket_JNI_OnLoad(JNIEnv* env, const char* packagePrefix);
|
||||
void netty_epoll_linuxsocket_JNI_OnUnLoad(JNIEnv* env, const char* packagePrefix);
|
||||
|
||||
#endif
|
919
netty-channel-epoll-native/src/main/c/netty_epoll_native.c
Normal file
919
netty-channel-epoll-native/src/main/c/netty_epoll_native.c
Normal file
|
@ -0,0 +1,919 @@
|
|||
/*
|
||||
* 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:
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <jni.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <sys/un.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/timerfd.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <stddef.h>
|
||||
#include <limits.h>
|
||||
#include <inttypes.h>
|
||||
#include <link.h>
|
||||
#include <time.h>
|
||||
// Needed to be able to use syscalls directly and so not depend on newer GLIBC versions
|
||||
#include <linux/net.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
// Needed for UDP_SEGMENT
|
||||
#include <netinet/udp.h>
|
||||
|
||||
#include "netty_epoll_linuxsocket.h"
|
||||
#include "netty_unix_buffer.h"
|
||||
#include "netty_unix_errors.h"
|
||||
#include "netty_unix_filedescriptor.h"
|
||||
#include "netty_unix_jni.h"
|
||||
#include "netty_unix_limits.h"
|
||||
#include "netty_unix_socket.h"
|
||||
#include "netty_unix_util.h"
|
||||
#include "netty_unix.h"
|
||||
|
||||
// Add define if NETTY_BUILD_STATIC is defined so it is picked up in netty_jni_util.c
|
||||
#ifdef NETTY_BUILD_STATIC
|
||||
#define NETTY_JNI_UTIL_BUILD_STATIC
|
||||
#endif
|
||||
|
||||
#define STATICALLY_CLASSNAME "io/netty/channel/epoll/NativeStaticallyReferencedJniMethods"
|
||||
#define NATIVE_CLASSNAME "io/netty/channel/epoll/Native"
|
||||
|
||||
// TCP_FASTOPEN is defined in linux 3.7. We define this here so older kernels can compile.
|
||||
#ifndef TCP_FASTOPEN
|
||||
#define TCP_FASTOPEN 23
|
||||
#endif
|
||||
|
||||
// Allow to compile on systems with older kernels.
|
||||
#ifndef UDP_SEGMENT
|
||||
#define UDP_SEGMENT 103
|
||||
#endif
|
||||
|
||||
// UDP_GRO is defined in linux 5. We define this here so older kernels can compile.
|
||||
#ifndef UDP_GRO
|
||||
#define UDP_GRO 104
|
||||
#endif
|
||||
|
||||
#ifdef IP_RECVORIGDSTADDR
|
||||
#if !defined(SOL_IP) && defined(IPPROTO_IP)
|
||||
#define SOL_IP IPPROTO_IP
|
||||
#endif /* !SOL_IP && IPPROTO_IP */
|
||||
#endif // IP_RECVORIGDSTADDR
|
||||
|
||||
// optional
|
||||
extern int epoll_create1(int flags) __attribute__((weak));
|
||||
extern int epoll_pwait2(int epfd, struct epoll_event *events, int maxevents, const struct timespec *timeout, const sigset_t *sigmask) __attribute__((weak));
|
||||
|
||||
#ifndef __USE_GNU
|
||||
struct mmsghdr {
|
||||
struct msghdr msg_hdr; /* Message header */
|
||||
unsigned int msg_len; /* Number of bytes transmitted */
|
||||
};
|
||||
#endif
|
||||
|
||||
// All linux syscall numbers are stable so this is safe.
|
||||
#ifndef SYS_recvmmsg
|
||||
// Only support SYS_recvmmsg for __x86_64__ / __i386__ for now
|
||||
#if defined(__x86_64__)
|
||||
// See https://github.com/torvalds/linux/blob/v5.4/arch/x86/entry/syscalls/syscall_64.tbl
|
||||
#define SYS_recvmmsg 299
|
||||
#elif defined(__i386__)
|
||||
// See https://github.com/torvalds/linux/blob/v5.4/arch/x86/entry/syscalls/syscall_32.tbl
|
||||
#define SYS_recvmmsg 337
|
||||
#elif defined(__loongarch64)
|
||||
// See https://github.com/torvalds/linux/blob/v6.1/include/uapi/asm-generic/unistd.h
|
||||
#define SYS_recvmmsg 243
|
||||
#else
|
||||
#define SYS_recvmmsg -1
|
||||
#endif
|
||||
#endif // SYS_recvmmsg
|
||||
|
||||
#ifndef SYS_sendmmsg
|
||||
// Only support SYS_sendmmsg for __x86_64__ / __i386__ for now
|
||||
#if defined(__x86_64__)
|
||||
// See https://github.com/torvalds/linux/blob/v5.4/arch/x86/entry/syscalls/syscall_64.tbl
|
||||
#define SYS_sendmmsg 307
|
||||
#elif defined(__i386__)
|
||||
// See https://github.com/torvalds/linux/blob/v5.4/arch/x86/entry/syscalls/syscall_32.tbl
|
||||
#define SYS_sendmmsg 345
|
||||
#elif defined(__loongarch64)
|
||||
// See https://github.com/torvalds/linux/blob/v6.1/include/uapi/asm-generic/unistd.h
|
||||
#define SYS_sendmmsg 269
|
||||
#else
|
||||
#define SYS_sendmmsg -1
|
||||
#endif
|
||||
#endif // SYS_sendmmsg
|
||||
|
||||
// Those are initialized in the init(...) method and cached for performance reasons
|
||||
static jfieldID packetSenderAddrFieldId = NULL;
|
||||
static jfieldID packetSenderAddrLenFieldId = NULL;
|
||||
static jfieldID packetSenderScopeIdFieldId = NULL;
|
||||
static jfieldID packetSenderPortFieldId = NULL;
|
||||
static jfieldID packetRecipientAddrFieldId = NULL;
|
||||
static jfieldID packetRecipientAddrLenFieldId = NULL;
|
||||
static jfieldID packetRecipientScopeIdFieldId = NULL;
|
||||
static jfieldID packetRecipientPortFieldId = NULL;
|
||||
|
||||
static jfieldID packetSegmentSizeFieldId = NULL;
|
||||
static jfieldID packetMemoryAddressFieldId = NULL;
|
||||
static jfieldID packetCountFieldId = NULL;
|
||||
|
||||
static const char* staticPackagePrefix = NULL;
|
||||
static int register_unix_called = 0;
|
||||
static int epoll_pwait2_supported = 0;
|
||||
|
||||
// util methods
|
||||
static int getSysctlValue(const char * property, int* returnValue) {
|
||||
int rc = -1;
|
||||
FILE *fd=fopen(property, "r");
|
||||
if (fd != NULL) {
|
||||
char buf[32] = {0x0};
|
||||
if (fgets(buf, 32, fd) != NULL) {
|
||||
*returnValue = atoi(buf);
|
||||
rc = 0;
|
||||
}
|
||||
fclose(fd);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline jint epollCtl(JNIEnv* env, jint efd, int op, jint fd, jint flags) {
|
||||
uint32_t events = flags;
|
||||
struct epoll_event ev = {
|
||||
.data.fd = fd,
|
||||
.events = events
|
||||
};
|
||||
|
||||
return epoll_ctl(efd, op, fd, &ev);
|
||||
}
|
||||
// JNI Registered Methods Begin
|
||||
static jint netty_epoll_native_eventFd(JNIEnv* env, jclass clazz) {
|
||||
jint eventFD = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
|
||||
|
||||
if (eventFD < 0) {
|
||||
netty_unix_errors_throwChannelExceptionErrorNo(env, "eventfd() failed: ", errno);
|
||||
}
|
||||
return eventFD;
|
||||
}
|
||||
|
||||
static jint netty_epoll_native_timerFd(JNIEnv* env, jclass clazz) {
|
||||
jint timerFD = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
|
||||
|
||||
if (timerFD < 0) {
|
||||
netty_unix_errors_throwChannelExceptionErrorNo(env, "timerfd_create() failed: ", errno);
|
||||
}
|
||||
return timerFD;
|
||||
}
|
||||
|
||||
static void netty_epoll_native_eventFdWrite(JNIEnv* env, jclass clazz, jint fd, jlong value) {
|
||||
uint64_t val;
|
||||
|
||||
for (;;) {
|
||||
jint ret = eventfd_write(fd, (eventfd_t) value);
|
||||
|
||||
if (ret < 0) {
|
||||
// We need to read before we can write again, let's try to read and then write again and if this
|
||||
// fails we will bail out.
|
||||
//
|
||||
// See https://man7.org/linux/man-pages/man2/eventfd.2.html.
|
||||
if (errno == EAGAIN) {
|
||||
if (eventfd_read(fd, &val) == 0 || errno == EAGAIN) {
|
||||
// Try again
|
||||
continue;
|
||||
}
|
||||
netty_unix_errors_throwChannelExceptionErrorNo(env, "eventfd_read(...) failed: ", errno);
|
||||
} else {
|
||||
netty_unix_errors_throwChannelExceptionErrorNo(env, "eventfd_write(...) failed: ", errno);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void netty_epoll_native_eventFdRead(JNIEnv* env, jclass clazz, jint fd) {
|
||||
uint64_t eventfd_t;
|
||||
|
||||
if (eventfd_read(fd, &eventfd_t) != 0) {
|
||||
// something is serious wrong
|
||||
netty_unix_errors_throwRuntimeException(env, "eventfd_read() failed");
|
||||
}
|
||||
}
|
||||
|
||||
static jint netty_epoll_native_epollCreate(JNIEnv* env, jclass clazz) {
|
||||
jint efd;
|
||||
if (epoll_create1) {
|
||||
efd = epoll_create1(EPOLL_CLOEXEC);
|
||||
} else {
|
||||
// size will be ignored anyway but must be positive
|
||||
efd = epoll_create(126);
|
||||
}
|
||||
if (efd < 0) {
|
||||
int err = errno;
|
||||
if (epoll_create1) {
|
||||
netty_unix_errors_throwChannelExceptionErrorNo(env, "epoll_create1() failed: ", err);
|
||||
} else {
|
||||
netty_unix_errors_throwChannelExceptionErrorNo(env, "epoll_create() failed: ", err);
|
||||
}
|
||||
return efd;
|
||||
}
|
||||
if (!epoll_create1) {
|
||||
if (fcntl(efd, F_SETFD, FD_CLOEXEC) < 0) {
|
||||
int err = errno;
|
||||
close(efd);
|
||||
netty_unix_errors_throwChannelExceptionErrorNo(env, "fcntl() failed: ", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return efd;
|
||||
}
|
||||
|
||||
static jint netty_epoll_native_epollWait(JNIEnv* env, jclass clazz, jint efd, jlong address, jint len, jint timeout) {
|
||||
struct epoll_event *ev = (struct epoll_event*) (intptr_t) address;
|
||||
int result, err;
|
||||
|
||||
do {
|
||||
result = epoll_wait(efd, ev, len, timeout);
|
||||
if (result >= 0) {
|
||||
return result;
|
||||
}
|
||||
} while((err = errno) == EINTR);
|
||||
return -err;
|
||||
}
|
||||
|
||||
// This needs to be consistent with Native.java
|
||||
#define EPOLL_WAIT_RESULT(V, ARM_TIMER) ((jlong) ((uint64_t) ((uint32_t) V) << 32 | ARM_TIMER))
|
||||
|
||||
static jlong netty_epoll_native_epollWait0(JNIEnv* env, jclass clazz, jint efd, jlong address, jint len, jint timerFd, jint tvSec, jint tvNsec, jlong millisThreshold) {
|
||||
// only reschedule the timer if there is a newer event.
|
||||
// -1 is a special value used by EpollEventLoop.
|
||||
uint32_t armTimer = millisThreshold <= 0 ? 1 : 0;
|
||||
if (tvSec != ((jint) -1) && tvNsec != ((jint) -1)) {
|
||||
if (millisThreshold > 0 && (tvSec != 0 || tvNsec != 0)) {
|
||||
// Let's try to reduce the syscalls as much as possible as timerfd_settime(...) can be expensive:
|
||||
// See https://github.com/netty/netty/issues/11695
|
||||
|
||||
if (epoll_pwait2_supported == 1) {
|
||||
// We have epoll_pwait2(...) and it is supported, this means we can just pass in the itimerspec directly and not need an
|
||||
// extra syscall even for very small timeouts.
|
||||
struct timespec ts = { tvSec, tvNsec };
|
||||
struct epoll_event *ev = (struct epoll_event*) (intptr_t) address;
|
||||
int result, err;
|
||||
do {
|
||||
result = epoll_pwait2(efd, ev, len, &ts, NULL);
|
||||
if (result >= 0) {
|
||||
return EPOLL_WAIT_RESULT(result, armTimer);
|
||||
}
|
||||
} while((err = errno) == EINTR);
|
||||
return EPOLL_WAIT_RESULT(-err, armTimer);
|
||||
}
|
||||
|
||||
int millis = tvNsec / 1000000;
|
||||
// Check if we can reduce the syscall overhead by just use epoll_wait. This is done in cases when we can
|
||||
// tolerate some "drift".
|
||||
if (tvNsec == 0 ||
|
||||
// Let's use the threshold to accept that we may be not 100 % accurate and ignore anything that
|
||||
// is smaller then 1 ms.
|
||||
millis >= millisThreshold ||
|
||||
tvSec > 0) {
|
||||
millis += tvSec * 1000;
|
||||
int result = netty_epoll_native_epollWait(env, clazz, efd, address, len, millis);
|
||||
return EPOLL_WAIT_RESULT(result, armTimer);
|
||||
}
|
||||
}
|
||||
struct itimerspec ts;
|
||||
memset(&ts.it_interval, 0, sizeof(struct timespec));
|
||||
ts.it_value.tv_sec = tvSec;
|
||||
ts.it_value.tv_nsec = tvNsec;
|
||||
if (timerfd_settime(timerFd, 0, &ts, NULL) < 0) {
|
||||
netty_unix_errors_throwChannelExceptionErrorNo(env, "timerfd_settime() failed: ", errno);
|
||||
return -1;
|
||||
}
|
||||
armTimer = 1;
|
||||
}
|
||||
int result = netty_epoll_native_epollWait(env, clazz, efd, address, len, -1);
|
||||
return EPOLL_WAIT_RESULT(result, armTimer);
|
||||
}
|
||||
|
||||
static inline void cpu_relax() {
|
||||
#if defined(__x86_64__)
|
||||
asm volatile("pause\n": : :"memory");
|
||||
#elif defined(__aarch64__)
|
||||
asm volatile("isb\n": : :"memory");
|
||||
#elif defined(__riscv) && defined(__riscv_zihintpause)
|
||||
asm volatile("pause\n": : :"memory");
|
||||
#endif
|
||||
}
|
||||
|
||||
static jint netty_epoll_native_epollBusyWait0(JNIEnv* env, jclass clazz, jint efd, jlong address, jint len) {
|
||||
struct epoll_event *ev = (struct epoll_event*) (intptr_t) address;
|
||||
int result, err;
|
||||
|
||||
// Zeros = poll (aka return immediately).
|
||||
do {
|
||||
result = epoll_wait(efd, ev, len, 0);
|
||||
if (result == 0) {
|
||||
// Since we're always polling epoll_wait with no timeout,
|
||||
// signal CPU that we're in a busy loop
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
if (result >= 0) {
|
||||
return result;
|
||||
}
< |