New native transport for kqueue
Motivation: We currently don't have a native transport which supports kqueue https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2. This can be useful for BSD systems such as MacOS to take advantage of native features, and provide feature parity with the Linux native transport. Modifications: - Make a new transport-native-unix-common module with all the java classes and JNI code for generic unix items. This module will build a static library for each unix platform, and included in the dynamic libraries used for JNI (e.g. transport-native-epoll, and eventually kqueue). - Make a new transport-native-unix-common-tests module where the tests for the transport-native-unix-common module will live. This is so each unix platform can inherit from these test and ensure they pass. - Add a new transport-native-kqueue module which uses JNI to directly interact with kqueue Result: JNI support for kqueue. Fixes https://github.com/netty/netty/issues/2448 Fixes https://github.com/netty/netty/issues/4231
This commit is contained in:
parent
2d38a4453c
commit
3cc4052963
43
all/pom.xml
43
all/pom.xml
@ -36,7 +36,11 @@
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>full</id>
|
||||
|
||||
<activation>
|
||||
<property>
|
||||
<name>uber</name>
|
||||
</property>
|
||||
</activation>
|
||||
<!-- Only include in full profile as this will not work on Java9 yet -->
|
||||
<!-- https://issues.apache.org/jira/browse/JXR-133 -->
|
||||
<build>
|
||||
@ -162,25 +166,6 @@
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>linux</id>
|
||||
<activation>
|
||||
<os>
|
||||
<family>linux</family>
|
||||
</os>
|
||||
</activation>
|
||||
<dependencies>
|
||||
<!-- All release modules -->
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>netty-transport-native-epoll</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<classifier>${epoll.classifier}</classifier>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<dependencies>
|
||||
@ -347,6 +332,22 @@
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>netty-transport-native-epoll</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<classifier>linux-x86_64</classifier>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>netty-transport-native-kqueue</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<classifier>osx-x86_64</classifier>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Add optional dependencies explicitly to avoid Javadoc warnings and errors. -->
|
||||
<dependency>
|
||||
@ -429,7 +430,7 @@
|
||||
<goal>unpack-dependencies</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<excludes>io/netty/example/**,META-INF/native/libnetty-tcnative*</excludes>
|
||||
<excludes>io/netty/example/**,META-INF/native/libnetty-tcnative*,META-INF/native/include/**,META-INF/native/**/*.a</excludes>
|
||||
<includes>io/netty/**,META-INF/native/**</includes>
|
||||
<includeScope>runtime</includeScope>
|
||||
<includeGroupIds>${project.groupId}</includeGroupIds>
|
||||
|
@ -24,6 +24,9 @@ import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
@ -271,12 +274,27 @@ public final class NetUtil {
|
||||
logger.debug("{}: {}", file, somaxconn);
|
||||
}
|
||||
} else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("{}: {} (non-existent)", file, somaxconn);
|
||||
// Try to get from sysctl
|
||||
Integer tmp = null;
|
||||
if (SystemPropertyUtil.getBoolean("io.netty.net.somaxconn.trySysctl", false)) {
|
||||
tmp = sysctlGetInt("kern.ipc.somaxconn");
|
||||
if (tmp == null) {
|
||||
tmp = sysctlGetInt("kern.ipc.soacceptqueue");
|
||||
if (tmp != null) {
|
||||
somaxconn = tmp;
|
||||
}
|
||||
} else {
|
||||
somaxconn = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
if (tmp == null) {
|
||||
logger.debug("Failed to get SOMAXCONN from sysctl and file {}. Default: {}", file,
|
||||
somaxconn);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.debug("Failed to get SOMAXCONN from: {}", file, e);
|
||||
logger.debug("Failed to get SOMAXCONN from sysctl and file {}. Default: {}", file, somaxconn, e);
|
||||
} finally {
|
||||
if (in != null) {
|
||||
try {
|
||||
@ -291,6 +309,38 @@ public final class NetUtil {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This will execute <a href ="https://www.freebsd.org/cgi/man.cgi?sysctl(8)">sysctl</a> with the {@code sysctlKey}
|
||||
* which is expected to return the numeric value for for {@code sysctlKey}.
|
||||
* @param sysctlKey The key which the return value corresponds to.
|
||||
* @return The <a href ="https://www.freebsd.org/cgi/man.cgi?sysctl(8)">sysctl</a> value for {@code sysctlKey}.
|
||||
*/
|
||||
private static Integer sysctlGetInt(String sysctlKey) throws IOException {
|
||||
Process process = new ProcessBuilder("sysctl", sysctlKey).start();
|
||||
try {
|
||||
InputStream is = process.getInputStream();
|
||||
InputStreamReader isr = new InputStreamReader(is);
|
||||
BufferedReader br = new BufferedReader(isr);
|
||||
try {
|
||||
String line = br.readLine();
|
||||
if (line.startsWith(sysctlKey)) {
|
||||
for (int i = line.length() - 1; i > sysctlKey.length(); --i) {
|
||||
if (!Character.isDigit(line.charAt(i))) {
|
||||
return Integer.valueOf(line.substring(i + 1, line.length()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} finally {
|
||||
br.close();
|
||||
}
|
||||
} finally {
|
||||
if (process != null) {
|
||||
process.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if IPv4 should be used even if the system supports both IPv4 and IPv6. Setting this
|
||||
* property to {@code true} will disable IPv6 support. The default value of this property is {@code false}.
|
||||
|
@ -24,6 +24,7 @@ import java.security.cert.X509Certificate;
|
||||
|
||||
public final class EmptyArrays {
|
||||
|
||||
public static final int[] EMPTY_INTS = {};
|
||||
public static final byte[] EMPTY_BYTES = {};
|
||||
public static final char[] EMPTY_CHARS = {};
|
||||
public static final Object[] EMPTY_OBJECTS = {};
|
||||
|
@ -287,6 +287,10 @@ public final class PlatformDependent {
|
||||
PlatformDependent0.freeMemory(address);
|
||||
}
|
||||
|
||||
public static long reallocateMemory(long address, long newSize) {
|
||||
return PlatformDependent0.reallocateMemory(address, newSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Raises an exception bypassing compiler checks for checked exceptions.
|
||||
*/
|
||||
|
@ -752,6 +752,10 @@ final class PlatformDependent0 {
|
||||
UNSAFE.freeMemory(address);
|
||||
}
|
||||
|
||||
static long reallocateMemory(long address, long newSize) {
|
||||
return UNSAFE.reallocateMemory(address, newSize);
|
||||
}
|
||||
|
||||
private PlatformDependent0() {
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,7 @@
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>netty-transport-native-epoll</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<classifier>${epoll.classifier}</classifier>
|
||||
<classifier>${jni.classifier}</classifier>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
|
125
pom.xml
125
pom.xml
@ -146,55 +146,72 @@
|
||||
</os>
|
||||
</activation>
|
||||
<modules>
|
||||
<module>transport-native-unix-common-tests</module>
|
||||
<module>transport-native-unix-common</module>
|
||||
<module>transport-native-epoll</module>
|
||||
</modules>
|
||||
</profile>
|
||||
<!--
|
||||
Netty must be released from RHEL 6.8 x86_64 or compatible so that:
|
||||
|
||||
1) we ship x86_64 version of epoll transport officially, and
|
||||
2) we ensure the ABI compatibility with older GLIBC versions.
|
||||
|
||||
The shared library built on a distribution with newer GLIBC
|
||||
will not run on older distributions.
|
||||
-->
|
||||
<profile>
|
||||
<id>restricted-release</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-enforcer-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>enforce-release-environment</id>
|
||||
<goals>
|
||||
<goal>enforce</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<rules>
|
||||
<requireProperty>
|
||||
<regexMessage>
|
||||
Release process must be performed on linux-x86_64.
|
||||
</regexMessage>
|
||||
<property>os.detected.classifier</property>
|
||||
<regex>^linux-x86_64$</regex>
|
||||
</requireProperty>
|
||||
<requireFilesContent>
|
||||
<message>
|
||||
Release process must be performed on RHEL 6.8 or its derivatives.
|
||||
</message>
|
||||
<files>
|
||||
<file>/etc/redhat-release</file>
|
||||
</files>
|
||||
<content>release 6.8</content>
|
||||
</requireFilesContent>
|
||||
</rules>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<id>mac</id>
|
||||
<activation>
|
||||
<os>
|
||||
<family>mac</family>
|
||||
</os>
|
||||
</activation>
|
||||
<modules>
|
||||
<module>transport-native-unix-common-tests</module>
|
||||
<module>transport-native-unix-common</module>
|
||||
<module>transport-native-kqueue</module>
|
||||
</modules>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>freebsd</id>
|
||||
<activation>
|
||||
<os>
|
||||
<family>unix</family>
|
||||
<name>freebsd</name>
|
||||
</os>
|
||||
</activation>
|
||||
<modules>
|
||||
<module>transport-native-unix-common-tests</module>
|
||||
<module>transport-native-unix-common</module>
|
||||
<module>transport-native-kqueue</module>
|
||||
</modules>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>openbsd</id>
|
||||
<activation>
|
||||
<os>
|
||||
<family>unix</family>
|
||||
<name>openbsd</name>
|
||||
</os>
|
||||
</activation>
|
||||
<modules>
|
||||
<module>transport-native-unix-common-tests</module>
|
||||
<module>transport-native-unix-common</module>
|
||||
<module>transport-native-kqueue</module>
|
||||
</modules>
|
||||
</profile>
|
||||
<!-- The uber artifacts depend on artifacts built from different platforms and will fail if these artifacts are not
|
||||
available. Typically we only build the uber jars as part of the release process so the build sequencing
|
||||
is controlled and can be sequenced correctly. For example we may do the following:
|
||||
|
||||
<from a linux machine>
|
||||
$ mvn release
|
||||
<after this completes log onto a MacOS machine, note fail-at-end, and -D to activate multiple profiles>
|
||||
$ mvn release -fae -Duber
|
||||
-->
|
||||
<profile>
|
||||
<id>uber</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>uber</name>
|
||||
</property>
|
||||
</activation>
|
||||
<modules>
|
||||
<module>all</module>
|
||||
<module>tarball</module>
|
||||
</modules>
|
||||
</profile>
|
||||
<profile>
|
||||
<!--
|
||||
@ -252,7 +269,7 @@
|
||||
<conscrypt.artifactId>conscrypt-openjdk-uber</conscrypt.artifactId>
|
||||
<conscrypt.version>1.0.0.RC7</conscrypt.version>
|
||||
<conscrypt.classifier />
|
||||
<epoll.classifier>${os.detected.name}-${os.detected.arch}</epoll.classifier>
|
||||
<jni.classifier>${os.detected.name}-${os.detected.arch}</jni.classifier>
|
||||
<logging.config>${project.basedir}/../common/src/test/resources/logback-test.xml</logging.config>
|
||||
<logging.logLevel>debug</logging.logLevel>
|
||||
<log4j2.version>2.6.2</log4j2.version>
|
||||
@ -291,9 +308,7 @@
|
||||
<module>testsuite-autobahn</module>
|
||||
<module>testsuite-osgi</module>
|
||||
<module>microbench</module>
|
||||
<module>all</module>
|
||||
<module>bom</module>
|
||||
<module>tarball</module>
|
||||
</modules>
|
||||
|
||||
<dependencyManagement>
|
||||
@ -979,7 +994,7 @@
|
||||
<version>2.5.3</version>
|
||||
<configuration>
|
||||
<useReleaseProfile>false</useReleaseProfile>
|
||||
<arguments>-P restricted-release,sonatype-oss-release,full</arguments>
|
||||
<arguments>-P restricted-release,sonatype-oss-release</arguments>
|
||||
<autoVersionSubmodules>true</autoVersionSubmodules>
|
||||
<allowTimestampedSnapshots>false</allowTimestampedSnapshots>
|
||||
<tagNameFormat>netty-@{project.version}</tagNameFormat>
|
||||
@ -1110,18 +1125,6 @@
|
||||
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-enforcer-plugin</artifactId>
|
||||
<version>1.4.1</version>
|
||||
<dependencies>
|
||||
<!-- Provides the 'requireFilesContent' enforcer rule. -->
|
||||
<dependency>
|
||||
<groupId>com.ceilfors.maven.plugin</groupId>
|
||||
<artifactId>enforcer-rules</artifactId>
|
||||
<version>1.2.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<!-- keep surefire and failsafe in sync -->
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
|
@ -96,6 +96,11 @@
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>full</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>uber</name>
|
||||
</property>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
@ -30,7 +30,10 @@ import java.nio.channels.ClosedChannelException;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class SocketShutdownOutputBySelfTest extends AbstractClientSocketTest {
|
||||
|
||||
@ -43,9 +46,10 @@ public class SocketShutdownOutputBySelfTest extends AbstractClientSocketTest {
|
||||
TestHandler h = new TestHandler();
|
||||
ServerSocket ss = new ServerSocket();
|
||||
Socket s = null;
|
||||
SocketChannel ch = null;
|
||||
try {
|
||||
ss.bind(addr);
|
||||
SocketChannel ch = (SocketChannel) cb.handler(h).connect().sync().channel();
|
||||
ch = (SocketChannel) cb.handler(h).connect().sync().channel();
|
||||
assertTrue(ch.isActive());
|
||||
assertFalse(ch.isOutputShutdown());
|
||||
|
||||
@ -68,12 +72,15 @@ public class SocketShutdownOutputBySelfTest extends AbstractClientSocketTest {
|
||||
assertTrue(h.ch.isOutputShutdown());
|
||||
|
||||
// If half-closed, the peer should be able to write something.
|
||||
s.getOutputStream().write(1);
|
||||
s.getOutputStream().write(new byte[] { 1 });
|
||||
assertEquals(1, (int) h.queue.take());
|
||||
} finally {
|
||||
if (s != null) {
|
||||
s.close();
|
||||
}
|
||||
if (ch != null) {
|
||||
ch.close();
|
||||
}
|
||||
ss.close();
|
||||
}
|
||||
}
|
||||
|
@ -27,11 +27,81 @@
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<jni.compiler.args.ldflags>LDFLAGS=-Wl,--no-as-needed -lrt</jni.compiler.args.ldflags>
|
||||
<!-- Needed by the native transport as we need the memoryAddress of the ByteBuffer -->
|
||||
<argLine.java9.extras>--add-exports java.base/sun.security.x509=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED</argLine.java9.extras>
|
||||
<unix.common.lib.name>netty-unix-common</unix.common.lib.name>
|
||||
<unix.common.lib.dir>${project.build.directory}/unix-common-lib</unix.common.lib.dir>
|
||||
<unix.common.lib.unpacked.dir>${unix.common.lib.dir}/META-INF/native/lib</unix.common.lib.unpacked.dir>
|
||||
<unix.common.include.unpacked.dir>${unix.common.lib.dir}/META-INF/native/include</unix.common.include.unpacked.dir>
|
||||
<jni.compiler.args.ldflags>LDFLAGS=-L${unix.common.lib.unpacked.dir} -Wl,--no-as-needed -lrt -Wl,--whole-archive -l${unix.common.lib.name} -Wl,--no-whole-archive</jni.compiler.args.ldflags>
|
||||
</properties>
|
||||
|
||||
<profiles>
|
||||
<!--
|
||||
Netty must be released from RHEL 6.8 x86_64 or compatible so that:
|
||||
|
||||
1) we ship x86_64 version of epoll transport officially, and
|
||||
2) we ensure the ABI compatibility with older GLIBC versions.
|
||||
|
||||
The shared library built on a distribution with newer GLIBC
|
||||
will not run on older distributions.
|
||||
-->
|
||||
<profile>
|
||||
<id>restricted-release-epoll</id>
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-enforcer-plugin</artifactId>
|
||||
<version>1.4.1</version>
|
||||
<dependencies>
|
||||
<!-- Provides the 'requireFilesContent' enforcer rule. -->
|
||||
<dependency>
|
||||
<groupId>com.ceilfors.maven.plugin</groupId>
|
||||
<artifactId>enforcer-rules</artifactId>
|
||||
<version>1.2.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-enforcer-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>enforce-release-environment</id>
|
||||
<goals>
|
||||
<goal>enforce</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<rules>
|
||||
<requireProperty>
|
||||
<regexMessage>
|
||||
Release process must be performed on linux-x86_64.
|
||||
</regexMessage>
|
||||
<property>os.detected.classifier</property>
|
||||
<regex>^linux-x86_64$</regex>
|
||||
</requireProperty>
|
||||
<requireFilesContent>
|
||||
<message>
|
||||
Release process must be performed on RHEL 6.8 or its derivatives.
|
||||
</message>
|
||||
<files>
|
||||
<file>/etc/redhat-release</file>
|
||||
</files>
|
||||
<content>release 6.8</content>
|
||||
</requireFilesContent>
|
||||
</rules>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
@ -43,6 +113,22 @@
|
||||
<artifactId>netty-buffer</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-transport-native-unix-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<classifier>${jni.classifier}</classifier>
|
||||
<!--
|
||||
The unix-common with classifier dependency is optional because it is not a runtime dependency, but a build time
|
||||
dependency to get the static library which is built directly into the shared library generated by this project.
|
||||
-->
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-transport-native-unix-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-transport</artifactId>
|
||||
@ -54,6 +140,12 @@
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-transport-native-unix-common-tests</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>${tcnative.artifactId}</artifactId>
|
||||
@ -64,6 +156,29 @@
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<!-- unpack the unix-common static library and include files -->
|
||||
<execution>
|
||||
<id>unpack</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>unpack-dependencies</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<includeGroupIds>${project.groupId}</includeGroupIds>
|
||||
<includeArtifactIds>netty-transport-native-unix-common</includeArtifactIds>
|
||||
<classifier>${jni.classifier}</classifier>
|
||||
<outputDirectory>${unix.common.lib.dir}</outputDirectory>
|
||||
<includes>META-INF/native/**</includes>
|
||||
<overWriteReleases>false</overWriteReleases>
|
||||
<overWriteSnapshots>true</overWriteSnapshots>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.fusesource.hawtjni</groupId>
|
||||
<artifactId>maven-hawtjni-plugin</artifactId>
|
||||
@ -122,7 +237,7 @@
|
||||
<index>true</index>
|
||||
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
|
||||
</archive>
|
||||
<classifier>${epoll.classifier}</classifier>
|
||||
<classifier>${jni.classifier}</classifier>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
@ -201,7 +316,7 @@
|
||||
<value>${linux.sendmmsg.support}${glibc.sendmmsg.support}</value>
|
||||
<!-- If glibc and linux kernel are both not sufficient...then define the CFLAGS -->
|
||||
<regex>.*IO_NETTY_SENDMSSG_NOT_FOUND.*</regex>
|
||||
<replacement>CFLAGS=-O3 -DIO_NETTY_SENDMMSG_NOT_FOUND -Werror -fno-omit-frame-pointer -Wunused-variable</replacement>
|
||||
<replacement>CFLAGS=-O3 -DIO_NETTY_SENDMMSG_NOT_FOUND -Werror -fno-omit-frame-pointer -Wunused-variable -I${unix.common.include.unpacked.dir}</replacement>
|
||||
<failIfNoMatch>false</failIfNoMatch>
|
||||
</configuration>
|
||||
</execution>
|
||||
@ -217,7 +332,7 @@
|
||||
<value>${jni.compiler.args.cflags}</value>
|
||||
<!-- If glibc and linux kernel are both not sufficient...then define the CFLAGS -->
|
||||
<regex>^((?!CFLAGS=).)*$</regex>
|
||||
<replacement>CFLAGS=-O3 -Werror -fno-omit-frame-pointer -Wunused-variable</replacement>
|
||||
<replacement>CFLAGS=-O3 -Werror -fno-omit-frame-pointer -Wunused-variable -I${unix.common.include.unpacked.dir}</replacement>
|
||||
<failIfNoMatch>false</failIfNoMatch>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
346
transport-native-epoll/src/main/c/netty_epoll_linuxsocket.c
Normal file
346
transport-native-epoll/src/main/c/netty_epoll_linuxsocket.c
Normal file
@ -0,0 +1,346 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 <linux/tcp.h> // TCP_NOTSENT_LOWAT is a linux specific define
|
||||
#include "netty_unix_filedescriptor.h"
|
||||
#include "netty_unix_socket.h"
|
||||
#include "netty_unix_errors.h"
|
||||
#include "netty_unix_util.h"
|
||||
#include "netty_epoll_linuxsocket.h"
|
||||
|
||||
// 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_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
|
||||
|
||||
static jclass peerCredentialsClass = NULL;
|
||||
static jmethodID peerCredentialsMethodId = NULL;
|
||||
|
||||
// JNI Registered Methods Begin
|
||||
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_setTcpMd5Sig(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jbyteArray key) {
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t addrSize;
|
||||
if (netty_unix_socket_initSockaddr(env, address, scopeId, 0, &addr, &addrSize) == -1) {
|
||||
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_throwChannelExceptionErrorNo(env, "setsockopt() failed: ", errno);
|
||||
}
|
||||
}
|
||||
|
||||
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 void netty_epoll_linuxsocket_getTcpInfo(JNIEnv* env, jclass clazz, jint fd, jintArray array) {
|
||||
struct tcp_info tcp_info;
|
||||
if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_INFO, &tcp_info, sizeof(tcp_info)) == -1) {
|
||||
return;
|
||||
}
|
||||
unsigned int cArray[32];
|
||||
cArray[0] = tcp_info.tcpi_state;
|
||||
cArray[1] = tcp_info.tcpi_ca_state;
|
||||
cArray[2] = tcp_info.tcpi_retransmits;
|
||||
cArray[3] = tcp_info.tcpi_probes;
|
||||
cArray[4] = tcp_info.tcpi_backoff;
|
||||
cArray[5] = tcp_info.tcpi_options;
|
||||
cArray[6] = tcp_info.tcpi_snd_wscale;
|
||||
cArray[7] = tcp_info.tcpi_rcv_wscale;
|
||||
cArray[8] = tcp_info.tcpi_rto;
|
||||
cArray[9] = tcp_info.tcpi_ato;
|
||||
cArray[10] = tcp_info.tcpi_snd_mss;
|
||||
cArray[11] = tcp_info.tcpi_rcv_mss;
|
||||
cArray[12] = tcp_info.tcpi_unacked;
|
||||
cArray[13] = tcp_info.tcpi_sacked;
|
||||
cArray[14] = tcp_info.tcpi_lost;
|
||||
cArray[15] = tcp_info.tcpi_retrans;
|
||||
cArray[16] = tcp_info.tcpi_fackets;
|
||||
cArray[17] = tcp_info.tcpi_last_data_sent;
|
||||
cArray[18] = tcp_info.tcpi_last_ack_sent;
|
||||
cArray[19] = tcp_info.tcpi_last_data_recv;
|
||||
cArray[20] = tcp_info.tcpi_last_ack_recv;
|
||||
cArray[21] = tcp_info.tcpi_pmtu;
|
||||
cArray[22] = tcp_info.tcpi_rcv_ssthresh;
|
||||
cArray[23] = tcp_info.tcpi_rtt;
|
||||
cArray[24] = tcp_info.tcpi_rttvar;
|
||||
cArray[25] = tcp_info.tcpi_snd_ssthresh;
|
||||
cArray[26] = tcp_info.tcpi_snd_cwnd;
|
||||
cArray[27] = tcp_info.tcpi_advmss;
|
||||
cArray[28] = tcp_info.tcpi_reordering;
|
||||
cArray[29] = tcp_info.tcpi_rcv_rtt;
|
||||
cArray[30] = tcp_info.tcpi_rcv_space;
|
||||
cArray[31] = tcp_info.tcpi_total_retrans;
|
||||
|
||||
(*env)->SetIntArrayRegion(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_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;
|
||||
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);
|
||||
return (*env)->NewObject(env, peerCredentialsClass, peerCredentialsMethodId, credentials.pid, credentials.uid, gids);
|
||||
}
|
||||
// JNI Registered Methods End
|
||||
|
||||
// JNI Method Registration Table Begin
|
||||
static const JNINativeMethod fixed_method_table[] = {
|
||||
{ "setTcpCork", "(II)V", (void *) netty_epoll_linuxsocket_setTcpCork },
|
||||
{ "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 },
|
||||
{ "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 },
|
||||
{ "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 },
|
||||
{ "getTcpInfo", "(I[I)V", (void *) netty_epoll_linuxsocket_getTcpInfo },
|
||||
{ "setTcpMd5Sig", "(I[BI[B)V", (void *) netty_epoll_linuxsocket_setTcpMd5Sig }
|
||||
};
|
||||
|
||||
static const jint fixed_method_table_size = sizeof(fixed_method_table) / sizeof(fixed_method_table[0]);
|
||||
|
||||
static jint dynamicMethodsTableSize() {
|
||||
return fixed_method_table_size + 1;
|
||||
}
|
||||
|
||||
static JNINativeMethod* createDynamicMethodsTable(const char* packagePrefix) {
|
||||
JNINativeMethod* dynamicMethods = malloc(sizeof(JNINativeMethod) * dynamicMethodsTableSize());
|
||||
memcpy(dynamicMethods, fixed_method_table, sizeof(fixed_method_table));
|
||||
JNINativeMethod* dynamicMethod = &dynamicMethods[fixed_method_table_size];
|
||||
char* dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/unix/PeerCredentials;");
|
||||
dynamicMethod->name = "getPeerCredentials";
|
||||
dynamicMethod->signature = netty_unix_util_prepend("(I)L", dynamicTypeName);
|
||||
dynamicMethod->fnPtr = (void *) netty_epoll_linuxsocket_getPeerCredentials;
|
||||
free(dynamicTypeName);
|
||||
return dynamicMethods;
|
||||
}
|
||||
|
||||
static void freeDynamicMethodsTable(JNINativeMethod* dynamicMethods) {
|
||||
jint fullMethodTableSize = dynamicMethodsTableSize();
|
||||
jint i = fixed_method_table_size;
|
||||
for (; i < fullMethodTableSize; ++i) {
|
||||
free(dynamicMethods[i].signature);
|
||||
}
|
||||
free(dynamicMethods);
|
||||
}
|
||||
// JNI Method Registration Table End
|
||||
|
||||
jint netty_epoll_linuxsocket_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) {
|
||||
JNINativeMethod* dynamicMethods = createDynamicMethodsTable(packagePrefix);
|
||||
if (netty_unix_util_register_natives(env,
|
||||
packagePrefix,
|
||||
"io/netty/channel/epoll/LinuxSocket",
|
||||
dynamicMethods,
|
||||
dynamicMethodsTableSize()) != 0) {
|
||||
freeDynamicMethodsTable(dynamicMethods);
|
||||
return JNI_ERR;
|
||||
}
|
||||
freeDynamicMethodsTable(dynamicMethods);
|
||||
dynamicMethods = NULL;
|
||||
|
||||
char* nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/unix/PeerCredentials");
|
||||
jclass localPeerCredsClass = (*env)->FindClass(env, nettyClassName);
|
||||
free(nettyClassName);
|
||||
nettyClassName = NULL;
|
||||
if (localPeerCredsClass == NULL) {
|
||||
// pending exception...
|
||||
return JNI_ERR;
|
||||
}
|
||||
peerCredentialsClass = (jclass) (*env)->NewGlobalRef(env, localPeerCredsClass);
|
||||
if (peerCredentialsClass == NULL) {
|
||||
// out-of-memory!
|
||||
netty_unix_errors_throwOutOfMemoryError(env);
|
||||
return JNI_ERR;
|
||||
}
|
||||
peerCredentialsMethodId = (*env)->GetMethodID(env, peerCredentialsClass, "<init>", "(II[I)V");
|
||||
if (peerCredentialsMethodId == NULL) {
|
||||
netty_unix_errors_throwRuntimeException(env, "failed to get method ID: PeerCredentials.<init>(int, int, int[])");
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
|
||||
void netty_epoll_linuxsocket_JNI_OnUnLoad(JNIEnv* env) {
|
||||
if (peerCredentialsClass != NULL) {
|
||||
(*env)->DeleteGlobalRef(env, peerCredentialsClass);
|
||||
peerCredentialsClass = NULL;
|
||||
}
|
||||
}
|
26
transport-native-epoll/src/main/c/netty_epoll_linuxsocket.h
Normal file
26
transport-native-epoll/src/main/c/netty_epoll_linuxsocket.h
Normal file
@ -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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef NETTY_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);
|
||||
|
||||
#endif
|
@ -23,8 +23,8 @@
|
||||
#include <sys/eventfd.h>
|
||||
#include <sys/sendfile.h>
|
||||
#include <sys/un.h>
|
||||
#include <linux/tcp.h> // TCP_NOTSENT_LOWAT is a linux specific define
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
@ -40,28 +40,8 @@
|
||||
#include "netty_unix_socket.h"
|
||||
#include "netty_unix_errors.h"
|
||||
#include "netty_unix_util.h"
|
||||
|
||||
// Define SO_REUSEPORT if not found to fix build issues.
|
||||
// See https://github.com/netty/netty/issues/2558
|
||||
#ifndef SO_REUSEPORT
|
||||
#define SO_REUSEPORT 15
|
||||
#endif /* SO_REUSEPORT */
|
||||
|
||||
// Define IOV_MAX if not found to limit the iov size on writev calls
|
||||
// See https://github.com/netty/netty/issues/2647
|
||||
#ifndef IOV_MAX
|
||||
#define IOV_MAX 1024
|
||||
#endif /* IOV_MAX */
|
||||
|
||||
// Define UIO_MAXIOV if not found
|
||||
#ifndef UIO_MAXIOV
|
||||
#define UIO_MAXIOV 1024
|
||||
#endif /* UIO_MAXIOV */
|
||||
|
||||
// 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
|
||||
#include "netty_unix_limits.h"
|
||||
#include "netty_epoll_linuxsocket.h"
|
||||
|
||||
// TCP_FASTOPEN is defined in linux 3.7. We define this here so older kernels can compile.
|
||||
#ifndef TCP_FASTOPEN
|
||||
@ -104,49 +84,9 @@ jfieldID packetPortFieldId = NULL;
|
||||
jfieldID packetMemoryAddressFieldId = NULL;
|
||||
jfieldID packetCountFieldId = NULL;
|
||||
|
||||
clockid_t epollWaitClock = 0; // initialized in initializeEpollWaitClock
|
||||
clockid_t waitClockId = 0; // initialized by netty_unix_util_initialize_wait_clock
|
||||
|
||||
// util methods
|
||||
static jboolean initializeEpollWaitClock() {
|
||||
struct timespec ts;
|
||||
// First try to get a monotonic clock, as we effectively measure execution time and don't want the underlying clock
|
||||
// moving unexpectedly/abruptly.
|
||||
#ifdef CLOCK_MONOTONIC_COARSE
|
||||
epollWaitClock = CLOCK_MONOTONIC_COARSE;
|
||||
if (clock_gettime(epollWaitClock, &ts) != EINVAL) {
|
||||
return JNI_TRUE;
|
||||
}
|
||||
#endif
|
||||
#ifdef CLOCK_MONOTONIC_RAW
|
||||
epollWaitClock = CLOCK_MONOTONIC_RAW;
|
||||
if (clock_gettime(epollWaitClock, &ts) != EINVAL) {
|
||||
return JNI_TRUE;
|
||||
}
|
||||
#endif
|
||||
#ifdef CLOCK_MONOTONIC
|
||||
epollWaitClock = CLOCK_MONOTONIC;
|
||||
if (clock_gettime(epollWaitClock, &ts) != EINVAL) {
|
||||
return JNI_TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Fallback to realtime ... in this case we are subject to clock movements on the system.
|
||||
#ifdef CLOCK_REALTIME_COARSE
|
||||
epollWaitClock = CLOCK_REALTIME_COARSE;
|
||||
if (clock_gettime(epollWaitClock, &ts) != EINVAL) {
|
||||
return JNI_TRUE;
|
||||
}
|
||||
#endif
|
||||
#ifdef CLOCK_REALTIME
|
||||
epollWaitClock = CLOCK_REALTIME;
|
||||
if (clock_gettime(epollWaitClock, &ts) != EINVAL) {
|
||||
return JNI_TRUE;
|
||||
}
|
||||
#endif
|
||||
fprintf(stderr, "FATAL: could not find a clock for clock_gettime!\n");
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
static int getSysctlValue(const char * property, int* returnValue) {
|
||||
int rc = -1;
|
||||
FILE *fd=fopen(property, "r");
|
||||
@ -238,7 +178,7 @@ static jint netty_epoll_native_epollWait0(JNIEnv* env, jclass clazz, jint efd, j
|
||||
timeout = MAX_EPOLL_TIMEOUT_MSEC;
|
||||
}
|
||||
|
||||
clock_gettime(epollWaitClock, &ts);
|
||||
clock_gettime(waitClockId, &ts);
|
||||
timeBeforeWait = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
|
||||
|
||||
for (;;) {
|
||||
@ -248,7 +188,7 @@ static jint netty_epoll_native_epollWait0(JNIEnv* env, jclass clazz, jint efd, j
|
||||
}
|
||||
if ((err = errno) == EINTR) {
|
||||
if (timeout > 0) {
|
||||
clock_gettime(epollWaitClock, &ts);
|
||||
clock_gettime(waitClockId, &ts);
|
||||
timeNow = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
|
||||
timeDiff = timeNow - timeBeforeWait;
|
||||
timeout -= timeDiff;
|
||||
@ -293,6 +233,7 @@ static jint netty_epoll_native_epollCtlDel0(JNIEnv* env, jclass clazz, jint efd,
|
||||
static jint netty_epoll_native_sendmmsg0(JNIEnv* env, jclass clazz, jint fd, jobjectArray packets, jint offset, jint len) {
|
||||
struct mmsghdr msg[len];
|
||||
struct sockaddr_storage addr[len];
|
||||
socklen_t addrSize;
|
||||
int i;
|
||||
|
||||
memset(msg, 0, sizeof(msg));
|
||||
@ -304,12 +245,12 @@ static jint netty_epoll_native_sendmmsg0(JNIEnv* env, jclass clazz, jint fd, job
|
||||
jint scopeId = (*env)->GetIntField(env, packet, packetScopeIdFieldId);
|
||||
jint port = (*env)->GetIntField(env, packet, packetPortFieldId);
|
||||
|
||||
if (netty_unix_socket_initSockaddr(env, address, scopeId, port, &addr[i]) == -1) {
|
||||
if (netty_unix_socket_initSockaddr(env, address, scopeId, port, &addr[i], &addrSize) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
msg[i].msg_hdr.msg_name = &addr[i];
|
||||
msg[i].msg_hdr.msg_namelen = sizeof(addr[i]);
|
||||
msg[i].msg_hdr.msg_namelen = addrSize;
|
||||
|
||||
msg[i].msg_hdr.msg_iov = (struct iovec*) (intptr_t) (*env)->GetLongField(env, packet, packetMemoryAddressFieldId);
|
||||
msg[i].msg_hdr.msg_iovlen = (*env)->GetIntField(env, packet, packetCountFieldId);;
|
||||
@ -327,89 +268,6 @@ static jint netty_epoll_native_sendmmsg0(JNIEnv* env, jclass clazz, jint fd, job
|
||||
}
|
||||
return (jint) res;
|
||||
}
|
||||
static jint netty_epoll_native_recvFd0(JNIEnv* env, jclass clazz, jint fd) {
|
||||
int socketFd;
|
||||
struct msghdr descriptorMessage = { 0 };
|
||||
struct iovec iov[1] = { 0 };
|
||||
char control[CMSG_SPACE(sizeof(int))] = { 0 };
|
||||
char iovecData[1];
|
||||
|
||||
descriptorMessage.msg_control = control;
|
||||
descriptorMessage.msg_controllen = sizeof(control);
|
||||
descriptorMessage.msg_iov = iov;
|
||||
descriptorMessage.msg_iovlen = 1;
|
||||
iov[0].iov_base = iovecData;
|
||||
iov[0].iov_len = sizeof(iovecData);
|
||||
|
||||
ssize_t res;
|
||||
int err;
|
||||
|
||||
for (;;) {
|
||||
do {
|
||||
res = recvmsg(fd, &descriptorMessage, 0);
|
||||
// Keep on reading if we was interrupted
|
||||
} while (res == -1 && ((err = errno) == EINTR));
|
||||
|
||||
if (res == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (res < 0) {
|
||||
return -err;
|
||||
}
|
||||
|
||||
struct cmsghdr* cmsg = CMSG_FIRSTHDR(&descriptorMessage);
|
||||
if (!cmsg) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if ((cmsg->cmsg_len == CMSG_LEN(sizeof(int))) && (cmsg->cmsg_level == SOL_SOCKET) && (cmsg->cmsg_type == SCM_RIGHTS)) {
|
||||
socketFd = *((int *) CMSG_DATA(cmsg));
|
||||
// set as non blocking as we want to use it with epoll
|
||||
if (fcntl(socketFd, F_SETFL, O_NONBLOCK) == -1) {
|
||||
err = errno;
|
||||
close(socketFd);
|
||||
return -err;
|
||||
}
|
||||
return socketFd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static jint netty_epoll_native_sendFd0(JNIEnv* env, jclass clazz, jint socketFd, jint fd) {
|
||||
struct msghdr descriptorMessage = { 0 };
|
||||
struct iovec iov[1] = { 0 };
|
||||
char control[CMSG_SPACE(sizeof(int))] = { 0 };
|
||||
char iovecData[1];
|
||||
|
||||
descriptorMessage.msg_control = control;
|
||||
descriptorMessage.msg_controllen = sizeof(control);
|
||||
struct cmsghdr* cmsg = CMSG_FIRSTHDR(&descriptorMessage);
|
||||
|
||||
if (cmsg) {
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
*((int *)CMSG_DATA(cmsg)) = fd;
|
||||
descriptorMessage.msg_iov = iov;
|
||||
descriptorMessage.msg_iovlen = 1;
|
||||
iov[0].iov_base = iovecData;
|
||||
iov[0].iov_len = sizeof(iovecData);
|
||||
|
||||
ssize_t res;
|
||||
int err;
|
||||
do {
|
||||
res = sendmsg(socketFd, &descriptorMessage, 0);
|
||||
// keep on writing if it was interrupted
|
||||
} while (res == -1 && ((err = errno) == EINTR));
|
||||
|
||||
if (res < 0) {
|
||||
return -err;
|
||||
}
|
||||
return (jint) res;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static jlong netty_epoll_native_sendfile0(JNIEnv* env, jclass clazz, jint fd, jobject fileRegion, jlong base_off, jlong off, jlong len) {
|
||||
jobject fileChannel = (*env)->GetObjectField(env, fileRegion, fileChannelFieldId);
|
||||
@ -443,177 +301,6 @@ static jlong netty_epoll_native_sendfile0(JNIEnv* env, jclass clazz, jint fd, jo
|
||||
|
||||
return res;
|
||||
}
|
||||
static void netty_epoll_native_setReuseAddress(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
||||
netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
|
||||
}
|
||||
|
||||
static void netty_epoll_native_setReusePort(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
||||
netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));
|
||||
}
|
||||
|
||||
static void netty_epoll_native_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_native_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_native_setTrafficClass(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
||||
netty_unix_socket_setOption(env, fd, IPPROTO_IP, IP_TOS, &optval, sizeof(optval));
|
||||
|
||||
/* Try to set the ipv6 equivalent, but don't throw if this is an ipv4 only socket. */
|
||||
int rc = setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &optval, sizeof(optval));
|
||||
if (rc < 0 && errno != ENOPROTOOPT) {
|
||||
netty_unix_errors_throwChannelExceptionErrorNo(env, "setting ipv6 dscp failed: ", errno);
|
||||
}
|
||||
}
|
||||
|
||||
static void netty_epoll_native_setBroadcast(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
||||
netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval));
|
||||
}
|
||||
|
||||
static void netty_epoll_native_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_native_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_native_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_native_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_native_setIpFreeBind(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
||||
netty_unix_socket_setOption(env, fd, IPPROTO_IP, IP_FREEBIND, &optval, sizeof(optval));
|
||||
}
|
||||
|
||||
static jint netty_epoll_native_isReuseAddress(JNIEnv* env, jclass clazz, jint fd) {
|
||||
int optval;
|
||||
if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return optval;
|
||||
}
|
||||
|
||||
static jint netty_epoll_native_isReusePort(JNIEnv* env, jclass clazz, jint fd) {
|
||||
int optval;
|
||||
if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return optval;
|
||||
}
|
||||
|
||||
static jint netty_epoll_native_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 jint netty_epoll_native_getTrafficClass(JNIEnv* env, jclass clazz, jint fd) {
|
||||
int optval;
|
||||
if (netty_unix_socket_getOption(env, fd, IPPROTO_IP, IP_TOS, &optval, sizeof(optval)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return optval;
|
||||
}
|
||||
|
||||
static jint netty_epoll_native_isBroadcast(JNIEnv* env, jclass clazz, jint fd) {
|
||||
int optval;
|
||||
if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return optval;
|
||||
}
|
||||
|
||||
static jint netty_epoll_native_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_native_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_native_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_native_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_Native_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 void netty_epoll_native_tcpInfo0(JNIEnv* env, jclass clazz, jint fd, jintArray array) {
|
||||
struct tcp_info tcp_info;
|
||||
if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_INFO, &tcp_info, sizeof(tcp_info)) == -1) {
|
||||
return;
|
||||
}
|
||||
unsigned int cArray[32];
|
||||
cArray[0] = tcp_info.tcpi_state;
|
||||
cArray[1] = tcp_info.tcpi_ca_state;
|
||||
cArray[2] = tcp_info.tcpi_retransmits;
|
||||
cArray[3] = tcp_info.tcpi_probes;
|
||||
cArray[4] = tcp_info.tcpi_backoff;
|
||||
cArray[5] = tcp_info.tcpi_options;
|
||||
cArray[6] = tcp_info.tcpi_snd_wscale;
|
||||
cArray[7] = tcp_info.tcpi_rcv_wscale;
|
||||
cArray[8] = tcp_info.tcpi_rto;
|
||||
cArray[9] = tcp_info.tcpi_ato;
|
||||
cArray[10] = tcp_info.tcpi_snd_mss;
|
||||
cArray[11] = tcp_info.tcpi_rcv_mss;
|
||||
cArray[12] = tcp_info.tcpi_unacked;
|
||||
cArray[13] = tcp_info.tcpi_sacked;
|
||||
cArray[14] = tcp_info.tcpi_lost;
|
||||
cArray[15] = tcp_info.tcpi_retrans;
|
||||
cArray[16] = tcp_info.tcpi_fackets;
|
||||
cArray[17] = tcp_info.tcpi_last_data_sent;
|
||||
cArray[18] = tcp_info.tcpi_last_ack_sent;
|
||||
cArray[19] = tcp_info.tcpi_last_data_recv;
|
||||
cArray[20] = tcp_info.tcpi_last_ack_recv;
|
||||
cArray[21] = tcp_info.tcpi_pmtu;
|
||||
cArray[22] = tcp_info.tcpi_rcv_ssthresh;
|
||||
cArray[23] = tcp_info.tcpi_rtt;
|
||||
cArray[24] = tcp_info.tcpi_rttvar;
|
||||
cArray[25] = tcp_info.tcpi_snd_ssthresh;
|
||||
cArray[26] = tcp_info.tcpi_snd_cwnd;
|
||||
cArray[27] = tcp_info.tcpi_advmss;
|
||||
cArray[28] = tcp_info.tcpi_reordering;
|
||||
cArray[29] = tcp_info.tcpi_rcv_rtt;
|
||||
cArray[30] = tcp_info.tcpi_rcv_space;
|
||||
cArray[31] = tcp_info.tcpi_total_retrans;
|
||||
|
||||
(*env)->SetIntArrayRegion(env, array, 0, 32, cArray);
|
||||
}
|
||||
|
||||
static jstring netty_epoll_native_kernelVersion(JNIEnv* env, jclass clazz) {
|
||||
struct utsname name;
|
||||
@ -627,14 +314,6 @@ static jstring netty_epoll_native_kernelVersion(JNIEnv* env, jclass clazz) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static jint netty_epoll_native_iovMax(JNIEnv* env, jclass clazz) {
|
||||
return IOV_MAX;
|
||||
}
|
||||
|
||||
static jint netty_epoll_native_uioMaxIov(JNIEnv* env, jclass clazz) {
|
||||
return UIO_MAXIOV;
|
||||
}
|
||||
|
||||
static jboolean netty_epoll_native_isSupportingSendmmsg(JNIEnv* env, jclass clazz) {
|
||||
if (sendmmsg) {
|
||||
return JNI_TRUE;
|
||||
@ -699,10 +378,6 @@ static jint netty_epoll_native_splice0(JNIEnv* env, jclass clazz, jint fd, jlong
|
||||
return (jint) res;
|
||||
}
|
||||
|
||||
static jlong netty_epoll_native_ssizeMax(JNIEnv* env, jclass clazz) {
|
||||
return SSIZE_MAX;
|
||||
}
|
||||
|
||||
static jint netty_epoll_native_tcpMd5SigMaxKeyLen(JNIEnv* env, jclass clazz) {
|
||||
struct tcp_md5sig md5sig;
|
||||
|
||||
@ -713,43 +388,6 @@ static jint netty_epoll_native_tcpMd5SigMaxKeyLen(JNIEnv* env, jclass clazz) {
|
||||
|
||||
return TCP_MD5SIG_MAXKEYLEN;
|
||||
}
|
||||
|
||||
static void netty_epoll_native_setTcpMd5Sig0(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jbyteArray key) {
|
||||
struct sockaddr_storage addr;
|
||||
if (netty_unix_socket_initSockaddr(env, address, scopeId, 0, &addr) == -1) {
|
||||
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_throwChannelExceptionErrorNo(env, "setsockopt() failed: ", errno);
|
||||
}
|
||||
}
|
||||
// JNI Registered Methods End
|
||||
|
||||
// JNI Method Registration Table Begin
|
||||
@ -759,10 +397,7 @@ static const JNINativeMethod statically_referenced_fixed_method_table[] = {
|
||||
{ "epollout", "()I", (void *) netty_epoll_native_epollout },
|
||||
{ "epollrdhup", "()I", (void *) netty_epoll_native_epollrdhup },
|
||||
{ "epollerr", "()I", (void *) netty_epoll_native_epollerr },
|
||||
{ "ssizeMax", "()J", (void *) netty_epoll_native_ssizeMax },
|
||||
{ "tcpMd5SigMaxKeyLen", "()I", (void *) netty_epoll_native_tcpMd5SigMaxKeyLen },
|
||||
{ "iovMax", "()I", (void *) netty_epoll_native_iovMax },
|
||||
{ "uioMaxIov", "()I", (void *) netty_epoll_native_uioMaxIov },
|
||||
{ "isSupportingSendmmsg", "()Z", (void *) netty_epoll_native_isSupportingSendmmsg },
|
||||
{ "isSupportingTcpFastopen", "()Z", (void *) netty_epoll_native_isSupportingTcpFastopen },
|
||||
{ "kernelVersion", "()Ljava/lang/String;", (void *) netty_epoll_native_kernelVersion }
|
||||
@ -778,35 +413,10 @@ static const JNINativeMethod fixed_method_table[] = {
|
||||
{ "epollCtlMod0", "(III)I", (void *) netty_epoll_native_epollCtlMod0 },
|
||||
{ "epollCtlDel0", "(II)I", (void *) netty_epoll_native_epollCtlDel0 },
|
||||
// "sendmmsg0" has a dynamic signature
|
||||
{ "recvFd0", "(I)I", (void *) netty_epoll_native_recvFd0 },
|
||||
{ "sendFd0", "(II)I", (void *) netty_epoll_native_sendFd0 },
|
||||
// "sendFile0" has a dynamic signature
|
||||
{ "setReuseAddress", "(II)V", (void *) netty_epoll_native_setReuseAddress },
|
||||
{ "setReusePort", "(II)V", (void *) netty_epoll_native_setReusePort },
|
||||
{ "setTcpFastopen", "(II)V", (void *) netty_epoll_native_setTcpFastopen },
|
||||
{ "setTcpNotSentLowAt", "(II)V", (void *) netty_epoll_native_setTcpNotSentLowAt },
|
||||
{ "setTrafficClass", "(II)V", (void *) netty_epoll_native_setTrafficClass },
|
||||
{ "setBroadcast", "(II)V", (void *) netty_epoll_native_setBroadcast },
|
||||
{ "setTcpKeepIdle", "(II)V", (void *) netty_epoll_native_setTcpKeepIdle },
|
||||
{ "setTcpKeepIntvl", "(II)V", (void *) netty_epoll_native_setTcpKeepIntvl },
|
||||
{ "setTcpKeepCnt", "(II)V", (void *) netty_epoll_native_setTcpKeepCnt },
|
||||
{ "setTcpUserTimeout", "(II)V", (void *) netty_epoll_native_setTcpUserTimeout },
|
||||
{ "setIpFreeBind", "(II)V", (void *) netty_epoll_native_setIpFreeBind },
|
||||
{ "isReuseAddress", "(I)I", (void *) netty_epoll_native_isReuseAddress },
|
||||
{ "isReusePort", "(I)I", (void *) netty_epoll_native_isReusePort },
|
||||
{ "getTcpNotSentLowAt", "(I)I", (void *) netty_epoll_native_getTcpNotSentLowAt },
|
||||
{ "getTrafficClass", "(I)I", (void *) netty_epoll_native_getTrafficClass },
|
||||
{ "isBroadcast", "(I)I", (void *) netty_epoll_native_isBroadcast },
|
||||
{ "getTcpKeepIdle", "(I)I", (void *) netty_epoll_native_getTcpKeepIdle },
|
||||
{ "getTcpKeepIntvl", "(I)I", (void *) netty_epoll_native_getTcpKeepIntvl },
|
||||
{ "getTcpKeepCnt", "(I)I", (void *) netty_epoll_native_getTcpKeepCnt },
|
||||
{ "getTcpUserTimeout", "(I)I", (void *) netty_epoll_native_getTcpUserTimeout },
|
||||
{ "isIpFreeBind", "(I)I", (void *) netty_epoll_Native_isIpFreeBind },
|
||||
{ "tcpInfo0", "(I[I)V", (void *) netty_epoll_native_tcpInfo0 },
|
||||
{ "sizeofEpollEvent", "()I", (void *) netty_epoll_native_sizeofEpollEvent },
|
||||
{ "offsetofEpollData", "()I", (void *) netty_epoll_native_offsetofEpollData },
|
||||
{ "splice0", "(IJIJJ)I", (void *) netty_epoll_native_splice0 },
|
||||
{ "setTcpMd5Sig0", "(I[BI[B)V", (void *) netty_epoll_native_setTcpMd5Sig0 }
|
||||
{ "splice0", "(IJIJJ)I", (void *) netty_epoll_native_splice0 }
|
||||
};
|
||||
static const jint fixed_method_table_size = sizeof(fixed_method_table) / sizeof(fixed_method_table[0]);
|
||||
|
||||
@ -823,6 +433,7 @@ static JNINativeMethod* createDynamicMethodsTable(const char* packagePrefix) {
|
||||
dynamicMethod->signature = netty_unix_util_prepend("(I[L", dynamicTypeName);
|
||||
dynamicMethod->fnPtr = (void *) netty_epoll_native_sendmmsg0;
|
||||
free(dynamicTypeName);
|
||||
|
||||
++dynamicMethod;
|
||||
dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/DefaultFileRegion;JJJ)J");
|
||||
dynamicMethod->name = "sendfile0";
|
||||
@ -864,6 +475,9 @@ static jint netty_epoll_native_JNI_OnLoad(JNIEnv* env, const char* packagePrefix
|
||||
freeDynamicMethodsTable(dynamicMethods);
|
||||
dynamicMethods = NULL;
|
||||
// Load all c modules that we depend upon
|
||||
if (netty_unix_limits_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
|
||||
return JNI_ERR;
|
||||
}
|
||||
if (netty_unix_errors_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
|
||||
return JNI_ERR;
|
||||
}
|
||||
@ -873,6 +487,10 @@ static jint netty_epoll_native_JNI_OnLoad(JNIEnv* env, const char* packagePrefix
|
||||
if (netty_unix_socket_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
|
||||
return JNI_ERR;
|
||||
}
|
||||
if (netty_epoll_linuxsocket_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
// Initialize this module
|
||||
char* nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/DefaultFileRegion");
|
||||
jclass fileRegionCls = (*env)->FindClass(env, nettyClassName);
|
||||
@ -950,7 +568,8 @@ static jint netty_epoll_native_JNI_OnLoad(JNIEnv* env, const char* packagePrefix
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
if (!initializeEpollWaitClock()) {
|
||||
if (!netty_unix_util_initialize_wait_clock(&waitClockId)) {
|
||||
fprintf(stderr, "FATAL: could not find a clock for clock_gettime!\n");
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
@ -958,49 +577,11 @@ static jint netty_epoll_native_JNI_OnLoad(JNIEnv* env, const char* packagePrefix
|
||||
}
|
||||
|
||||
static void netty_epoll_native_JNI_OnUnLoad(JNIEnv* env) {
|
||||
netty_unix_limits_JNI_OnUnLoad(env);
|
||||
netty_unix_errors_JNI_OnUnLoad(env);
|
||||
netty_unix_filedescriptor_JNI_OnUnLoad(env);
|
||||
netty_unix_socket_JNI_OnUnLoad(env);
|
||||
}
|
||||
|
||||
/**
|
||||
* The expected format of the library name is "lib<>netty-transport-native-epoll" where the <> portion is what we will return.
|
||||
*/
|
||||
static char* parsePackagePrefix(const char* libraryPathName, jint* status) {
|
||||
char* packageNameEnd = strstr(libraryPathName, "netty-transport-native-epoll");
|
||||
if (packageNameEnd == NULL) {
|
||||
*status = JNI_ERR;
|
||||
return NULL;
|
||||
}
|
||||
char* packagePrefix = netty_unix_util_rstrstr(packageNameEnd, libraryPathName, "lib");
|
||||
if (packagePrefix == NULL) {
|
||||
*status = JNI_ERR;
|
||||
return NULL;
|
||||
}
|
||||
packagePrefix += 3;
|
||||
if (packagePrefix == packageNameEnd) {
|
||||
return NULL;
|
||||
}
|
||||
// packagePrefix length is > 0
|
||||
// Make a copy so we can modify the value without impacting libraryPathName.
|
||||
size_t packagePrefixLen = packageNameEnd - packagePrefix;
|
||||
packagePrefix = strndup(packagePrefix, packagePrefixLen);
|
||||
// Make sure the packagePrefix is in the correct format for the JNI functions it will be used with.
|
||||
char* temp = packagePrefix;
|
||||
packageNameEnd = packagePrefix + packagePrefixLen;
|
||||
// Package names must be sanitized, in JNI packages names are separated by '/' characters.
|
||||
for (; temp != packageNameEnd; ++temp) {
|
||||
if (*temp == '-') {
|
||||
*temp = '/';
|
||||
}
|
||||
}
|
||||
// Make sure packagePrefix is terminated with the '/' JNI package separator.
|
||||
if(*(--temp) != '/') {
|
||||
temp = packagePrefix;
|
||||
packagePrefix = netty_unix_util_prepend(packagePrefix, "/");
|
||||
free(temp);
|
||||
}
|
||||
return packagePrefix;
|
||||
netty_epoll_linuxsocket_JNI_OnUnLoad(env);
|
||||
}
|
||||
|
||||
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||
@ -1013,11 +594,11 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||
jint status = 0;
|
||||
// We need to use an address of a function that is uniquely part of this library, so choose a static
|
||||
// function. See https://github.com/netty/netty/issues/4840.
|
||||
if (!dladdr((void*) parsePackagePrefix, &dlinfo)) {
|
||||
if (!dladdr((void*) netty_epoll_native_JNI_OnUnLoad, &dlinfo)) {
|
||||
fprintf(stderr, "FATAL: transport-native-epoll JNI call to dladdr failed!\n");
|
||||
return JNI_ERR;
|
||||
}
|
||||
char* packagePrefix = parsePackagePrefix(dlinfo.dli_fname, &status);
|
||||
char* packagePrefix = netty_unix_util_parse_package_prefix(dlinfo.dli_fname, "netty-transport-native-epoll", &status);
|
||||
if (status == JNI_ERR) {
|
||||
fprintf(stderr, "FATAL: transport-native-epoll JNI encountered unexpected dlinfo.dli_fname: %s\n", dlinfo.dli_fname);
|
||||
return JNI_ERR;
|
||||
|
@ -1,55 +0,0 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "netty_unix_util.h"
|
||||
|
||||
char* netty_unix_util_prepend(const char* prefix, const char* str) {
|
||||
if (prefix == NULL) {
|
||||
char* result = (char*) malloc(sizeof(char) * (strlen(str) + 1));
|
||||
strcpy(result, str);
|
||||
return result;
|
||||
}
|
||||
char* result = (char*) malloc(sizeof(char) * (strlen(prefix) + strlen(str) + 1));
|
||||
strcpy(result, prefix);
|
||||
strcat(result, str);
|
||||
return result;
|
||||
}
|
||||
|
||||
char* netty_unix_util_rstrstr(char* s1rbegin, const char* s1rend, const char* s2) {
|
||||
size_t s2len = strlen(s2);
|
||||
char *s = s1rbegin - s2len;
|
||||
|
||||
for (; s >= s1rend; --s) {
|
||||
if (strncmp(s, s2, s2len) == 0) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jint netty_unix_util_register_natives(JNIEnv* env, const char* packagePrefix, const char* className, const JNINativeMethod* methods, jint numMethods) {
|
||||
char* nettyClassName = netty_unix_util_prepend(packagePrefix, className);
|
||||
jclass nativeCls = (*env)->FindClass(env, nettyClassName);
|
||||
free(nettyClassName);
|
||||
nettyClassName = NULL;
|
||||
if (nativeCls == NULL) {
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
return (*env)->RegisterNatives(env, nativeCls, methods, numMethods);
|
||||
}
|
@ -28,6 +28,7 @@ import io.netty.channel.EventLoop;
|
||||
import io.netty.channel.RecvByteBufAllocator;
|
||||
import io.netty.channel.socket.ChannelInputShutdownEvent;
|
||||
import io.netty.channel.socket.ChannelInputShutdownReadComplete;
|
||||
import io.netty.channel.unix.FileDescriptor;
|
||||
import io.netty.channel.unix.Socket;
|
||||
import io.netty.channel.unix.UnixChannel;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
@ -43,20 +44,20 @@ import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
abstract class AbstractEpollChannel extends AbstractChannel implements UnixChannel {
|
||||
private static final ChannelMetadata METADATA = new ChannelMetadata(false);
|
||||
private final int readFlag;
|
||||
private final Socket fileDescriptor;
|
||||
final LinuxSocket socket;
|
||||
protected int flags = Native.EPOLLET;
|
||||
boolean inputClosedSeenErrorOnRead;
|
||||
boolean epollInReadyRunnablePending;
|
||||
|
||||
protected volatile boolean active;
|
||||
|
||||
AbstractEpollChannel(Socket fd, int flag) {
|
||||
AbstractEpollChannel(LinuxSocket fd, int flag) {
|
||||
this(null, fd, flag, false);
|
||||
}
|
||||
|
||||
AbstractEpollChannel(Channel parent, Socket fd, int flag, boolean active) {
|
||||
AbstractEpollChannel(Channel parent, LinuxSocket fd, int flag, boolean active) {
|
||||
super(parent);
|
||||
fileDescriptor = checkNotNull(fd, "fd");
|
||||
socket = checkNotNull(fd, "fd");
|
||||
readFlag = flag;
|
||||
flags |= flag;
|
||||
this.active = active;
|
||||
@ -89,8 +90,8 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Socket fd() {
|
||||
return fileDescriptor;
|
||||
public final FileDescriptor fd() {
|
||||
return socket;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -115,7 +116,7 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
|
||||
try {
|
||||
doDeregister();
|
||||
} finally {
|
||||
fileDescriptor.close();
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,7 +132,7 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return fileDescriptor.isOpen();
|
||||
return socket.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -158,7 +159,7 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
|
||||
}
|
||||
|
||||
final boolean shouldBreakEpollInReady(ChannelConfig config) {
|
||||
return fileDescriptor.isInputShutdown() && (inputClosedSeenErrorOnRead || !isAllowHalfClosure(config));
|
||||
return socket.isInputShutdown() && (inputClosedSeenErrorOnRead || !isAllowHalfClosure(config));
|
||||
}
|
||||
|
||||
final boolean isAllowHalfClosure(ChannelConfig config) {
|
||||
@ -265,10 +266,10 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
|
||||
int localReadAmount;
|
||||
unsafe().recvBufAllocHandle().attemptedBytesRead(byteBuf.writableBytes());
|
||||
if (byteBuf.hasMemoryAddress()) {
|
||||
localReadAmount = fileDescriptor.readAddress(byteBuf.memoryAddress(), writerIndex, byteBuf.capacity());
|
||||
localReadAmount = socket.readAddress(byteBuf.memoryAddress(), writerIndex, byteBuf.capacity());
|
||||
} else {
|
||||
ByteBuffer buf = byteBuf.internalNioBuffer(writerIndex, byteBuf.writableBytes());
|
||||
localReadAmount = fileDescriptor.read(buf, buf.position(), buf.limit());
|
||||
localReadAmount = socket.read(buf, buf.position(), buf.limit());
|
||||
}
|
||||
if (localReadAmount > 0) {
|
||||
byteBuf.writerIndex(writerIndex + localReadAmount);
|
||||
@ -283,8 +284,8 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
|
||||
long memoryAddress = buf.memoryAddress();
|
||||
int readerIndex = buf.readerIndex();
|
||||
int writerIndex = buf.writerIndex();
|
||||
for (int i = writeSpinCount - 1; i >= 0; i--) {
|
||||
int localFlushedAmount = fileDescriptor.writeAddress(memoryAddress, readerIndex, writerIndex);
|
||||
for (int i = writeSpinCount; i > 0; --i) {
|
||||
int localFlushedAmount = socket.writeAddress(memoryAddress, readerIndex, writerIndex);
|
||||
if (localFlushedAmount > 0) {
|
||||
writtenBytes += localFlushedAmount;
|
||||
if (writtenBytes == readableBytes) {
|
||||
@ -302,10 +303,10 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
|
||||
} else {
|
||||
nioBuf = buf.nioBuffer();
|
||||
}
|
||||
for (int i = writeSpinCount - 1; i >= 0; i--) {
|
||||
for (int i = writeSpinCount; i > 0; --i) {
|
||||
int pos = nioBuf.position();
|
||||
int limit = nioBuf.limit();
|
||||
int localFlushedAmount = fileDescriptor.write(nioBuf, pos, limit);
|
||||
int localFlushedAmount = socket.write(nioBuf, pos, limit);
|
||||
if (localFlushedAmount > 0) {
|
||||
nioBuf.position(pos + localFlushedAmount);
|
||||
writtenBytes += localFlushedAmount;
|
||||
@ -410,10 +411,10 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
|
||||
* Shutdown the input side of the channel.
|
||||
*/
|
||||
void shutdownInput(boolean rdHup) {
|
||||
if (!fd().isInputShutdown()) {
|
||||
if (!socket.isInputShutdown()) {
|
||||
if (isAllowHalfClosure(config())) {
|
||||
try {
|
||||
fd().shutdown(true, false);
|
||||
socket.shutdown(true, false);
|
||||
} catch (IOException ignored) {
|
||||
// We attempted to shutdown and failed, which means the input has already effectively been
|
||||
// shutdown.
|
||||
@ -422,9 +423,8 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
|
||||
} catch (NotYetConnectedException ignore) {
|
||||
// We attempted to shutdown and failed, which means the input has already effectively been
|
||||
// shutdown.
|
||||
fireEventAndClose(ChannelInputShutdownEvent.INSTANCE);
|
||||
return;
|
||||
}
|
||||
clearEpollIn();
|
||||
pipeline().fireUserEventTriggered(ChannelInputShutdownEvent.INSTANCE);
|
||||
} else {
|
||||
close(voidPromise());
|
||||
@ -471,7 +471,7 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
|
||||
* Called once a EPOLLOUT event is ready to be processed
|
||||
*/
|
||||
void epollOutReady() {
|
||||
if (fd().isOutputShutdown()) {
|
||||
if (socket.isOutputShutdown()) {
|
||||
return;
|
||||
}
|
||||
// directly call super.flush0() to force a flush now
|
||||
|
@ -23,8 +23,6 @@ import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import io.netty.channel.EventLoop;
|
||||
import io.netty.channel.ServerChannel;
|
||||
import io.netty.channel.unix.FileDescriptor;
|
||||
import io.netty.channel.unix.Socket;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
@ -32,31 +30,15 @@ import java.net.SocketAddress;
|
||||
public abstract class AbstractEpollServerChannel extends AbstractEpollChannel implements ServerChannel {
|
||||
private static final ChannelMetadata METADATA = new ChannelMetadata(false, 16);
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #AbstractEpollServerChannel(Socket, boolean)}.
|
||||
*/
|
||||
@Deprecated
|
||||
protected AbstractEpollServerChannel(int fd) {
|
||||
this(new Socket(fd), false);
|
||||
this(new LinuxSocket(fd), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #AbstractEpollServerChannel(Socket, boolean)}.
|
||||
*/
|
||||
@Deprecated
|
||||
protected AbstractEpollServerChannel(FileDescriptor fd) {
|
||||
this(new Socket(fd.intValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #AbstractEpollServerChannel(Socket, boolean)}.
|
||||
*/
|
||||
@Deprecated
|
||||
protected AbstractEpollServerChannel(Socket fd) {
|
||||
AbstractEpollServerChannel(LinuxSocket fd) {
|
||||
this(fd, isSoErrorZero(fd));
|
||||
}
|
||||
|
||||
protected AbstractEpollServerChannel(Socket fd, boolean active) {
|
||||
AbstractEpollServerChannel(LinuxSocket fd, boolean active) {
|
||||
super(null, fd, Native.EPOLLIN, active);
|
||||
}
|
||||
|
||||
@ -117,6 +99,7 @@ public abstract class AbstractEpollServerChannel extends AbstractEpollChannel im
|
||||
|
||||
final ChannelPipeline pipeline = pipeline();
|
||||
allocHandle.reset(config);
|
||||
allocHandle.attemptedBytesRead(1);
|
||||
epollInBefore();
|
||||
|
||||
Throwable exception = null;
|
||||
@ -126,16 +109,16 @@ public abstract class AbstractEpollServerChannel extends AbstractEpollChannel im
|
||||
// lastBytesRead represents the fd. We use lastBytesRead because it must be set so that the
|
||||
// EpollRecvByteAllocatorHandle knows if it should try to read again or not when autoRead is
|
||||
// enabled.
|
||||
allocHandle.lastBytesRead(fd().accept(acceptedAddress));
|
||||
allocHandle.lastBytesRead(socket.accept(acceptedAddress));
|
||||
if (allocHandle.lastBytesRead() == -1) {
|
||||
// this means everything was handled for now
|
||||
break;
|
||||
}
|
||||
allocHandle.incMessagesRead(1);
|
||||
|
||||
int len = acceptedAddress[0];
|
||||
readPending = false;
|
||||
pipeline.fireChannelRead(newChildChannel(allocHandle.lastBytesRead(), acceptedAddress, 1, len));
|
||||
pipeline.fireChannelRead(newChildChannel(allocHandle.lastBytesRead(), acceptedAddress, 1,
|
||||
acceptedAddress[0]));
|
||||
} while (allocHandle.continueReading());
|
||||
} catch (Throwable t) {
|
||||
exception = t;
|
||||
|
@ -17,9 +17,7 @@ package io.netty.channel.epoll;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.buffer.CompositeByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelConfig;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
@ -35,7 +33,8 @@ import io.netty.channel.FileRegion;
|
||||
import io.netty.channel.RecvByteBufAllocator;
|
||||
import io.netty.channel.socket.DuplexChannel;
|
||||
import io.netty.channel.unix.FileDescriptor;
|
||||
import io.netty.channel.unix.Socket;
|
||||
import io.netty.channel.unix.IovArray;
|
||||
import io.netty.channel.unix.SocketWritableByteChannel;
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import io.netty.util.internal.ThrowableUtil;
|
||||
@ -54,6 +53,7 @@ import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static io.netty.channel.unix.FileDescriptor.pipe;
|
||||
import static io.netty.channel.unix.Limits.IOV_MAX;
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
|
||||
public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel implements DuplexChannel {
|
||||
@ -89,45 +89,25 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel im
|
||||
|
||||
private WritableByteChannel byteChannel;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #AbstractEpollStreamChannel(Channel, Socket)}.
|
||||
*/
|
||||
@Deprecated
|
||||
protected AbstractEpollStreamChannel(Channel parent, int fd) {
|
||||
this(parent, new Socket(fd));
|
||||
this(parent, new LinuxSocket(fd));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #AbstractEpollStreamChannel(Socket, boolean)}.
|
||||
*/
|
||||
@Deprecated
|
||||
protected AbstractEpollStreamChannel(int fd) {
|
||||
this(new Socket(fd));
|
||||
this(new LinuxSocket(fd));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #AbstractEpollStreamChannel(Socket, boolean)}.
|
||||
*/
|
||||
@Deprecated
|
||||
protected AbstractEpollStreamChannel(FileDescriptor fd) {
|
||||
this(new Socket(fd.intValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #AbstractEpollStreamChannel(Socket, boolean)}.
|
||||
*/
|
||||
@Deprecated
|
||||
protected AbstractEpollStreamChannel(Socket fd) {
|
||||
AbstractEpollStreamChannel(LinuxSocket fd) {
|
||||
this(fd, isSoErrorZero(fd));
|
||||
}
|
||||
|
||||
protected AbstractEpollStreamChannel(Channel parent, Socket fd) {
|
||||
AbstractEpollStreamChannel(Channel parent, LinuxSocket fd) {
|
||||
super(parent, fd, Native.EPOLLIN, true);
|
||||
// Add EPOLLRDHUP so we are notified once the remote peer close the connection.
|
||||
flags |= Native.EPOLLRDHUP;
|
||||
}
|
||||
|
||||
protected AbstractEpollStreamChannel(Socket fd, boolean active) {
|
||||
protected AbstractEpollStreamChannel(LinuxSocket fd, boolean active) {
|
||||
super(null, fd, Native.EPOLLIN, active);
|
||||
// Add EPOLLRDHUP so we are notified once the remote peer close the connection.
|
||||
flags |= Native.EPOLLRDHUP;
|
||||
@ -301,8 +281,8 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel im
|
||||
boolean done = false;
|
||||
int offset = 0;
|
||||
int end = offset + cnt;
|
||||
for (int i = writeSpinCount - 1; i >= 0; i--) {
|
||||
long localWrittenBytes = fd().writevAddresses(array.memoryAddress(offset), cnt);
|
||||
for (int i = writeSpinCount; i > 0; --i) {
|
||||
long localWrittenBytes = socket.writevAddresses(array.memoryAddress(offset), cnt);
|
||||
if (localWrittenBytes == 0) {
|
||||
break;
|
||||
}
|
||||
@ -340,8 +320,8 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel im
|
||||
boolean done = false;
|
||||
int offset = 0;
|
||||
int end = offset + nioBufferCnt;
|
||||
for (int i = writeSpinCount - 1; i >= 0; i--) {
|
||||
long localWrittenBytes = fd().writev(nioBuffers, offset, nioBufferCnt);
|
||||
for (int i = writeSpinCount; i > 0; --i) {
|
||||
long localWrittenBytes = socket.writev(nioBuffers, offset, nioBufferCnt);
|
||||
if (localWrittenBytes == 0) {
|
||||
break;
|
||||
}
|
||||
@ -390,10 +370,10 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel im
|
||||
boolean done = false;
|
||||
long flushedAmount = 0;
|
||||
|
||||
for (int i = writeSpinCount - 1; i >= 0; i--) {
|
||||
for (int i = writeSpinCount; i > 0; --i) {
|
||||
final long offset = region.transferred();
|
||||
final long localFlushedAmount =
|
||||
Native.sendfile(fd().intValue(), region, baseOffset, offset, regionCount - offset);
|
||||
Native.sendfile(socket.intValue(), region, baseOffset, offset, regionCount - offset);
|
||||
if (localFlushedAmount == 0) {
|
||||
break;
|
||||
}
|
||||
@ -426,9 +406,9 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel im
|
||||
long flushedAmount = 0;
|
||||
|
||||
if (byteChannel == null) {
|
||||
byteChannel = new SocketWritableByteChannel();
|
||||
byteChannel = new EpollSocketWritableByteChannel();
|
||||
}
|
||||
for (int i = writeSpinCount - 1; i >= 0; i--) {
|
||||
for (int i = writeSpinCount; i > 0; --i) {
|
||||
final long localFlushedAmount = region.transferTo(byteChannel, region.transferred());
|
||||
if (localFlushedAmount == 0) {
|
||||
break;
|
||||
@ -564,7 +544,7 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel im
|
||||
// Special handling of CompositeByteBuf to reduce memory copies if some of the Components
|
||||
// in the CompositeByteBuf are backed by a memoryAddress.
|
||||
CompositeByteBuf comp = (CompositeByteBuf) buf;
|
||||
if (!comp.isDirect() || comp.nioBufferCount() > Native.IOV_MAX) {
|
||||
if (!comp.isDirect() || comp.nioBufferCount() > IOV_MAX) {
|
||||
// more then 1024 buffers for gathering writes so just do a memory copy.
|
||||
buf = newDirectBuffer(buf);
|
||||
assert buf.hasMemoryAddress();
|
||||
@ -589,7 +569,7 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel im
|
||||
|
||||
private void shutdownOutput0(final ChannelPromise promise) {
|
||||
try {
|
||||
fd().shutdown(false, true);
|
||||
socket.shutdown(false, true);
|
||||
promise.setSuccess();
|
||||
} catch (Throwable cause) {
|
||||
promise.setFailure(cause);
|
||||
@ -598,7 +578,7 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel im
|
||||
|
||||
private void shutdownInput0(final ChannelPromise promise) {
|
||||
try {
|
||||
fd().shutdown(true, false);
|
||||
socket.shutdown(true, false);
|
||||
promise.setSuccess();
|
||||
} catch (Throwable cause) {
|
||||
promise.setFailure(cause);
|
||||
@ -607,7 +587,7 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel im
|
||||
|
||||
private void shutdown0(final ChannelPromise promise) {
|
||||
try {
|
||||
fd().shutdown(true, true);
|
||||
socket.shutdown(true, true);
|
||||
promise.setSuccess();
|
||||
} catch (Throwable cause) {
|
||||
promise.setFailure(cause);
|
||||
@ -616,17 +596,17 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel im
|
||||
|
||||
@Override
|
||||
public boolean isOutputShutdown() {
|
||||
return fd().isOutputShutdown();
|
||||
return socket.isOutputShutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInputShutdown() {
|
||||
return fd().isInputShutdown();
|
||||
return socket.isInputShutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShutdown() {
|
||||
return fd().isShutdown();
|
||||
return socket.isShutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -764,12 +744,12 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel im
|
||||
*/
|
||||
protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
|
||||
if (localAddress != null) {
|
||||
fd().bind(localAddress);
|
||||
socket.bind(localAddress);
|
||||
}
|
||||
|
||||
boolean success = false;
|
||||
try {
|
||||
boolean connected = fd().connect(remoteAddress);
|
||||
boolean connected = socket.connect(remoteAddress);
|
||||
if (!connected) {
|
||||
setFlag(Native.EPOLLOUT);
|
||||
}
|
||||
@ -952,7 +932,7 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel im
|
||||
* Finish the connect
|
||||
*/
|
||||
boolean doFinishConnect() throws Exception {
|
||||
if (fd().finishConnect()) {
|
||||
if (socket.finishConnect()) {
|
||||
clearFlag(Native.EPOLLOUT);
|
||||
return true;
|
||||
} else {
|
||||
@ -1085,7 +1065,7 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel im
|
||||
int splicedIn = 0;
|
||||
for (;;) {
|
||||
// Splicing until there is nothing left to splice.
|
||||
int localSplicedIn = Native.splice(fd().intValue(), -1, pipeOut.intValue(), -1, length);
|
||||
int localSplicedIn = Native.splice(socket.intValue(), -1, pipeOut.intValue(), -1, length);
|
||||
if (localSplicedIn == 0) {
|
||||
break;
|
||||
}
|
||||
@ -1185,7 +1165,7 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel im
|
||||
public boolean spliceOut() throws Exception {
|
||||
assert ch.eventLoop().inEventLoop();
|
||||
try {
|
||||
int splicedOut = Native.splice(ch.pipeIn.intValue(), -1, ch.fd().intValue(), -1, len);
|
||||
int splicedOut = Native.splice(ch.pipeIn.intValue(), -1, ch.socket.intValue(), -1, len);
|
||||
len -= splicedOut;
|
||||
if (len == 0) {
|
||||
if (autoRead) {
|
||||
@ -1257,55 +1237,14 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel im
|
||||
}
|
||||
}
|
||||
|
||||
private final class SocketWritableByteChannel implements WritableByteChannel {
|
||||
|
||||
@Override
|
||||
public int write(ByteBuffer src) throws IOException {
|
||||
final int written;
|
||||
int position = src.position();
|
||||
int limit = src.limit();
|
||||
if (src.isDirect()) {
|
||||
written = fd().write(src, position, src.limit());
|
||||
} else {
|
||||
final int readableBytes = limit - position;
|
||||
ByteBuf buffer = null;
|
||||
try {
|
||||
if (readableBytes == 0) {
|
||||
buffer = Unpooled.EMPTY_BUFFER;
|
||||
} else {
|
||||
final ByteBufAllocator alloc = alloc();
|
||||
if (alloc.isDirectBufferPooled()) {
|
||||
buffer = alloc.directBuffer(readableBytes);
|
||||
} else {
|
||||
buffer = ByteBufUtil.threadLocalDirectBuffer();
|
||||
if (buffer == null) {
|
||||
buffer = Unpooled.directBuffer(readableBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
buffer.writeBytes(src.duplicate());
|
||||
ByteBuffer nioBuffer = buffer.internalNioBuffer(buffer.readerIndex(), readableBytes);
|
||||
written = fd().write(nioBuffer, nioBuffer.position(), nioBuffer.limit());
|
||||
} finally {
|
||||
if (buffer != null) {
|
||||
buffer.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (written > 0) {
|
||||
src.position(position + written);
|
||||
}
|
||||
return written;
|
||||
private final class EpollSocketWritableByteChannel extends SocketWritableByteChannel {
|
||||
EpollSocketWritableByteChannel() {
|
||||
super(socket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return fd().isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
fd().close();
|
||||
protected ByteBufAllocator alloc() {
|
||||
return AbstractEpollStreamChannel.this.alloc();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,6 @@ import io.netty.channel.DefaultChannelConfig;
|
||||
import io.netty.channel.MessageSizeEstimator;
|
||||
import io.netty.channel.RecvByteBufAllocator;
|
||||
import io.netty.channel.WriteBufferWaterMark;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -16,15 +16,13 @@
|
||||
package io.netty.channel.epoll;
|
||||
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.unix.DomainSocketReadMode;
|
||||
|
||||
import io.netty.channel.unix.UnixChannelOption;
|
||||
import java.net.InetAddress;
|
||||
import java.util.Map;
|
||||
|
||||
public final class EpollChannelOption<T> extends ChannelOption<T> {
|
||||
public final class EpollChannelOption<T> extends UnixChannelOption<T> {
|
||||
|
||||
public static final ChannelOption<Boolean> TCP_CORK = valueOf(EpollChannelOption.class, "TCP_CORK");
|
||||
public static final ChannelOption<Boolean> SO_REUSEPORT = valueOf(EpollChannelOption.class, "SO_REUSEPORT");
|
||||
public static final ChannelOption<Long> TCP_NOTSENT_LOWAT = valueOf(EpollChannelOption.class, "TCP_NOTSENT_LOWAT");
|
||||
public static final ChannelOption<Integer> TCP_KEEPIDLE = valueOf(EpollChannelOption.class, "TCP_KEEPIDLE");
|
||||
public static final ChannelOption<Integer> TCP_KEEPINTVL = valueOf(EpollChannelOption.class, "TCP_KEEPINTVL");
|
||||
@ -37,8 +35,6 @@ public final class EpollChannelOption<T> extends ChannelOption<T> {
|
||||
ChannelOption.valueOf(EpollChannelOption.class, "TCP_DEFER_ACCEPT");
|
||||
public static final ChannelOption<Boolean> TCP_QUICKACK = valueOf(EpollChannelOption.class, "TCP_QUICKACK");
|
||||
|
||||
public static final ChannelOption<DomainSocketReadMode> DOMAIN_SOCKET_READ_MODE =
|
||||
ChannelOption.valueOf(EpollChannelOption.class, "DOMAIN_SOCKET_READ_MODE");
|
||||
public static final ChannelOption<EpollMode> EPOLL_MODE =
|
||||
ChannelOption.valueOf(EpollChannelOption.class, "EPOLL_MODE");
|
||||
|
||||
@ -46,6 +42,5 @@ public final class EpollChannelOption<T> extends ChannelOption<T> {
|
||||
|
||||
@SuppressWarnings({ "unused", "deprecation" })
|
||||
private EpollChannelOption() {
|
||||
super(null);
|
||||
}
|
||||
}
|
||||
|
@ -29,8 +29,7 @@ import io.netty.channel.socket.DatagramChannel;
|
||||
import io.netty.channel.socket.DatagramChannelConfig;
|
||||
import io.netty.channel.socket.DatagramPacket;
|
||||
import io.netty.channel.unix.DatagramSocketAddress;
|
||||
import io.netty.channel.unix.FileDescriptor;
|
||||
import io.netty.channel.unix.Socket;
|
||||
import io.netty.channel.unix.IovArray;
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
@ -45,7 +44,8 @@ import java.nio.channels.NotYetConnectedException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static io.netty.channel.unix.Socket.newSocketDgram;
|
||||
import static io.netty.channel.epoll.LinuxSocket.newSocketDgram;
|
||||
import static io.netty.channel.unix.Limits.IOV_MAX;
|
||||
|
||||
/**
|
||||
* {@link DatagramChannel} implementation that uses linux EPOLL Edge-Triggered Mode for
|
||||
@ -70,15 +70,11 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
|
||||
config = new EpollDatagramChannelConfig(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #EpollDatagramChannel(Socket)}.
|
||||
*/
|
||||
@Deprecated
|
||||
public EpollDatagramChannel(FileDescriptor fd) {
|
||||
this(new Socket(fd.intValue()));
|
||||
public EpollDatagramChannel(int fd) {
|
||||
this(new LinuxSocket(fd));
|
||||
}
|
||||
|
||||
public EpollDatagramChannel(Socket fd) {
|
||||
EpollDatagramChannel(LinuxSocket fd) {
|
||||
super(null, fd, Native.EPOLLIN, true);
|
||||
// As we create an EpollDatagramChannel from a FileDescriptor we should try to obtain the remote and local
|
||||
// address from it. This is needed as the FileDescriptor may be bound already.
|
||||
@ -104,7 +100,7 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public boolean isActive() {
|
||||
return fd().isOpen() && (config.getActiveOnOpen() && isRegistered() || active);
|
||||
return socket.isOpen() && (config.getActiveOnOpen() && isRegistered() || active);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -280,8 +276,8 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
|
||||
protected void doBind(SocketAddress localAddress) throws Exception {
|
||||
InetSocketAddress addr = (InetSocketAddress) localAddress;
|
||||
checkResolvable(addr);
|
||||
fd().bind(addr);
|
||||
local = fd().localAddress();
|
||||
socket.bind(addr);
|
||||
local = socket.localAddress();
|
||||
active = true;
|
||||
}
|
||||
|
||||
@ -307,7 +303,7 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
|
||||
NativeDatagramPacketArray.NativeDatagramPacket[] packets = array.packets();
|
||||
|
||||
while (cnt > 0) {
|
||||
int send = Native.sendmmsg(fd().intValue(), packets, offset, cnt);
|
||||
int send = Native.sendmmsg(socket.intValue(), packets, offset, cnt);
|
||||
if (send == 0) {
|
||||
// Did not write all messages.
|
||||
setFlag(Native.EPOLLOUT);
|
||||
@ -323,7 +319,7 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
|
||||
}
|
||||
}
|
||||
boolean done = false;
|
||||
for (int i = config().getWriteSpinCount() - 1; i >= 0; i--) {
|
||||
for (int i = config().getWriteSpinCount(); i > 0; --i) {
|
||||
if (doWriteMessage(msg)) {
|
||||
done = true;
|
||||
break;
|
||||
@ -375,7 +371,7 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
|
||||
final int writtenBytes;
|
||||
if (data.hasMemoryAddress()) {
|
||||
long memoryAddress = data.memoryAddress();
|
||||
writtenBytes = fd().sendToAddress(memoryAddress, data.readerIndex(), data.writerIndex(),
|
||||
writtenBytes = socket.sendToAddress(memoryAddress, data.readerIndex(), data.writerIndex(),
|
||||
remoteAddress.getAddress(), remoteAddress.getPort());
|
||||
} else if (data instanceof CompositeByteBuf) {
|
||||
IovArray array = ((EpollEventLoop) eventLoop()).cleanArray();
|
||||
@ -383,11 +379,11 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
|
||||
int cnt = array.count();
|
||||
assert cnt != 0;
|
||||
|
||||
writtenBytes = fd().sendToAddresses(array.memoryAddress(0),
|
||||
writtenBytes = socket.sendToAddresses(array.memoryAddress(0),
|
||||
cnt, remoteAddress.getAddress(), remoteAddress.getPort());
|
||||
} else {
|
||||
ByteBuffer nioData = data.internalNioBuffer(data.readerIndex(), data.readableBytes());
|
||||
writtenBytes = fd().sendTo(nioData, nioData.position(), nioData.limit(),
|
||||
writtenBytes = socket.sendTo(nioData, nioData.position(), nioData.limit(),
|
||||
remoteAddress.getAddress(), remoteAddress.getPort());
|
||||
}
|
||||
|
||||
@ -407,7 +403,7 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
|
||||
// Special handling of CompositeByteBuf to reduce memory copies if some of the Components
|
||||
// in the CompositeByteBuf are backed by a memoryAddress.
|
||||
CompositeByteBuf comp = (CompositeByteBuf) content;
|
||||
if (comp.isDirect() && comp.nioBufferCount() <= Native.IOV_MAX) {
|
||||
if (comp.isDirect() && comp.nioBufferCount() <= IOV_MAX) {
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
@ -423,7 +419,7 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
|
||||
// Special handling of CompositeByteBuf to reduce memory copies if some of the Components
|
||||
// in the CompositeByteBuf are backed by a memoryAddress.
|
||||
CompositeByteBuf comp = (CompositeByteBuf) buf;
|
||||
if (!comp.isDirect() || comp.nioBufferCount() > Native.IOV_MAX) {
|
||||
if (!comp.isDirect() || comp.nioBufferCount() > IOV_MAX) {
|
||||
// more then 1024 buffers for gathering writes so just do a memory copy.
|
||||
buf = newDirectBuffer(buf);
|
||||
assert buf.hasMemoryAddress();
|
||||
@ -452,7 +448,7 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
|
||||
// Special handling of CompositeByteBuf to reduce memory copies if some of the Components
|
||||
// in the CompositeByteBuf are backed by a memoryAddress.
|
||||
CompositeByteBuf comp = (CompositeByteBuf) content;
|
||||
if (comp.isDirect() && comp.nioBufferCount() <= Native.IOV_MAX) {
|
||||
if (comp.isDirect() && comp.nioBufferCount() <= IOV_MAX) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
@ -494,7 +490,7 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
|
||||
|
||||
checkResolvable(remoteAddress);
|
||||
EpollDatagramChannel.this.remote = remoteAddress;
|
||||
EpollDatagramChannel.this.local = fd().localAddress();
|
||||
EpollDatagramChannel.this.local = socket.localAddress();
|
||||
success = true;
|
||||
|
||||
// First notify the promise before notifying the handler.
|
||||
@ -543,11 +539,11 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
|
||||
final DatagramSocketAddress remoteAddress;
|
||||
if (data.hasMemoryAddress()) {
|
||||
// has a memory address so use optimized call
|
||||
remoteAddress = fd().recvFromAddress(data.memoryAddress(), data.writerIndex(),
|
||||
remoteAddress = socket.recvFromAddress(data.memoryAddress(), data.writerIndex(),
|
||||
data.capacity());
|
||||
} else {
|
||||
ByteBuffer nioData = data.internalNioBuffer(data.writerIndex(), data.writableBytes());
|
||||
remoteAddress = fd().recvFrom(nioData, nioData.position(), nioData.limit());
|
||||
remoteAddress = socket.recvFrom(nioData, nioData.position(), nioData.limit());
|
||||
}
|
||||
|
||||
if (remoteAddress == null) {
|
||||
|
@ -23,7 +23,6 @@ import io.netty.channel.MessageSizeEstimator;
|
||||
import io.netty.channel.RecvByteBufAllocator;
|
||||
import io.netty.channel.WriteBufferWaterMark;
|
||||
import io.netty.channel.socket.DatagramChannelConfig;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
@ -208,7 +207,7 @@ public final class EpollDatagramChannelConfig extends EpollChannelConfig impleme
|
||||
@Override
|
||||
public int getSendBufferSize() {
|
||||
try {
|
||||
return datagramChannel.fd().getSendBufferSize();
|
||||
return datagramChannel.socket.getSendBufferSize();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
@ -217,7 +216,7 @@ public final class EpollDatagramChannelConfig extends EpollChannelConfig impleme
|
||||
@Override
|
||||
public EpollDatagramChannelConfig setSendBufferSize(int sendBufferSize) {
|
||||
try {
|
||||
datagramChannel.fd().setSendBufferSize(sendBufferSize);
|
||||
datagramChannel.socket.setSendBufferSize(sendBufferSize);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
@ -227,7 +226,7 @@ public final class EpollDatagramChannelConfig extends EpollChannelConfig impleme
|
||||
@Override
|
||||
public int getReceiveBufferSize() {
|
||||
try {
|
||||
return datagramChannel.fd().getReceiveBufferSize();
|
||||
return datagramChannel.socket.getReceiveBufferSize();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
@ -236,7 +235,7 @@ public final class EpollDatagramChannelConfig extends EpollChannelConfig impleme
|
||||
@Override
|
||||
public EpollDatagramChannelConfig setReceiveBufferSize(int receiveBufferSize) {
|
||||
try {
|
||||
datagramChannel.fd().setReceiveBufferSize(receiveBufferSize);
|
||||
datagramChannel.socket.setReceiveBufferSize(receiveBufferSize);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
@ -246,7 +245,7 @@ public final class EpollDatagramChannelConfig extends EpollChannelConfig impleme
|
||||
@Override
|
||||
public int getTrafficClass() {
|
||||
try {
|
||||
return Native.getTrafficClass(datagramChannel.fd().intValue());
|
||||
return datagramChannel.socket.getTrafficClass();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
@ -255,7 +254,7 @@ public final class EpollDatagramChannelConfig extends EpollChannelConfig impleme
|
||||
@Override
|
||||
public EpollDatagramChannelConfig setTrafficClass(int trafficClass) {
|
||||
try {
|
||||
Native.setTrafficClass(datagramChannel.fd().intValue(), trafficClass);
|
||||
datagramChannel.socket.setTrafficClass(trafficClass);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
@ -265,7 +264,7 @@ public final class EpollDatagramChannelConfig extends EpollChannelConfig impleme
|
||||
@Override
|
||||
public boolean isReuseAddress() {
|
||||
try {
|
||||
return Native.isReuseAddress(datagramChannel.fd().intValue()) == 1;
|
||||
return datagramChannel.socket.isReuseAddress();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
@ -274,7 +273,7 @@ public final class EpollDatagramChannelConfig extends EpollChannelConfig impleme
|
||||
@Override
|
||||
public EpollDatagramChannelConfig setReuseAddress(boolean reuseAddress) {
|
||||
try {
|
||||
Native.setReuseAddress(datagramChannel.fd().intValue(), reuseAddress ? 1 : 0);
|
||||
datagramChannel.socket.setReuseAddress(reuseAddress);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
@ -284,7 +283,7 @@ public final class EpollDatagramChannelConfig extends EpollChannelConfig impleme
|
||||
@Override
|
||||
public boolean isBroadcast() {
|
||||
try {
|
||||
return Native.isBroadcast(datagramChannel.fd().intValue()) == 1;
|
||||
return datagramChannel.socket.isBroadcast();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
@ -293,7 +292,7 @@ public final class EpollDatagramChannelConfig extends EpollChannelConfig impleme
|
||||
@Override
|
||||
public EpollDatagramChannelConfig setBroadcast(boolean broadcast) {
|
||||
try {
|
||||
Native.setBroadcast(datagramChannel.fd().intValue(), broadcast ? 1 : 0);
|
||||
datagramChannel.socket.setBroadcast(broadcast);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
@ -351,7 +350,7 @@ public final class EpollDatagramChannelConfig extends EpollChannelConfig impleme
|
||||
*/
|
||||
public boolean isReusePort() {
|
||||
try {
|
||||
return Native.isReusePort(datagramChannel.fd().intValue()) == 1;
|
||||
return datagramChannel.socket.isReusePort();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
@ -366,7 +365,7 @@ public final class EpollDatagramChannelConfig extends EpollChannelConfig impleme
|
||||
*/
|
||||
public EpollDatagramChannelConfig setReusePort(boolean reusePort) {
|
||||
try {
|
||||
Native.setReusePort(datagramChannel.fd().intValue(), reusePort ? 1 : 0);
|
||||
datagramChannel.socket.setReusePort(reusePort);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
|
@ -23,12 +23,12 @@ import io.netty.channel.unix.DomainSocketAddress;
|
||||
import io.netty.channel.unix.DomainSocketChannel;
|
||||
import io.netty.channel.unix.FileDescriptor;
|
||||
import io.netty.channel.unix.PeerCredentials;
|
||||
import io.netty.channel.unix.Socket;
|
||||
import io.netty.util.internal.UnstableApi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.SocketAddress;
|
||||
|
||||
import static io.netty.channel.unix.Socket.newSocketDomain;
|
||||
import java.io.IOException;
|
||||
import static io.netty.channel.epoll.LinuxSocket.newSocketDomain;
|
||||
|
||||
public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel implements DomainSocketChannel {
|
||||
private final EpollDomainSocketChannelConfig config = new EpollDomainSocketChannelConfig(this);
|
||||
@ -40,33 +40,20 @@ public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel i
|
||||
super(newSocketDomain(), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #EpollDomainSocketChannel(Channel, Socket)}.
|
||||
*/
|
||||
@Deprecated
|
||||
public EpollDomainSocketChannel(Channel parent, FileDescriptor fd) {
|
||||
super(parent, new Socket(fd.intValue()));
|
||||
EpollDomainSocketChannel(Channel parent, FileDescriptor fd) {
|
||||
super(parent, new LinuxSocket(fd.intValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #EpollDomainSocketChannel(Socket, boolean)}.
|
||||
* <p>
|
||||
* Creates a new {@link EpollDomainSocketChannel} from an existing {@link FileDescriptor}
|
||||
*/
|
||||
@Deprecated
|
||||
public EpollDomainSocketChannel(FileDescriptor fd) {
|
||||
public EpollDomainSocketChannel(int fd) {
|
||||
super(fd);
|
||||
}
|
||||
|
||||
public EpollDomainSocketChannel(Channel parent, Socket fd) {
|
||||
public EpollDomainSocketChannel(Channel parent, LinuxSocket fd) {
|
||||
super(parent, fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link EpollDomainSocketChannel} from an existing {@link FileDescriptor}
|
||||
*/
|
||||
public EpollDomainSocketChannel(Socket fd, boolean active) {
|
||||
super(fd, active);
|
||||
public EpollDomainSocketChannel(int fd, boolean active) {
|
||||
super(new LinuxSocket(fd), active);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -86,7 +73,7 @@ public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel i
|
||||
|
||||
@Override
|
||||
protected void doBind(SocketAddress localAddress) throws Exception {
|
||||
fd().bind(localAddress);
|
||||
socket.bind(localAddress);
|
||||
local = (DomainSocketAddress) localAddress;
|
||||
}
|
||||
|
||||
@ -118,7 +105,7 @@ public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel i
|
||||
@Override
|
||||
protected boolean doWriteSingle(ChannelOutboundBuffer in, int writeSpinCount) throws Exception {
|
||||
Object msg = in.current();
|
||||
if (msg instanceof FileDescriptor && Native.sendFd(fd().intValue(), ((FileDescriptor) msg).intValue()) > 0) {
|
||||
if (msg instanceof FileDescriptor && socket.sendFd(((FileDescriptor) msg).intValue()) > 0) {
|
||||
// File descriptor was written, so remove it.
|
||||
in.remove();
|
||||
return true;
|
||||
@ -138,8 +125,9 @@ public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel i
|
||||
* Returns the unix credentials (uid, gid, pid) of the peer
|
||||
* <a href=http://man7.org/linux/man-pages/man7/socket.7.html>SO_PEERCRED</a>
|
||||
*/
|
||||
@UnstableApi
|
||||
public PeerCredentials peerCredentials() throws IOException {
|
||||
return fd().getPeerCredentials();
|
||||
return socket.getPeerCredentials();
|
||||
}
|
||||
|
||||
private final class EpollDomainUnsafe extends EpollStreamUnsafe {
|
||||
@ -158,7 +146,7 @@ public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel i
|
||||
}
|
||||
|
||||
private void epollInReadFd() {
|
||||
if (fd().isInputShutdown()) {
|
||||
if (socket.isInputShutdown()) {
|
||||
clearEpollIn0();
|
||||
return;
|
||||
}
|
||||
@ -175,7 +163,7 @@ public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel i
|
||||
// lastBytesRead represents the fd. We use lastBytesRead because it must be set so that the
|
||||
// EpollRecvByteAllocatorHandle knows if it should try to read again or not when autoRead is
|
||||
// enabled.
|
||||
allocHandle.lastBytesRead(Native.recvFd(fd().intValue()));
|
||||
allocHandle.lastBytesRead(socket.recvFd());
|
||||
switch(allocHandle.lastBytesRead()) {
|
||||
case 0:
|
||||
break readLoop;
|
||||
|
@ -21,6 +21,7 @@ import io.netty.channel.SelectStrategy;
|
||||
import io.netty.channel.SingleThreadEventLoop;
|
||||
import io.netty.channel.epoll.AbstractEpollChannel.AbstractEpollUnsafe;
|
||||
import io.netty.channel.unix.FileDescriptor;
|
||||
import io.netty.channel.unix.IovArray;
|
||||
import io.netty.util.IntSupplier;
|
||||
import io.netty.util.collection.IntObjectHashMap;
|
||||
import io.netty.util.collection.IntObjectMap;
|
||||
@ -46,6 +47,12 @@ final class EpollEventLoop extends SingleThreadEventLoop {
|
||||
private static final AtomicIntegerFieldUpdater<EpollEventLoop> WAKEN_UP_UPDATER =
|
||||
AtomicIntegerFieldUpdater.newUpdater(EpollEventLoop.class, "wakenUp");
|
||||
|
||||
static {
|
||||
// Ensure JNI is initialized by the time this class is loaded by this time!
|
||||
// We use unix-common methods in this class which are backed by JNI methods.
|
||||
Epoll.ensureAvailability();
|
||||
}
|
||||
|
||||
private final FileDescriptor epollFd;
|
||||
private final FileDescriptor eventFd;
|
||||
private final IntObjectMap<AbstractEpollChannel> channels = new IntObjectHashMap<AbstractEpollChannel>(4096);
|
||||
@ -132,7 +139,7 @@ final class EpollEventLoop extends SingleThreadEventLoop {
|
||||
*/
|
||||
void add(AbstractEpollChannel ch) throws IOException {
|
||||
assert inEventLoop();
|
||||
int fd = ch.fd().intValue();
|
||||
int fd = ch.socket.intValue();
|
||||
Native.epollCtlAdd(epollFd.intValue(), fd, ch.flags);
|
||||
channels.put(fd, ch);
|
||||
}
|
||||
@ -142,7 +149,7 @@ final class EpollEventLoop extends SingleThreadEventLoop {
|
||||
*/
|
||||
void modify(AbstractEpollChannel ch) throws IOException {
|
||||
assert inEventLoop();
|
||||
Native.epollCtlMod(epollFd.intValue(), ch.fd().intValue(), ch.flags);
|
||||
Native.epollCtlMod(epollFd.intValue(), ch.socket.intValue(), ch.flags);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -152,7 +159,7 @@ final class EpollEventLoop extends SingleThreadEventLoop {
|
||||
assert inEventLoop();
|
||||
|
||||
if (ch.isOpen()) {
|
||||
int fd = ch.fd().intValue();
|
||||
int fd = ch.socket.intValue();
|
||||
if (channels.remove(fd) != null) {
|
||||
// Remove the epoll. This is only needed if it's still open as otherwise it will be automatically
|
||||
// removed once the file-descriptor is closed.
|
||||
|
@ -33,6 +33,10 @@ import java.util.concurrent.ThreadFactory;
|
||||
* it only works on linux.
|
||||
*/
|
||||
public final class EpollEventLoopGroup extends MultithreadEventLoopGroup {
|
||||
static {
|
||||
// Ensure JNI is initialized by the time this class is loaded by this time!
|
||||
Epoll.ensureAvailability();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance using the default number of threads and the default {@link ThreadFactory}.
|
||||
|
@ -24,14 +24,14 @@ import io.netty.util.internal.ObjectUtil;
|
||||
|
||||
class EpollRecvByteAllocatorHandle implements RecvByteBufAllocator.ExtendedHandle {
|
||||
private final RecvByteBufAllocator.ExtendedHandle delegate;
|
||||
private boolean isEdgeTriggered;
|
||||
private boolean receivedRdHup;
|
||||
private final UncheckedBooleanSupplier defaultMaybeMoreDataSupplier = new UncheckedBooleanSupplier() {
|
||||
@Override
|
||||
public boolean get() {
|
||||
return maybeMoreDataToRead();
|
||||
}
|
||||
};
|
||||
private boolean isEdgeTriggered;
|
||||
private boolean receivedRdHup;
|
||||
|
||||
EpollRecvByteAllocatorHandle(RecvByteBufAllocator.ExtendedHandle handle) {
|
||||
this.delegate = ObjectUtil.checkNotNull(handle, "handle");
|
||||
|
@ -21,6 +21,7 @@ import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.MessageSizeEstimator;
|
||||
import io.netty.channel.RecvByteBufAllocator;
|
||||
import io.netty.channel.WriteBufferWaterMark;
|
||||
import io.netty.channel.socket.ServerSocketChannelConfig;
|
||||
import io.netty.util.NetUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -30,7 +31,7 @@ import static io.netty.channel.ChannelOption.SO_BACKLOG;
|
||||
import static io.netty.channel.ChannelOption.SO_RCVBUF;
|
||||
import static io.netty.channel.ChannelOption.SO_REUSEADDR;
|
||||
|
||||
public class EpollServerChannelConfig extends EpollChannelConfig {
|
||||
public class EpollServerChannelConfig extends EpollChannelConfig implements ServerSocketChannelConfig {
|
||||
protected final AbstractEpollChannel channel;
|
||||
private volatile int backlog = NetUtil.SOMAXCONN;
|
||||
private volatile int pendingFastOpenRequestsThreshold;
|
||||
@ -84,7 +85,7 @@ public class EpollServerChannelConfig extends EpollChannelConfig {
|
||||
|
||||
public boolean isReuseAddress() {
|
||||
try {
|
||||
return Native.isReuseAddress(channel.fd().intValue()) == 1;
|
||||
return channel.socket.isReuseAddress();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
@ -92,7 +93,7 @@ public class EpollServerChannelConfig extends EpollChannelConfig {
|
||||
|
||||
public EpollServerChannelConfig setReuseAddress(boolean reuseAddress) {
|
||||
try {
|
||||
Native.setReuseAddress(channel.fd().intValue(), reuseAddress ? 1 : 0);
|
||||
channel.socket.setReuseAddress(reuseAddress);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
@ -101,7 +102,7 @@ public class EpollServerChannelConfig extends EpollChannelConfig {
|
||||
|
||||
public int getReceiveBufferSize() {
|
||||
try {
|
||||
return channel.fd().getReceiveBufferSize();
|
||||
return channel.socket.getReceiveBufferSize();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
@ -109,7 +110,7 @@ public class EpollServerChannelConfig extends EpollChannelConfig {
|
||||
|
||||
public EpollServerChannelConfig setReceiveBufferSize(int receiveBufferSize) {
|
||||
try {
|
||||
channel.fd().setReceiveBufferSize(receiveBufferSize);
|
||||
channel.socket.setReceiveBufferSize(receiveBufferSize);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
@ -154,6 +155,11 @@ public class EpollServerChannelConfig extends EpollChannelConfig {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EpollServerChannelConfig setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EpollServerChannelConfig setConnectTimeoutMillis(int connectTimeoutMillis) {
|
||||
super.setConnectTimeoutMillis(connectTimeoutMillis);
|
||||
|
@ -17,7 +17,6 @@ package io.netty.channel.epoll;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.unix.DomainSocketAddress;
|
||||
import io.netty.channel.unix.FileDescriptor;
|
||||
import io.netty.channel.unix.ServerDomainSocketChannel;
|
||||
import io.netty.channel.unix.Socket;
|
||||
import io.netty.util.internal.logging.InternalLogger;
|
||||
@ -26,8 +25,7 @@ import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||
import java.io.File;
|
||||
import java.net.SocketAddress;
|
||||
|
||||
import static io.netty.channel.unix.Socket.newSocketDomain;
|
||||
|
||||
import static io.netty.channel.epoll.LinuxSocket.newSocketDomain;
|
||||
|
||||
public final class EpollServerDomainSocketChannel extends AbstractEpollServerChannel
|
||||
implements ServerDomainSocketChannel {
|
||||
@ -41,22 +39,15 @@ public final class EpollServerDomainSocketChannel extends AbstractEpollServerCha
|
||||
super(newSocketDomain(), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #EpollServerDomainSocketChannel(Socket, boolean)}.
|
||||
* Creates a new {@link EpollServerDomainSocketChannel} from an existing {@link FileDescriptor}.
|
||||
*/
|
||||
public EpollServerDomainSocketChannel(FileDescriptor fd) {
|
||||
public EpollServerDomainSocketChannel(int fd) {
|
||||
super(fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #EpollServerDomainSocketChannel(Socket, boolean)}.
|
||||
*/
|
||||
public EpollServerDomainSocketChannel(Socket fd) {
|
||||
EpollServerDomainSocketChannel(LinuxSocket fd) {
|
||||
super(fd);
|
||||
}
|
||||
|
||||
public EpollServerDomainSocketChannel(Socket fd, boolean active) {
|
||||
EpollServerDomainSocketChannel(LinuxSocket fd, boolean active) {
|
||||
super(fd, active);
|
||||
}
|
||||
|
||||
@ -72,8 +63,8 @@ public final class EpollServerDomainSocketChannel extends AbstractEpollServerCha
|
||||
|
||||
@Override
|
||||
protected void doBind(SocketAddress localAddress) throws Exception {
|
||||
fd().bind(localAddress);
|
||||
fd().listen(config.getBacklog());
|
||||
socket.bind(localAddress);
|
||||
socket.listen(config.getBacklog());
|
||||
local = (DomainSocketAddress) localAddress;
|
||||
active = true;
|
||||
}
|
||||
|
@ -18,8 +18,6 @@ package io.netty.channel.epoll;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.EventLoop;
|
||||
import io.netty.channel.socket.ServerSocketChannel;
|
||||
import io.netty.channel.unix.FileDescriptor;
|
||||
import io.netty.channel.unix.Socket;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
@ -29,8 +27,8 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import static io.netty.channel.epoll.LinuxSocket.newSocketStream;
|
||||
import static io.netty.channel.unix.NativeInetAddress.address;
|
||||
import static io.netty.channel.unix.Socket.newSocketStream;
|
||||
|
||||
/**
|
||||
* {@link ServerSocketChannel} implementation that uses linux EPOLL Edge-Triggered Mode for
|
||||
@ -47,23 +45,13 @@ public final class EpollServerSocketChannel extends AbstractEpollServerChannel i
|
||||
config = new EpollServerSocketChannelConfig(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #EpollServerSocketChannel(Socket, boolean)}.
|
||||
* Creates a new {@link EpollServerSocketChannel} from an existing {@link FileDescriptor}.
|
||||
*/
|
||||
@Deprecated
|
||||
public EpollServerSocketChannel(FileDescriptor fd) {
|
||||
public EpollServerSocketChannel(int fd) {
|
||||
// Must call this constructor to ensure this object's local address is configured correctly.
|
||||
// The local address can only be obtained from a Socket object.
|
||||
this(new Socket(fd.intValue()));
|
||||
this(new LinuxSocket(fd));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #EpollServerSocketChannel(Socket, boolean)}.
|
||||
* Creates a new {@link EpollServerSocketChannel} from an existing {@link Socket}.
|
||||
*/
|
||||
@Deprecated
|
||||
public EpollServerSocketChannel(Socket fd) {
|
||||
EpollServerSocketChannel(LinuxSocket fd) {
|
||||
super(fd);
|
||||
// As we create an EpollServerSocketChannel from a FileDescriptor we should try to obtain the remote and local
|
||||
// address from it. This is needed as the FileDescriptor may be bound already.
|
||||
@ -71,7 +59,7 @@ public final class EpollServerSocketChannel extends AbstractEpollServerChannel i
|
||||
config = new EpollServerSocketChannelConfig(this);
|
||||
}
|
||||
|
||||
public EpollServerSocketChannel(Socket fd, boolean active) {
|
||||
EpollServerSocketChannel(LinuxSocket fd, boolean active) {
|
||||
super(fd, active);
|
||||
// As we create an EpollServerSocketChannel from a FileDescriptor we should try to obtain the remote and local
|
||||
// address from it. This is needed as the FileDescriptor may be bound already.
|
||||
@ -88,12 +76,12 @@ public final class EpollServerSocketChannel extends AbstractEpollServerChannel i
|
||||
protected void doBind(SocketAddress localAddress) throws Exception {
|
||||
InetSocketAddress addr = (InetSocketAddress) localAddress;
|
||||
checkResolvable(addr);
|
||||
fd().bind(addr);
|
||||
local = fd().localAddress();
|
||||
socket.bind(addr);
|
||||
local = socket.localAddress();
|
||||
if (Native.IS_SUPPORTING_TCP_FASTOPEN && config.getTcpFastopen() > 0) {
|
||||
Native.setTcpFastopen(fd().intValue(), config.getTcpFastopen());
|
||||
socket.setTcpFastOpen(config.getTcpFastopen());
|
||||
}
|
||||
fd().listen(config.getBacklog());
|
||||
socket.listen(config.getBacklog());
|
||||
active = true;
|
||||
}
|
||||
|
||||
@ -119,7 +107,7 @@ public final class EpollServerSocketChannel extends AbstractEpollServerChannel i
|
||||
|
||||
@Override
|
||||
protected Channel newChildChannel(int fd, byte[] address, int offset, int len) throws Exception {
|
||||
return new EpollSocketChannel(this, new Socket(fd), address(address, offset, len));
|
||||
return new EpollSocketChannel(this, new LinuxSocket(fd), address(address, offset, len));
|
||||
}
|
||||
|
||||
Collection<InetAddress> tcpMd5SigAddresses() {
|
||||
|
@ -186,7 +186,7 @@ public final class EpollServerSocketChannelConfig extends EpollServerChannelConf
|
||||
*/
|
||||
public boolean isReusePort() {
|
||||
try {
|
||||
return Native.isReusePort(channel.fd().intValue()) == 1;
|
||||
return channel.socket.isReusePort();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
@ -201,7 +201,7 @@ public final class EpollServerSocketChannelConfig extends EpollServerChannelConf
|
||||
*/
|
||||
public EpollServerSocketChannelConfig setReusePort(boolean reusePort) {
|
||||
try {
|
||||
Native.setReusePort(channel.fd().intValue(), reusePort ? 1 : 0);
|
||||
channel.socket.setReusePort(reusePort);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
@ -214,7 +214,7 @@ public final class EpollServerSocketChannelConfig extends EpollServerChannelConf
|
||||
*/
|
||||
public boolean isFreeBind() {
|
||||
try {
|
||||
return Native.isIpFreeBind(channel.fd().intValue()) != 0;
|
||||
return channel.socket.isIpFreeBind();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
@ -226,7 +226,7 @@ public final class EpollServerSocketChannelConfig extends EpollServerChannelConf
|
||||
*/
|
||||
public EpollServerSocketChannelConfig setFreeBind(boolean freeBind) {
|
||||
try {
|
||||
Native.setIpFreeBind(channel.fd().intValue(), freeBind ? 1 : 0);
|
||||
channel.socket.setIpFreeBind(freeBind);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
@ -238,7 +238,7 @@ public final class EpollServerSocketChannelConfig extends EpollServerChannelConf
|
||||
*/
|
||||
public EpollServerSocketChannelConfig setTcpDeferAccept(int deferAccept) {
|
||||
try {
|
||||
channel.fd().setTcpDeferAccept(deferAccept);
|
||||
channel.socket.setTcpDeferAccept(deferAccept);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
@ -250,7 +250,7 @@ public final class EpollServerSocketChannelConfig extends EpollServerChannelConf
|
||||
*/
|
||||
public int getTcpDeferAccept() {
|
||||
try {
|
||||
return channel.fd().getTcpDeferAccept();
|
||||
return channel.socket.getTcpDeferAccept();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
|
@ -19,8 +19,6 @@ import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelException;
|
||||
import io.netty.channel.socket.ServerSocketChannel;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.unix.FileDescriptor;
|
||||
import io.netty.channel.unix.Socket;
|
||||
import io.netty.util.concurrent.GlobalEventExecutor;
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
|
||||
@ -35,7 +33,7 @@ import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import static io.netty.channel.unix.Socket.newSocketStream;
|
||||
import static io.netty.channel.epoll.LinuxSocket.newSocketStream;
|
||||
|
||||
/**
|
||||
* {@link SocketChannel} implementation that uses linux EPOLL Edge-Triggered Mode for
|
||||
@ -51,7 +49,7 @@ public final class EpollSocketChannel extends AbstractEpollStreamChannel impleme
|
||||
|
||||
private volatile Collection<InetAddress> tcpMd5SigAddresses = Collections.emptyList();
|
||||
|
||||
EpollSocketChannel(Channel parent, Socket fd, InetSocketAddress remote) {
|
||||
EpollSocketChannel(Channel parent, LinuxSocket fd, InetSocketAddress remote) {
|
||||
super(parent, fd);
|
||||
config = new EpollSocketChannelConfig(this);
|
||||
// Directly cache the remote and local addresses
|
||||
@ -69,23 +67,16 @@ public final class EpollSocketChannel extends AbstractEpollStreamChannel impleme
|
||||
config = new EpollSocketChannelConfig(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #EpollSocketChannel(Socket, boolean)}.
|
||||
*/
|
||||
@Deprecated
|
||||
public EpollSocketChannel(FileDescriptor fd) {
|
||||
public EpollSocketChannel(int fd) {
|
||||
super(fd);
|
||||
// As we create an EpollSocketChannel from a FileDescriptor we should try to obtain the remote and local
|
||||
// address from it. This is needed as the FileDescriptor may be bound/connected already.
|
||||
remote = fd().remoteAddress();
|
||||
local = fd().localAddress();
|
||||
remote = socket.remoteAddress();
|
||||
local = socket.localAddress();
|
||||
config = new EpollSocketChannelConfig(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link EpollSocketChannel} from an existing {@link FileDescriptor}.
|
||||
*/
|
||||
public EpollSocketChannel(Socket fd, boolean active) {
|
||||
EpollSocketChannel(LinuxSocket fd, boolean active) {
|
||||
super(fd, active);
|
||||
// As we create an EpollSocketChannel from a FileDescriptor we should try to obtain the remote and local
|
||||
// address from it. This is needed as the FileDescriptor may be bound/connected already.
|
||||
@ -107,7 +98,7 @@ public final class EpollSocketChannel extends AbstractEpollStreamChannel impleme
|
||||
*/
|
||||
public EpollTcpInfo tcpInfo(EpollTcpInfo info) {
|
||||
try {
|
||||
Native.tcpInfo(fd().intValue(), info);
|
||||
socket.getTcpInfo(info);
|
||||
return info;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
@ -137,8 +128,8 @@ public final class EpollSocketChannel extends AbstractEpollStreamChannel impleme
|
||||
@Override
|
||||
protected void doBind(SocketAddress local) throws Exception {
|
||||
InetSocketAddress localAddress = (InetSocketAddress) local;
|
||||
fd().bind(localAddress);
|
||||
this.local = fd().localAddress();
|
||||
socket.bind(localAddress);
|
||||
this.local = socket.localAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -192,7 +183,7 @@ public final class EpollSocketChannel extends AbstractEpollStreamChannel impleme
|
||||
|
||||
boolean connected = super.doConnect(remoteAddress, localAddress);
|
||||
if (connected) {
|
||||
remote = computeRemoteAddr(remoteAddr, fd().remoteAddress());
|
||||
remote = computeRemoteAddr(remoteAddr, socket.remoteAddress());
|
||||
} else {
|
||||
// Store for later usage in doFinishConnect()
|
||||
requestedRemote = remoteAddr;
|
||||
@ -200,7 +191,7 @@ public final class EpollSocketChannel extends AbstractEpollStreamChannel impleme
|
||||
// We always need to set the localAddress even if not connected yet as the bind already took place.
|
||||
//
|
||||
// See https://github.com/netty/netty/issues/3463
|
||||
local = fd().localAddress();
|
||||
local = socket.localAddress();
|
||||
return connected;
|
||||
}
|
||||
|
||||
@ -229,7 +220,7 @@ public final class EpollSocketChannel extends AbstractEpollStreamChannel impleme
|
||||
@Override
|
||||
boolean doFinishConnect() throws Exception {
|
||||
if (super.doFinishConnect()) {
|
||||
remote = computeRemoteAddr(requestedRemote, fd().remoteAddress());
|
||||
remote = computeRemoteAddr(requestedRemote, socket.remoteAddress());
|
||||
requestedRemote = null;
|
||||
return true;
|
||||
}
|
||||
|
@ -28,10 +28,16 @@ import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.util.Map;
|
||||
|
||||
import static io.netty.channel.ChannelOption.*;
|
||||
import static io.netty.channel.ChannelOption.ALLOW_HALF_CLOSURE;
|
||||
import static io.netty.channel.ChannelOption.IP_TOS;
|
||||
import static io.netty.channel.ChannelOption.SO_KEEPALIVE;
|
||||
import static io.netty.channel.ChannelOption.SO_LINGER;
|
||||
import static io.netty.channel.ChannelOption.SO_RCVBUF;
|
||||
import static io.netty.channel.ChannelOption.SO_REUSEADDR;
|
||||
import static io.netty.channel.ChannelOption.SO_SNDBUF;
|
||||
import static io.netty.channel.ChannelOption.TCP_NODELAY;
|
||||
|
||||
public final class EpollSocketChannelConfig extends EpollChannelConfig implements SocketChannelConfig {
|
||||
private static final long MAX_UINT32_T = 0xFFFFFFFFL;
|
||||
private final EpollSocketChannel channel;
|
||||
private volatile boolean allowHalfClosure;
|
||||
|
||||
@ -156,7 +162,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
@Override
|
||||
public int getReceiveBufferSize() {
|
||||
try {
|
||||
return channel.fd().getReceiveBufferSize();
|
||||
return channel.socket.getReceiveBufferSize();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
@ -165,7 +171,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
@Override
|
||||
public int getSendBufferSize() {
|
||||
try {
|
||||
return channel.fd().getSendBufferSize();
|
||||
return channel.socket.getSendBufferSize();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
@ -174,7 +180,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
@Override
|
||||
public int getSoLinger() {
|
||||
try {
|
||||
return channel.fd().getSoLinger();
|
||||
return channel.socket.getSoLinger();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
@ -183,7 +189,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
@Override
|
||||
public int getTrafficClass() {
|
||||
try {
|
||||
return Native.getTrafficClass(channel.fd().intValue());
|
||||
return channel.socket.getTrafficClass();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
@ -192,7 +198,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
@Override
|
||||
public boolean isKeepAlive() {
|
||||
try {
|
||||
return channel.fd().isKeepAlive();
|
||||
return channel.socket.isKeepAlive();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
@ -201,7 +207,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
@Override
|
||||
public boolean isReuseAddress() {
|
||||
try {
|
||||
return Native.isReuseAddress(channel.fd().intValue()) == 1;
|
||||
return channel.socket.isReuseAddress();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
@ -210,7 +216,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
@Override
|
||||
public boolean isTcpNoDelay() {
|
||||
try {
|
||||
return channel.fd().isTcpNoDelay();
|
||||
return channel.socket.isTcpNoDelay();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
@ -221,7 +227,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
*/
|
||||
public boolean isTcpCork() {
|
||||
try {
|
||||
return channel.fd().isTcpCork();
|
||||
return channel.socket.isTcpCork();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
@ -233,7 +239,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
*/
|
||||
public long getTcpNotSentLowAt() {
|
||||
try {
|
||||
return Native.getTcpNotSentLowAt(channel.fd().intValue()) & MAX_UINT32_T;
|
||||
return channel.socket.getTcpNotSentLowAt();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
@ -244,7 +250,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
*/
|
||||
public int getTcpKeepIdle() {
|
||||
try {
|
||||
return Native.getTcpKeepIdle(channel.fd().intValue());
|
||||
return channel.socket.getTcpKeepIdle();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
@ -255,7 +261,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
*/
|
||||
public int getTcpKeepIntvl() {
|
||||
try {
|
||||
return Native.getTcpKeepIntvl(channel.fd().intValue());
|
||||
return channel.socket.getTcpKeepIntvl();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
@ -266,7 +272,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
*/
|
||||
public int getTcpKeepCnt() {
|
||||
try {
|
||||
return Native.getTcpKeepCnt(channel.fd().intValue());
|
||||
return channel.socket.getTcpKeepCnt();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
@ -277,7 +283,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
*/
|
||||
public int getTcpUserTimeout() {
|
||||
try {
|
||||
return Native.getTcpUserTimeout(channel.fd().intValue());
|
||||
return channel.socket.getTcpUserTimeout();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
@ -286,7 +292,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
@Override
|
||||
public EpollSocketChannelConfig setKeepAlive(boolean keepAlive) {
|
||||
try {
|
||||
channel.fd().setKeepAlive(keepAlive);
|
||||
channel.socket.setKeepAlive(keepAlive);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
@ -302,7 +308,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
@Override
|
||||
public EpollSocketChannelConfig setReceiveBufferSize(int receiveBufferSize) {
|
||||
try {
|
||||
channel.fd().setReceiveBufferSize(receiveBufferSize);
|
||||
channel.socket.setReceiveBufferSize(receiveBufferSize);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
@ -312,7 +318,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
@Override
|
||||
public EpollSocketChannelConfig setReuseAddress(boolean reuseAddress) {
|
||||
try {
|
||||
Native.setReuseAddress(channel.fd().intValue(), reuseAddress ? 1 : 0);
|
||||
channel.socket.setReuseAddress(reuseAddress);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
@ -322,7 +328,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
@Override
|
||||
public EpollSocketChannelConfig setSendBufferSize(int sendBufferSize) {
|
||||
try {
|
||||
channel.fd().setSendBufferSize(sendBufferSize);
|
||||
channel.socket.setSendBufferSize(sendBufferSize);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
@ -332,7 +338,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
@Override
|
||||
public EpollSocketChannelConfig setSoLinger(int soLinger) {
|
||||
try {
|
||||
channel.fd().setSoLinger(soLinger);
|
||||
channel.socket.setSoLinger(soLinger);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
@ -342,7 +348,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
@Override
|
||||
public EpollSocketChannelConfig setTcpNoDelay(boolean tcpNoDelay) {
|
||||
try {
|
||||
channel.fd().setTcpNoDelay(tcpNoDelay);
|
||||
channel.socket.setTcpNoDelay(tcpNoDelay);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
@ -354,7 +360,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
*/
|
||||
public EpollSocketChannelConfig setTcpCork(boolean tcpCork) {
|
||||
try {
|
||||
channel.fd().setTcpCork(tcpCork);
|
||||
channel.socket.setTcpCork(tcpCork);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
@ -366,11 +372,8 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
* @param tcpNotSentLowAt is a uint32_t
|
||||
*/
|
||||
public EpollSocketChannelConfig setTcpNotSentLowAt(long tcpNotSentLowAt) {
|
||||
if (tcpNotSentLowAt < 0 || tcpNotSentLowAt > MAX_UINT32_T) {
|
||||
throw new IllegalArgumentException("tcpNotSentLowAt must be a uint32_t");
|
||||
}
|
||||
try {
|
||||
Native.setTcpNotSentLowAt(channel.fd().intValue(), (int) tcpNotSentLowAt);
|
||||
channel.socket.setTcpNotSentLowAt(tcpNotSentLowAt);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
@ -380,7 +383,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
@Override
|
||||
public EpollSocketChannelConfig setTrafficClass(int trafficClass) {
|
||||
try {
|
||||
Native.setTrafficClass(channel.fd().intValue(), trafficClass);
|
||||
channel.socket.setTrafficClass(trafficClass);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
@ -392,7 +395,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
*/
|
||||
public EpollSocketChannelConfig setTcpKeepIdle(int seconds) {
|
||||
try {
|
||||
Native.setTcpKeepIdle(channel.fd().intValue(), seconds);
|
||||
channel.socket.setTcpKeepIdle(seconds);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
@ -404,7 +407,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
*/
|
||||
public EpollSocketChannelConfig setTcpKeepIntvl(int seconds) {
|
||||
try {
|
||||
Native.setTcpKeepIntvl(channel.fd().intValue(), seconds);
|
||||
channel.socket.setTcpKeepIntvl(seconds);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
@ -416,7 +419,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
*/
|
||||
public EpollSocketChannelConfig setTcpKeepCntl(int probes) {
|
||||
try {
|
||||
Native.setTcpKeepCnt(channel.fd().intValue(), probes);
|
||||
channel.socket.setTcpKeepCnt(probes);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
@ -428,7 +431,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
*/
|
||||
public EpollSocketChannelConfig setTcpUserTimeout(int milliseconds) {
|
||||
try {
|
||||
Native.setTcpUserTimeout(channel.fd().intValue(), milliseconds);
|
||||
channel.socket.setTcpUserTimeout(milliseconds);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
@ -455,7 +458,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
*/
|
||||
public EpollSocketChannelConfig setTcpQuickAck(boolean quickAck) {
|
||||
try {
|
||||
channel.fd().setTcpQuickAck(quickAck);
|
||||
channel.socket.setTcpQuickAck(quickAck);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
@ -468,7 +471,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
*/
|
||||
public boolean isTcpQuickAck() {
|
||||
try {
|
||||
return channel.fd().isTcpQuickAck();
|
||||
return channel.socket.isTcpQuickAck();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
|
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.epoll;
|
||||
|
||||
import io.netty.channel.unix.NativeInetAddress;
|
||||
import io.netty.channel.unix.PeerCredentials;
|
||||
import io.netty.channel.unix.Socket;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
|
||||
/**
|
||||
* A socket which provides access Linux native methods.
|
||||
*/
|
||||
final class LinuxSocket extends Socket {
|
||||
private static final long MAX_UINT32_T = 0xFFFFFFFFL;
|
||||
|
||||
public LinuxSocket(int fd) {
|
||||
super(fd);
|
||||
}
|
||||
|
||||
void setTcpDeferAccept(int deferAccept) throws IOException {
|
||||
setTcpDeferAccept(intValue(), deferAccept);
|
||||
}
|
||||
|
||||
void setTcpQuickAck(boolean quickAck) throws IOException {
|
||||
setTcpQuickAck(intValue(), quickAck ? 1 : 0);
|
||||
}
|
||||
|
||||
void setTcpCork(boolean tcpCork) throws IOException {
|
||||
setTcpCork(intValue(), tcpCork ? 1 : 0);
|
||||
}
|
||||
|
||||
void setTcpNotSentLowAt(long tcpNotSentLowAt) throws IOException {
|
||||
if (tcpNotSentLowAt < 0 || tcpNotSentLowAt > MAX_UINT32_T) {
|
||||
throw new IllegalArgumentException("tcpNotSentLowAt must be a uint32_t");
|
||||
}
|
||||
setTcpNotSentLowAt(intValue(), (int) tcpNotSentLowAt);
|
||||
}
|
||||
|
||||
void setTcpFastOpen(int tcpFastopenBacklog) throws IOException {
|
||||
setTcpFastOpen(intValue(), tcpFastopenBacklog);
|
||||
}
|
||||
|
||||
void setTcpKeepIdle(int seconds) throws IOException {
|
||||
setTcpKeepIdle(intValue(), seconds);
|
||||
}
|
||||
|
||||
void setTcpKeepIntvl(int seconds) throws IOException {
|
||||
setTcpKeepIntvl(intValue(), seconds);
|
||||
}
|
||||
|
||||
void setTcpKeepCnt(int probes) throws IOException {
|
||||
setTcpKeepCnt(intValue(), probes);
|
||||
}
|
||||
|
||||
void setTcpUserTimeout(int milliseconds) throws IOException {
|
||||
setTcpUserTimeout(intValue(), milliseconds);
|
||||
}
|
||||
|
||||
void setIpFreeBind(boolean enabled) throws IOException {
|
||||
setIpFreeBind(intValue(), enabled ? 1 : 0);
|
||||
}
|
||||
|
||||
void getTcpInfo(EpollTcpInfo info) throws IOException {
|
||||
getTcpInfo(intValue(), info.info);
|
||||
}
|
||||
|
||||
void setTcpMd5Sig(InetAddress address, byte[] key) throws IOException {
|
||||
final NativeInetAddress a = NativeInetAddress.newInstance(address);
|
||||
setTcpMd5Sig(intValue(), a.address(), a.scopeId(), key);
|
||||
}
|
||||
|
||||
boolean isTcpCork() throws IOException {
|
||||
return isTcpCork(intValue()) != 0;
|
||||
}
|
||||
|
||||
int getTcpDeferAccept() throws IOException {
|
||||
return getTcpDeferAccept(intValue());
|
||||
}
|
||||
|
||||
boolean isTcpQuickAck() throws IOException {
|
||||
return isTcpQuickAck(intValue()) != 0;
|
||||
}
|
||||
|
||||
long getTcpNotSentLowAt() throws IOException {
|
||||
return getTcpNotSentLowAt(intValue()) & MAX_UINT32_T;
|
||||
}
|
||||
|
||||
int getTcpKeepIdle() throws IOException {
|
||||
return getTcpKeepIdle(intValue());
|
||||
}
|
||||
|
||||
int getTcpKeepIntvl() throws IOException {
|
||||
return getTcpKeepIntvl(intValue());
|
||||
}
|
||||
|
||||
int getTcpKeepCnt() throws IOException {
|
||||
return getTcpKeepCnt(intValue());
|
||||
}
|
||||
|
||||
int getTcpUserTimeout() throws IOException {
|
||||
return getTcpUserTimeout(intValue());
|
||||
}
|
||||
|
||||
boolean isIpFreeBind() throws IOException {
|
||||
return isIpFreeBind(intValue()) != 0;
|
||||
}
|
||||
|
||||
PeerCredentials getPeerCredentials() throws IOException {
|
||||
return getPeerCredentials(intValue());
|
||||
}
|
||||
|
||||
public static LinuxSocket newSocketStream() {
|
||||
return new LinuxSocket(newSocketStream0());
|
||||
}
|
||||
|
||||
public static LinuxSocket newSocketDgram() {
|
||||
return new LinuxSocket(newSocketDgram0());
|
||||
}
|
||||
|
||||
public static LinuxSocket newSocketDomain() {
|
||||
return new LinuxSocket(newSocketDomain0());
|
||||
}
|
||||
|
||||
private static native int getTcpDeferAccept(int fd) throws IOException;
|
||||
private static native int isTcpQuickAck(int fd) throws IOException;
|
||||
private static native int isTcpCork(int fd) throws IOException;
|
||||
private static native int getTcpNotSentLowAt(int fd) throws IOException;
|
||||
private static native int getTcpKeepIdle(int fd) throws IOException;
|
||||
private static native int getTcpKeepIntvl(int fd) throws IOException;
|
||||
private static native int getTcpKeepCnt(int fd) throws IOException;
|
||||
private static native int getTcpUserTimeout(int fd) throws IOException;
|
||||
private static native int isIpFreeBind(int fd) throws IOException;
|
||||
private static native void getTcpInfo(int fd, int[] array) throws IOException;
|
||||
private static native PeerCredentials getPeerCredentials(int fd) throws IOException;
|
||||
|
||||
private static native void setTcpDeferAccept(int fd, int deferAccept) throws IOException;
|
||||
private static native void setTcpQuickAck(int fd, int quickAck) throws IOException;
|
||||
private static native void setTcpCork(int fd, int tcpCork) throws IOException;
|
||||
private static native void setTcpNotSentLowAt(int fd, int tcpNotSentLowAt) throws IOException;
|
||||
private static native void setTcpFastOpen(int fd, int tcpFastopenBacklog) throws IOException;
|
||||
private static native void setTcpKeepIdle(int fd, int seconds) throws IOException;
|
||||
private static native void setTcpKeepIntvl(int fd, int seconds) throws IOException;
|
||||
private static native void setTcpKeepCnt(int fd, int probes) throws IOException;
|
||||
private static native void setTcpUserTimeout(int fd, int milliseconds)throws IOException;
|
||||
private static native void setIpFreeBind(int fd, int freeBind) throws IOException;
|
||||
private static native void setTcpMd5Sig(int fd, byte[] address, int scopeId, byte[] key) throws IOException;
|
||||
}
|
@ -34,13 +34,10 @@ import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epolle
|
||||
import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollin;
|
||||
import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollout;
|
||||
import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollrdhup;
|
||||
import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.iovMax;
|
||||
import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.isSupportingSendmmsg;
|
||||
import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.isSupportingTcpFastopen;
|
||||
import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.kernelVersion;
|
||||
import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.ssizeMax;
|
||||
import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.tcpMd5SigMaxKeyLen;
|
||||
import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.uioMaxIov;
|
||||
import static io.netty.channel.unix.Errors.ERRNO_EAGAIN_NEGATIVE;
|
||||
import static io.netty.channel.unix.Errors.ERRNO_EPIPE_NEGATIVE;
|
||||
import static io.netty.channel.unix.Errors.ERRNO_EWOULDBLOCK_NEGATIVE;
|
||||
@ -72,11 +69,8 @@ public final class Native {
|
||||
public static final int EPOLLET = epollet();
|
||||
public static final int EPOLLERR = epollerr();
|
||||
|
||||
public static final int IOV_MAX = iovMax();
|
||||
public static final int UIO_MAX_IOV = uioMaxIov();
|
||||
public static final boolean IS_SUPPORTING_SENDMMSG = isSupportingSendmmsg();
|
||||
public static final boolean IS_SUPPORTING_TCP_FASTOPEN = isSupportingTcpFastopen();
|
||||
public static final long SSIZE_MAX = ssizeMax();
|
||||
public static final int TCP_MD5SIG_MAXKEYLEN = tcpMd5SigMaxKeyLen();
|
||||
public static final String KERNEL_VERSION = kernelVersion();
|
||||
|
||||
@ -185,82 +179,10 @@ public final class Native {
|
||||
private static native int sendmmsg0(
|
||||
int fd, NativeDatagramPacketArray.NativeDatagramPacket[] msgs, int offset, int len);
|
||||
|
||||
public static int recvFd(int fd) throws IOException {
|
||||
int res = recvFd0(fd);
|
||||
if (res > 0) {
|
||||
return res;
|
||||
}
|
||||
if (res == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (res == ERRNO_EAGAIN_NEGATIVE || res == ERRNO_EWOULDBLOCK_NEGATIVE) {
|
||||
// Everything consumed so just return -1 here.
|
||||
return 0;
|
||||
}
|
||||
throw newIOException("recvFd", res);
|
||||
}
|
||||
|
||||
private static native int recvFd0(int fd);
|
||||
|
||||
public static int sendFd(int socketFd, int fd) throws IOException {
|
||||
int res = sendFd0(socketFd, fd);
|
||||
if (res >= 0) {
|
||||
return res;
|
||||
}
|
||||
if (res == ERRNO_EAGAIN_NEGATIVE || res == ERRNO_EWOULDBLOCK_NEGATIVE) {
|
||||
// Everything consumed so just return -1 here.
|
||||
return -1;
|
||||
}
|
||||
throw newIOException("sendFd", res);
|
||||
}
|
||||
|
||||
private static native int sendFd0(int socketFd, int fd);
|
||||
|
||||
// Socket option operations
|
||||
public static native int isReuseAddress(int fd) throws IOException;
|
||||
public static native int isReusePort(int fd) throws IOException;
|
||||
public static native int getTcpNotSentLowAt(int fd) throws IOException;
|
||||
public static native int getTrafficClass(int fd) throws IOException;
|
||||
public static native int isBroadcast(int fd) throws IOException;
|
||||
public static native int getTcpKeepIdle(int fd) throws IOException;
|
||||
public static native int getTcpKeepIntvl(int fd) throws IOException;
|
||||
public static native int getTcpKeepCnt(int fd) throws IOException;
|
||||
public static native int getTcpUserTimeout(int milliseconds) throws IOException;
|
||||
public static native int isIpFreeBind(int fd)throws IOException;
|
||||
|
||||
public static native void setReuseAddress(int fd, int reuseAddress) throws IOException;
|
||||
public static native void setReusePort(int fd, int reuseAddress) throws IOException;
|
||||
public static native void setTcpFastopen(int fd, int tcpFastopenBacklog) throws IOException;
|
||||
public static native void setTcpNotSentLowAt(int fd, int tcpNotSentLowAt) throws IOException;
|
||||
public static native void setTrafficClass(int fd, int tcpNoDelay) throws IOException;
|
||||
public static native void setBroadcast(int fd, int broadcast) throws IOException;
|
||||
public static native void setTcpKeepIdle(int fd, int seconds) throws IOException;
|
||||
public static native void setTcpKeepIntvl(int fd, int seconds) throws IOException;
|
||||
public static native void setTcpKeepCnt(int fd, int probes) throws IOException;
|
||||
public static native void setTcpUserTimeout(int fd, int milliseconds)throws IOException;
|
||||
public static native void setIpFreeBind(int fd, int freeBind) throws IOException;
|
||||
public static void tcpInfo(int fd, EpollTcpInfo info) throws IOException {
|
||||
tcpInfo0(fd, info.info);
|
||||
}
|
||||
|
||||
private static native void tcpInfo0(int fd, int[] array) throws IOException;
|
||||
|
||||
public static void setTcpMd5Sig(int fd, InetAddress address, byte[] key) throws IOException {
|
||||
final NativeInetAddress a = NativeInetAddress.newInstance(address);
|
||||
setTcpMd5Sig0(fd, a.address(), a.scopeId(), key);
|
||||
}
|
||||
|
||||
private static native void setTcpMd5Sig0(int fd, byte[] address, int scopeId, byte[] key) throws IOException;
|
||||
|
||||
// epoll_event related
|
||||
public static native int sizeofEpollEvent();
|
||||
public static native int offsetofEpollData();
|
||||
|
||||
private Native() {
|
||||
// utility
|
||||
}
|
||||
|
||||
private static void loadNativeLibrary() {
|
||||
String name = SystemPropertyUtil.get("os.name").toLowerCase(Locale.UK).trim();
|
||||
if (!name.startsWith("linux")) {
|
||||
@ -269,4 +191,8 @@ public final class Native {
|
||||
NativeLibraryLoader.load(SystemPropertyUtil.get("io.netty.packagePrefix", "").replace('.', '-') +
|
||||
"netty-transport-native-epoll", PlatformDependent.getClassLoader(Native.class));
|
||||
}
|
||||
|
||||
private Native() {
|
||||
// utility
|
||||
}
|
||||
}
|
||||
|
@ -15,16 +15,18 @@
|
||||
*/
|
||||
package io.netty.channel.epoll;
|
||||
|
||||
import static io.netty.channel.unix.NativeInetAddress.ipv4MappedIpv6Address;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelOutboundBuffer;
|
||||
import io.netty.channel.socket.DatagramPacket;
|
||||
import io.netty.channel.unix.IovArray;
|
||||
import io.netty.util.concurrent.FastThreadLocal;
|
||||
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
import static io.netty.channel.unix.Limits.UIO_MAX_IOV;
|
||||
import static io.netty.channel.unix.NativeInetAddress.ipv4MappedIpv6Address;
|
||||
|
||||
/**
|
||||
* Support <a href="http://linux.die.net/man/2/sendmmsg">sendmmsg(...)</a> on linux with GLIBC 2.14+
|
||||
*/
|
||||
@ -48,7 +50,7 @@ final class NativeDatagramPacketArray implements ChannelOutboundBuffer.MessagePr
|
||||
};
|
||||
|
||||
// Use UIO_MAX_IOV as this is the maximum number we can write with one sendmmsg(...) call.
|
||||
private final NativeDatagramPacket[] packets = new NativeDatagramPacket[Native.UIO_MAX_IOV];
|
||||
private final NativeDatagramPacket[] packets = new NativeDatagramPacket[UIO_MAX_IOV];
|
||||
private int count;
|
||||
|
||||
private NativeDatagramPacketArray() {
|
||||
|
@ -55,7 +55,7 @@ final class TcpMd5Util {
|
||||
// Remove mappings not present in the new set.
|
||||
for (InetAddress addr : current) {
|
||||
if (!newKeys.containsKey(addr)) {
|
||||
Native.setTcpMd5Sig(channel.fd().intValue(), addr, null);
|
||||
channel.socket.setTcpMd5Sig(addr, null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,7 +66,7 @@ final class TcpMd5Util {
|
||||
// Set new mappings and store addresses which we set.
|
||||
final Collection<InetAddress> addresses = new ArrayList<InetAddress>(newKeys.size());
|
||||
for (Entry<InetAddress, byte[]> e : newKeys.entrySet()) {
|
||||
Native.setTcpMd5Sig(channel.fd().intValue(), e.getKey(), e.getValue());
|
||||
channel.socket.setTcpMd5Sig(e.getKey(), e.getValue());
|
||||
addresses.add(e.getKey());
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,6 @@ public class EpollAbstractDomainSocketEchoTest extends EpollDomainSocketEchoTest
|
||||
@Override
|
||||
protected SocketAddress newSocketAddress() {
|
||||
// these don't actually show up in the file system so creating a temp file isn't reliable
|
||||
return new DomainSocketAddress("\0/tmp/" + UUID.randomUUID());
|
||||
return new DomainSocketAddress("\0" + System.getProperty("java.io.tmpdir") + UUID.randomUUID());
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,9 @@ import java.net.InetSocketAddress;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.util.Random;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
@ -37,9 +39,18 @@ public class EpollSocketChannelConfigTest {
|
||||
private static Random rand;
|
||||
|
||||
@BeforeClass
|
||||
public static void before() {
|
||||
public static void beforeClass() {
|
||||
rand = new Random();
|
||||
group = new EpollEventLoopGroup(1);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClass() {
|
||||
group.shutdownGracefully();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
Bootstrap bootstrap = new Bootstrap();
|
||||
ch = (EpollSocketChannel) bootstrap.group(group)
|
||||
.channel(EpollSocketChannel.class)
|
||||
@ -47,13 +58,9 @@ public class EpollSocketChannelConfigTest {
|
||||
.bind(new InetSocketAddress(0)).syncUninterruptibly().channel();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void after() {
|
||||
try {
|
||||
ch.close().syncUninterruptibly();
|
||||
} finally {
|
||||
group.shutdownGracefully();
|
||||
}
|
||||
@After
|
||||
public void teardown() {
|
||||
ch.close().syncUninterruptibly();
|
||||
}
|
||||
|
||||
private long randLong(long min, long max) {
|
||||
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.epoll;
|
||||
|
||||
import io.netty.channel.unix.DomainSocketAddress;
|
||||
import io.netty.channel.unix.PeerCredentials;
|
||||
import io.netty.channel.unix.tests.SocketTest;
|
||||
import io.netty.channel.unix.tests.UnixTestUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class EpollSocketTest extends SocketTest<LinuxSocket> {
|
||||
@Test
|
||||
public void testTcpCork() throws Exception {
|
||||
assertFalse(socket.isTcpCork());
|
||||
socket.setTcpCork(true);
|
||||
assertTrue(socket.isTcpCork());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPeerCreds() throws IOException {
|
||||
LinuxSocket s1 = LinuxSocket.newSocketDomain();
|
||||
LinuxSocket s2 = LinuxSocket.newSocketDomain();
|
||||
|
||||
try {
|
||||
DomainSocketAddress dsa = UnixTestUtils.newSocketAddress();
|
||||
s1.bind(dsa);
|
||||
s1.listen(1);
|
||||
|
||||
assertTrue(s2.connect(dsa));
|
||||
byte [] addr = new byte[64];
|
||||
s1.accept(addr);
|
||||
PeerCredentials pc = s1.getPeerCredentials();
|
||||
assertNotEquals(pc.uid(), -1);
|
||||
} finally {
|
||||
s1.close();
|
||||
s2.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LinuxSocket newSocket() {
|
||||
return LinuxSocket.newSocketStream();
|
||||
}
|
||||
}
|
@ -25,6 +25,7 @@ import io.netty.channel.socket.nio.NioDatagramChannel;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.channel.unix.DomainSocketAddress;
|
||||
import io.netty.channel.unix.tests.UnixTestUtils;
|
||||
import io.netty.testsuite.transport.TestsuitePermutation;
|
||||
import io.netty.testsuite.transport.TestsuitePermutation.BootstrapFactory;
|
||||
import io.netty.testsuite.transport.socket.SocketTestPermutation;
|
||||
@ -214,12 +215,6 @@ class EpollSocketTestPermutation extends SocketTestPermutation {
|
||||
}
|
||||
|
||||
public static DomainSocketAddress newSocketAddress() {
|
||||
try {
|
||||
File file = File.createTempFile("netty", "dsocket");
|
||||
file.delete();
|
||||
return new DomainSocketAddress(file);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
return UnixTestUtils.newSocketAddress();
|
||||
}
|
||||
}
|
||||
|
197
transport-native-kqueue/pom.xml
Normal file
197
transport-native-kqueue/pom.xml
Normal file
@ -0,0 +1,197 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ 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:
|
||||
~
|
||||
~ 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.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-parent</artifactId>
|
||||
<version>4.1.11.Final-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>netty-transport-native-kqueue</artifactId>
|
||||
|
||||
<name>Netty/Transport/Native/KQueue</name>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>osx</id>
|
||||
<activation>
|
||||
<os>
|
||||
<family>mac</family>
|
||||
</os>
|
||||
</activation>
|
||||
<properties>
|
||||
<jni.compiler.args.ldflags>LDFLAGS=-Wl,-weak_library,${unix.common.lib.unpacked.dir}/lib${unix.common.lib.name}.a</jni.compiler.args.ldflags>
|
||||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<properties>
|
||||
<!-- Needed by the native transport as we need the memoryAddress of the ByteBuffer -->
|
||||
<argLine.java9.extras>--add-exports java.base/sun.security.x509=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED</argLine.java9.extras>
|
||||
<unix.common.lib.name>netty-unix-common</unix.common.lib.name>
|
||||
<unix.common.lib.dir>${project.build.directory}/unix-common-lib</unix.common.lib.dir>
|
||||
<unix.common.lib.unpacked.dir>${unix.common.lib.dir}/META-INF/native/lib</unix.common.lib.unpacked.dir>
|
||||
<unix.common.include.unpacked.dir>${unix.common.lib.dir}/META-INF/native/include</unix.common.include.unpacked.dir>
|
||||
<jni.compiler.args.cflags>CFLAGS=-O3 -Werror -fno-omit-frame-pointer -Wunused-variable -I${unix.common.include.unpacked.dir}</jni.compiler.args.cflags>
|
||||
<jni.compiler.args.ldflags>LDFLAGS=-L${unix.common.lib.unpacked.dir} -Wl,--whole-archive -l${unix.common.lib.name} -Wl,--no-whole-archive</jni.compiler.args.ldflags>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-buffer</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-transport-native-unix-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<classifier>${jni.classifier}</classifier>
|
||||
<!--
|
||||
The unix-common with classifier dependency is optional because it is not a runtime dependency, but a build time
|
||||
dependency to get the static library which is built directly into the shared library generated by this project.
|
||||
-->
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-transport-native-unix-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-transport</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-testsuite</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-transport-native-unix-common-tests</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>${tcnative.artifactId}</artifactId>
|
||||
<classifier>${tcnative.classifier}</classifier>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<!-- unpack the unix-common static library and include files -->
|
||||
<execution>
|
||||
<id>unpack</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>unpack-dependencies</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<includeGroupIds>${project.groupId}</includeGroupIds>
|
||||
<includeArtifactIds>netty-transport-native-unix-common</includeArtifactIds>
|
||||
<classifier>${jni.classifier}</classifier>
|
||||
<outputDirectory>${unix.common.lib.dir}</outputDirectory>
|
||||
<includes>META-INF/native/**</includes>
|
||||
<overWriteReleases>false</overWriteReleases>
|
||||
<overWriteSnapshots>true</overWriteSnapshots>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.fusesource.hawtjni</groupId>
|
||||
<artifactId>maven-hawtjni-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>build-native-lib</id>
|
||||
<configuration>
|
||||
<nativeSourceDirectory>${project.basedir}/src/main/c</nativeSourceDirectory>
|
||||
<libDirectory>${project.build.outputDirectory}</libDirectory>
|
||||
<!-- We use Maven's artifact classifier instead.
|
||||
This hack will make the hawtjni plugin to put the native library
|
||||
under 'META-INF/native' rather than 'META-INF/native/${platform}'. -->
|
||||
<platform>.</platform>
|
||||
<forceConfigure>true</forceConfigure>
|
||||
<forceAutogen>true</forceAutogen>
|
||||
<configureArgs>
|
||||
<arg>${jni.compiler.args.ldflags}</arg>
|
||||
<arg>${jni.compiler.args.cflags}</arg>
|
||||
</configureArgs>
|
||||
</configuration>
|
||||
<goals>
|
||||
<goal>generate</goal>
|
||||
<goal>build</goal>
|
||||
</goals>
|
||||
<phase>compile</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<executions>
|
||||
<!-- Generate the fallback JAR that does not contain the native library. -->
|
||||
<execution>
|
||||
<id>default-jar</id>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>META-INF/native/**</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</execution>
|
||||
<!-- Generate the JAR that contains the native library in it. -->
|
||||
<execution>
|
||||
<id>native-jar</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
|
||||
</manifest>
|
||||
<manifestEntries>
|
||||
<Bundle-NativeCode>META-INF/native/libnetty-transport-native-kqueue.jnilib; osname=darwin, processor=x86_64"</Bundle-NativeCode>
|
||||
</manifestEntries>
|
||||
<index>true</index>
|
||||
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
|
||||
</archive>
|
||||
<classifier>${jni.classifier}</classifier>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
299
transport-native-kqueue/src/main/c/netty_kqueue_bsdsocket.c
Normal file
299
transport-native-kqueue/src/main/c/netty_kqueue_bsdsocket.c
Normal file
@ -0,0 +1,299 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/ucred.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include "netty_unix_filedescriptor.h"
|
||||
#include "netty_unix_socket.h"
|
||||
#include "netty_unix_errors.h"
|
||||
#include "netty_unix_util.h"
|
||||
#include "netty_kqueue_bsdsocket.h"
|
||||
|
||||
// Those are initialized in the init(...) method and cached for performance reasons
|
||||
static jclass stringCls = NULL;
|
||||
static jclass peerCredentialsClass = NULL;
|
||||
static jfieldID fileChannelFieldId = NULL;
|
||||
static jfieldID transferredFieldId = NULL;
|
||||
static jfieldID fdFieldId = NULL;
|
||||
static jfieldID fileDescriptorFieldId = NULL;
|
||||
static jmethodID peerCredentialsMethodId = NULL;
|
||||
|
||||
// JNI Registered Methods Begin
|
||||
static jlong netty_kqueue_bsdsocket_sendFile(JNIEnv* env, jclass clazz, jint socketFd, 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;
|
||||
}
|
||||
const jlong lenBefore = len;
|
||||
off_t sbytes;
|
||||
int res, err;
|
||||
do {
|
||||
#ifdef __APPLE__
|
||||
// sbytes is an input (how many to write) and output (how many were written) parameter.
|
||||
sbytes = len;
|
||||
res = sendfile(srcFd, socketFd, base_off + off, &sbytes, NULL, 0);
|
||||
#else
|
||||
sbytes = 0;
|
||||
res = sendfile(srcFd, socketFd, base_off + off, len, NULL, &sbytes, 0);
|
||||
#endif
|
||||
len -= sbytes;
|
||||
} while (res < 0 && ((err = errno) == EINTR));
|
||||
sbytes = lenBefore - len;
|
||||
if (sbytes > 0) {
|
||||
// update the transferred field in DefaultFileRegion
|
||||
(*env)->SetLongField(env, fileRegion, transferredFieldId, off + sbytes);
|
||||
return sbytes;
|
||||
}
|
||||
return res < 0 ? -err : 0;
|
||||
}
|
||||
|
||||
static void netty_kqueue_bsdsocket_setAcceptFilter(JNIEnv* env, jclass clazz, jint fd, jstring afName, jstring afArg) {
|
||||
#ifdef SO_ACCEPTFILTER
|
||||
struct accept_filter_arg af;
|
||||
const char* tmpString = NULL;
|
||||
af.af_name[0] = af.af_arg[0] ='\0';
|
||||
|
||||
tmpString = (*env)->GetStringUTFChars(env, afName, NULL);
|
||||
strncat(af.af_name, tmpString, sizeof(af.af_name) / sizeof(af.af_name[0]));
|
||||
(*env)->ReleaseStringUTFChars(env, afName, tmpString);
|
||||
|
||||
tmpString = (*env)->GetStringUTFChars(env, afArg, NULL);
|
||||
strncat(af.af_arg, tmpString, sizeof(af.af_arg) / sizeof(af.af_arg[0]));
|
||||
(*env)->ReleaseStringUTFChars(env, afArg, tmpString);
|
||||
|
||||
netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_ACCEPTFILTER, &af, sizeof(af));
|
||||
#else // No know replacement on MacOS
|
||||
netty_unix_errors_throwChannelExceptionErrorNo(env, "setsockopt() failed: ", EINVAL);
|
||||
#endif
|
||||
}
|
||||
|
||||
static jobjectArray netty_kqueue_bsdsocket_getAcceptFilter(JNIEnv* env, jclass clazz, jint fd) {
|
||||
#ifdef SO_ACCEPTFILTER
|
||||
struct accept_filter_arg af;
|
||||
if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_ACCEPTFILTER, &af, sizeof(af)) == -1) {
|
||||
netty_unix_errors_throwChannelExceptionErrorNo(env, "getsockopt() failed: ", errno);
|
||||
return NULL;
|
||||
}
|
||||
jobjectArray resultArray = (*env)->NewObjectArray(env, 2, stringCls, NULL);
|
||||
(*env)->SetObjectArrayElement(env, resultArray, 0, (*env)->NewStringUTF(env, &af.af_name[0]));
|
||||
(*env)->SetObjectArrayElement(env, resultArray, 1, (*env)->NewStringUTF(env, &af.af_arg[0]));
|
||||
return resultArray;
|
||||
#else // No know replacement on MacOS
|
||||
// Don't throw here because this is used when getting a list of all options.
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void netty_kqueue_bsdsocket_setTcpNoPush(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
||||
netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_NOPUSH, &optval, sizeof(optval));
|
||||
}
|
||||
|
||||
static void netty_kqueue_bsdsocket_setSndLowAt(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
||||
netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_SNDLOWAT, &optval, sizeof(optval));
|
||||
}
|
||||
|
||||
static jint netty_kqueue_bsdsocket_getTcpNoPush(JNIEnv* env, jclass clazz, jint fd) {
|
||||
int optval;
|
||||
if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_NOPUSH, &optval, sizeof(optval)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return optval;
|
||||
}
|
||||
|
||||
static jint netty_kqueue_bsdsocket_getSndLowAt(JNIEnv* env, jclass clazz, jint fd) {
|
||||
int optval;
|
||||
if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_SNDLOWAT, &optval, sizeof(optval)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return optval;
|
||||
}
|
||||
|
||||
static jobject netty_kqueue_bsdsocket_getPeerCredentials(JNIEnv *env, jclass clazz, jint fd) {
|
||||
struct xucred credentials;
|
||||
// It has been observed on MacOS that this method can complete successfully but not set all fields of xucred.
|
||||
credentials.cr_ngroups = 0;
|
||||
if(netty_unix_socket_getOption(env,fd, SOL_SOCKET, LOCAL_PEERCRED, &credentials, sizeof (credentials)) == -1) {
|
||||
return NULL;
|
||||
}
|
||||
jintArray gids = NULL;
|
||||
if (credentials.cr_ngroups > 1) {
|
||||
gids = (*env)->NewIntArray(env, credentials.cr_ngroups);
|
||||
(*env)->SetIntArrayRegion(env, gids, 0, credentials.cr_ngroups, (jint*) credentials.cr_groups);
|
||||
} else {
|
||||
// It has been observed on MacOS that cr_ngroups may not be set, but the cr_gid field is set.
|
||||
gids = (*env)->NewIntArray(env, 1);
|
||||
(*env)->SetIntArrayRegion(env, gids, 0, 1, (jint*) &credentials.cr_gid);
|
||||
}
|
||||
|
||||
// TODO: getting the PID may require reading/sending "ancillary data" via SCM_CREDENTIALS which is not desirable.
|
||||
return (*env)->NewObject(env, peerCredentialsClass, peerCredentialsMethodId, 0, credentials.cr_uid, gids);
|
||||
}
|
||||
// JNI Registered Methods End
|
||||
|
||||
// JNI Method Registration Table Begin
|
||||
static const JNINativeMethod fixed_method_table[] = {
|
||||
{ "setAcceptFilter", "(ILjava/lang/String;Ljava/lang/String;)V", (void *) netty_kqueue_bsdsocket_setAcceptFilter },
|
||||
{ "setTcpNoPush", "(II)V", (void *) netty_kqueue_bsdsocket_setTcpNoPush },
|
||||
{ "setSndLowAt", "(II)V", (void *) netty_kqueue_bsdsocket_setSndLowAt },
|
||||
{ "getAcceptFilter", "(I)[Ljava/lang/String;", (void *) netty_kqueue_bsdsocket_getAcceptFilter },
|
||||
{ "getTcpNoPush", "(I)I", (void *) netty_kqueue_bsdsocket_getTcpNoPush },
|
||||
{ "getSndLowAt", "(I)I", (void *) netty_kqueue_bsdsocket_getSndLowAt }
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static JNINativeMethod* createDynamicMethodsTable(const char* packagePrefix) {
|
||||
JNINativeMethod* dynamicMethods = malloc(sizeof(JNINativeMethod) * dynamicMethodsTableSize());
|
||||
memcpy(dynamicMethods, fixed_method_table, sizeof(fixed_method_table));
|
||||
char* dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/DefaultFileRegion;JJJ)J");
|
||||
JNINativeMethod* dynamicMethod = &dynamicMethods[fixed_method_table_size];
|
||||
dynamicMethod->name = "sendFile";
|
||||
dynamicMethod->signature = netty_unix_util_prepend("(IL", dynamicTypeName);
|
||||
dynamicMethod->fnPtr = (void *) netty_kqueue_bsdsocket_sendFile;
|
||||
free(dynamicTypeName);
|
||||
|
||||
++dynamicMethod;
|
||||
dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/unix/PeerCredentials;");
|
||||
dynamicMethod->name = "getPeerCredentials";
|
||||
dynamicMethod->signature = netty_unix_util_prepend("(I)L", dynamicTypeName);
|
||||
dynamicMethod->fnPtr = (void *) netty_kqueue_bsdsocket_getPeerCredentials;
|
||||
free(dynamicTypeName);
|
||||
|
||||
return dynamicMethods;
|
||||
}
|
||||
|
||||
static void freeDynamicMethodsTable(JNINativeMethod* dynamicMethods) {
|
||||
jint fullMethodTableSize = dynamicMethodsTableSize();
|
||||
jint i = fixed_method_table_size;
|
||||
for (; i < fullMethodTableSize; ++i) {
|
||||
free(dynamicMethods[i].signature);
|
||||
}
|
||||
free(dynamicMethods);
|
||||
}
|
||||
// JNI Method Registration Table End
|
||||
|
||||
jint netty_kqueue_bsdsocket_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) {
|
||||
// Register the methods which are not referenced by static member variables
|
||||
JNINativeMethod* dynamicMethods = createDynamicMethodsTable(packagePrefix);
|
||||
if (netty_unix_util_register_natives(env,
|
||||
packagePrefix,
|
||||
"io/netty/channel/kqueue/BsdSocket",
|
||||
dynamicMethods,
|
||||
dynamicMethodsTableSize()) != 0) {
|
||||
freeDynamicMethodsTable(dynamicMethods);
|
||||
return JNI_ERR;
|
||||
}
|
||||
freeDynamicMethodsTable(dynamicMethods);
|
||||
dynamicMethods = NULL;
|
||||
|
||||
// Initialize this module
|
||||
char* nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/DefaultFileRegion");
|
||||
jclass fileRegionCls = (*env)->FindClass(env, nettyClassName);
|
||||
free(nettyClassName);
|
||||
nettyClassName = NULL;
|
||||
if (fileRegionCls == NULL) {
|
||||
return JNI_ERR;
|
||||
}
|
||||
fileChannelFieldId = (*env)->GetFieldID(env, fileRegionCls, "file", "Ljava/nio/channels/FileChannel;");
|
||||
if (fileChannelFieldId == NULL) {
|
||||
netty_unix_errors_throwRuntimeException(env, "failed to get field ID: DefaultFileRegion.file");
|
||||
return JNI_ERR;
|
||||
}
|
||||
transferredFieldId = (*env)->GetFieldID(env, fileRegionCls, "transferred", "J");
|
||||
if (transferredFieldId == NULL) {
|
||||
netty_unix_errors_throwRuntimeException(env, "failed to get field ID: DefaultFileRegion.transferred");
|
||||
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) {
|
||||
netty_unix_errors_throwRuntimeException(env, "failed to get field ID: FileChannelImpl.fd");
|
||||
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) {
|
||||
netty_unix_errors_throwRuntimeException(env, "failed to get field ID: FileDescriptor.fd");
|
||||
return JNI_ERR;
|
||||
}
|
||||
stringCls = (*env)->FindClass(env, "java/lang/String");
|
||||
if (stringCls == NULL) {
|
||||
// pending exception...
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/unix/PeerCredentials");
|
||||
jclass localPeerCredsClass = (*env)->FindClass(env, nettyClassName);
|
||||
free(nettyClassName);
|
||||
nettyClassName = NULL;
|
||||
if (localPeerCredsClass == NULL) {
|
||||
// pending exception...
|
||||
return JNI_ERR;
|
||||
}
|
||||
peerCredentialsClass = (jclass) (*env)->NewGlobalRef(env, localPeerCredsClass);
|
||||
if (peerCredentialsClass == NULL) {
|
||||
// out-of-memory!
|
||||
netty_unix_errors_throwOutOfMemoryError(env);
|
||||
return JNI_ERR;
|
||||
}
|
||||
peerCredentialsMethodId = (*env)->GetMethodID(env, peerCredentialsClass, "<init>", "(II[I)V");
|
||||
if (peerCredentialsMethodId == NULL) {
|
||||
netty_unix_errors_throwRuntimeException(env, "failed to get method ID: PeerCredentials.<init>(int, int, int[])");
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
|
||||
void netty_kqueue_bsdsocket_JNI_OnUnLoad(JNIEnv* env) {
|
||||
if (peerCredentialsClass != NULL) {
|
||||
(*env)->DeleteGlobalRef(env, peerCredentialsClass);
|
||||
peerCredentialsClass = NULL;
|
||||
}
|
||||
}
|
25
transport-native-kqueue/src/main/c/netty_kqueue_bsdsocket.h
Normal file
25
transport-native-kqueue/src/main/c/netty_kqueue_bsdsocket.h
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef NETTY_KQUEUE_BSDSOCKET_H_
|
||||
#define NETTY_KQUEUE_BSDSOCKET_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_kqueue_bsdsocket_JNI_OnLoad(JNIEnv* env, const char* packagePrefix);
|
||||
void netty_kqueue_bsdsocket_JNI_OnUnLoad(JNIEnv* env);
|
||||
|
||||
#endif /* NETTY_KQUEUE_BSDSOCKET_H_ */
|
124
transport-native-kqueue/src/main/c/netty_kqueue_eventarray.c
Normal file
124
transport-native-kqueue/src/main/c/netty_kqueue_eventarray.c
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/event.h>
|
||||
#include <sys/time.h>
|
||||
#include "netty_unix_util.h"
|
||||
#include "netty_unix_errors.h"
|
||||
#include "netty_kqueue_eventarray.h"
|
||||
|
||||
jfieldID kqueueJniPtrFieldId = NULL;
|
||||
|
||||
static void netty_kqueue_eventarray_evSet(JNIEnv* env, jclass clzz, jlong keventAddress, jobject channel, jint ident, jshort filter, jshort flags, jint fflags) {
|
||||
// Create a global pointer, cast it as a long, and retain it in java to re-use and free later.
|
||||
jlong jniSelfPtr = (*env)->GetLongField(env, channel, kqueueJniPtrFieldId);
|
||||
if (jniSelfPtr == 0) {
|
||||
jniSelfPtr = (jlong) (*env)->NewGlobalRef(env, channel);
|
||||
(*env)->SetLongField(env, channel, kqueueJniPtrFieldId, jniSelfPtr);
|
||||
}
|
||||
EV_SET((struct kevent*) keventAddress, ident, filter, flags, fflags, 0, (jobject) jniSelfPtr);
|
||||
}
|
||||
|
||||
static jobject netty_kqueue_eventarray_getChannel(JNIEnv* env, jclass clazz, jlong keventAddress) {
|
||||
struct kevent* event = (struct kevent*) keventAddress;
|
||||
return event->udata == NULL ? NULL : (jobject) event->udata;
|
||||
}
|
||||
|
||||
static void netty_kqueue_eventarray_deleteGlobalRefs(JNIEnv* env, jclass clazz, jlong channelAddressStart, jlong channelAddressEnd) {
|
||||
// Iterate over an array of longs, which are really pointers to the jobject NewGlobalRef created above in evSet
|
||||
// and delete each one. The field has already been set to 0 in java.
|
||||
jlong* itr = (jlong*) channelAddressStart;
|
||||
const jlong* end = (jlong*) channelAddressEnd;
|
||||
for (; itr != end; ++itr) {
|
||||
(*env)->DeleteGlobalRef(env, (jobject) *itr);
|
||||
}
|
||||
}
|
||||
|
||||
// JNI Method Registration Table Begin
|
||||
static const JNINativeMethod fixed_method_table[] = {
|
||||
{ "deleteGlobalRefs", "(JJ)V", (void *) netty_kqueue_eventarray_deleteGlobalRefs }
|
||||
// "evSet" has a dynamic signature
|
||||
// "getChannel" 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;
|
||||
}
|
||||
|
||||
static JNINativeMethod* createDynamicMethodsTable(const char* packagePrefix) {
|
||||
JNINativeMethod* dynamicMethods = malloc(sizeof(JNINativeMethod) * dynamicMethodsTableSize());
|
||||
memcpy(dynamicMethods, fixed_method_table, sizeof(fixed_method_table));
|
||||
char* dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/kqueue/AbstractKQueueChannel;ISSI)V");
|
||||
JNINativeMethod* dynamicMethod = &dynamicMethods[fixed_method_table_size];
|
||||
dynamicMethod->name = "evSet";
|
||||
dynamicMethod->signature = netty_unix_util_prepend("(JL", dynamicTypeName);
|
||||
dynamicMethod->fnPtr = (void *) netty_kqueue_eventarray_evSet;
|
||||
free(dynamicTypeName);
|
||||
|
||||
++dynamicMethod;
|
||||
dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/kqueue/AbstractKQueueChannel;");
|
||||
dynamicMethod->name = "getChannel";
|
||||
dynamicMethod->signature = netty_unix_util_prepend("(J)L", dynamicTypeName);
|
||||
dynamicMethod->fnPtr = (void *) netty_kqueue_eventarray_getChannel;
|
||||
free(dynamicTypeName);
|
||||
return dynamicMethods;
|
||||
}
|
||||
|
||||
static void freeDynamicMethodsTable(JNINativeMethod* dynamicMethods) {
|
||||
jint fullMethodTableSize = dynamicMethodsTableSize();
|
||||
jint i = fixed_method_table_size;
|
||||
for (; i < fullMethodTableSize; ++i) {
|
||||
free(dynamicMethods[i].signature);
|
||||
}
|
||||
free(dynamicMethods);
|
||||
}
|
||||
// JNI Method Registration Table End
|
||||
|
||||
jint netty_kqueue_eventarray_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) {
|
||||
JNINativeMethod* dynamicMethods = createDynamicMethodsTable(packagePrefix);
|
||||
if (netty_unix_util_register_natives(env,
|
||||
packagePrefix,
|
||||
"io/netty/channel/kqueue/KQueueEventArray",
|
||||
dynamicMethods,
|
||||
dynamicMethodsTableSize()) != 0) {
|
||||
freeDynamicMethodsTable(dynamicMethods);
|
||||
return JNI_ERR;
|
||||
}
|
||||
freeDynamicMethodsTable(dynamicMethods);
|
||||
dynamicMethods = NULL;
|
||||
|
||||
char* nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/kqueue/AbstractKQueueChannel");
|
||||
jclass kqueueChannelCls = (*env)->FindClass(env, nettyClassName);
|
||||
free(nettyClassName);
|
||||
nettyClassName = NULL;
|
||||
if (kqueueChannelCls == NULL) {
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
kqueueJniPtrFieldId = (*env)->GetFieldID(env, kqueueChannelCls, "jniSelfPtr", "J");
|
||||
if (kqueueJniPtrFieldId == NULL) {
|
||||
netty_unix_errors_throwRuntimeException(env, "failed to get field ID: AbstractKQueueChannel.jniSelfPtr");
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
|
||||
void netty_kqueue_eventarray_JNI_OnUnLoad(JNIEnv* env) {
|
||||
}
|
25
transport-native-kqueue/src/main/c/netty_kqueue_eventarray.h
Normal file
25
transport-native-kqueue/src/main/c/netty_kqueue_eventarray.h
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef NETTY_KQUEUE_EVENTARRAY_H_
|
||||
#define NETTY_KQUEUE_EVENTARRAY_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_kqueue_eventarray_JNI_OnLoad(JNIEnv* env, const char* packagePrefix);
|
||||
void netty_kqueue_eventarray_JNI_OnUnLoad(JNIEnv* env);
|
||||
|
||||
#endif /* NETTY_KQUEUE_EVENTARRAY_H_ */
|
307
transport-native-kqueue/src/main/c/netty_kqueue_native.c
Normal file
307
transport-native-kqueue/src/main/c/netty_kqueue_native.c
Normal file
@ -0,0 +1,307 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stddef.h>
|
||||
#include <time.h>
|
||||
#include <sys/event.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include "netty_unix_filedescriptor.h"
|
||||
#include "netty_unix_socket.h"
|
||||
#include "netty_unix_errors.h"
|
||||
#include "netty_unix_util.h"
|
||||
#include "netty_unix_limits.h"
|
||||
#include "netty_kqueue_bsdsocket.h"
|
||||
#include "netty_kqueue_eventarray.h"
|
||||
|
||||
clockid_t waitClockId = 0; // initialized by netty_unix_util_initialize_wait_clock
|
||||
|
||||
static jint netty_kqueue_native_kqueueCreate(JNIEnv* env, jclass clazz) {
|
||||
jint kq = kqueue();
|
||||
if (kq < 0) {
|
||||
netty_unix_errors_throwChannelExceptionErrorNo(env, "kqueue() failed: ", errno);
|
||||
}
|
||||
return kq;
|
||||
}
|
||||
|
||||
static jint netty_kqueue_native_keventChangeSingleUserEvent(jint kqueueFd, struct kevent* userEvent) {
|
||||
int result, err;
|
||||
result = kevent(kqueueFd, userEvent, 1, NULL, 0, NULL);
|
||||
if (result < 0) {
|
||||
// https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
|
||||
// When kevent() call fails with EINTR error, all changes in the changelist have been applied.
|
||||
return (err = errno) == EINTR ? 0 : -err;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static jint netty_kqueue_native_keventTriggerUserEvent(JNIEnv* env, jclass clazz, jint kqueueFd, jint ident) {
|
||||
struct kevent userEvent;
|
||||
EV_SET(&userEvent, ident, EVFILT_USER, 0, NOTE_TRIGGER | NOTE_FFNOP, 0, NULL);
|
||||
return netty_kqueue_native_keventChangeSingleUserEvent(kqueueFd, &userEvent);
|
||||
}
|
||||
|
||||
static jint netty_kqueue_native_keventAddUserEvent(JNIEnv* env, jclass clazz, jint kqueueFd, jint ident) {
|
||||
struct kevent userEvent;
|
||||
EV_SET(&userEvent, ident, EVFILT_USER, EV_ADD | EV_ENABLE | EV_CLEAR, NOTE_FFNOP, 0, NULL);
|
||||
return netty_kqueue_native_keventChangeSingleUserEvent(kqueueFd, &userEvent);
|
||||
}
|
||||
|
||||
static jint netty_kqueue_native_keventWait(JNIEnv* env, jclass clazz, jint kqueueFd, jlong changeListAddress, jint changeListLength,
|
||||
jlong eventListAddress, jint eventListLength, jint tvSec, jint tvNsec) {
|
||||
struct kevent* changeList = (struct kevent*) changeListAddress;
|
||||
struct kevent* eventList = (struct kevent*) eventListAddress;
|
||||
struct timespec beforeTs, nowTs, timeoutTs;
|
||||
int result, err;
|
||||
|
||||
timeoutTs.tv_sec = tvSec;
|
||||
timeoutTs.tv_nsec = tvNsec;
|
||||
|
||||
// Negatives = wait indefinitely, Zeros = poll (aka return immediately).
|
||||
if ((tvSec == 0 && tvNsec == 0) || tvSec < 0 || tvNsec < 0) {
|
||||
const struct timespec* fixedTs = (tvSec == 0 && tvNsec == 0) ? &timeoutTs : NULL;
|
||||
for (;;) {
|
||||
result = kevent(kqueueFd, changeList, changeListLength, eventList, eventListLength, fixedTs);
|
||||
if (result >= 0) {
|
||||
return result;
|
||||
}
|
||||
if ((err = errno) != EINTR) {
|
||||
return -err;
|
||||
}
|
||||
|
||||
// https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
|
||||
// When kevent() call fails with EINTR error, all changes in the changelist have been applied.
|
||||
changeListLength = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait with a timeout value.
|
||||
netty_unix_util_clock_gettime(waitClockId, &beforeTs);
|
||||
for (;;) {
|
||||
result = kevent(kqueueFd, changeList, changeListLength, eventList, eventListLength, &timeoutTs);
|
||||
if (result >= 0) {
|
||||
return result;
|
||||
}
|
||||
if ((err = errno) != EINTR) {
|
||||
return -err;
|
||||
}
|
||||
|
||||
netty_unix_util_clock_gettime(waitClockId, &nowTs);
|
||||
// beforeTs will store the time difference to check for overflow
|
||||
beforeTs.tv_sec = nowTs.tv_sec - beforeTs.tv_sec;
|
||||
beforeTs.tv_nsec = nowTs.tv_nsec - beforeTs.tv_nsec;
|
||||
// Now subtract the time difference
|
||||
timeoutTs.tv_sec -= beforeTs.tv_sec;
|
||||
timeoutTs.tv_nsec -= beforeTs.tv_nsec;
|
||||
if (beforeTs.tv_sec < 0 || beforeTs.tv_nsec < 0 || (timeoutTs.tv_sec <= 0 && timeoutTs.tv_nsec <= 0)) {
|
||||
return 0;
|
||||
}
|
||||
beforeTs = nowTs;
|
||||
// https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
|
||||
// When kevent() call fails with EINTR error, all changes in the changelist have been applied.
|
||||
changeListLength = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static jint netty_kqueue_native_sizeofKEvent(JNIEnv* env, jclass clazz) {
|
||||
return sizeof(struct kevent);
|
||||
}
|
||||
|
||||
static jint netty_kqueue_native_offsetofKEventIdent(JNIEnv* env, jclass clazz) {
|
||||
return offsetof(struct kevent, ident);
|
||||
}
|
||||
|
||||
static jint netty_kqueue_native_offsetofKEventFlags(JNIEnv* env, jclass clazz) {
|
||||
return offsetof(struct kevent, flags);
|
||||
}
|
||||
|
||||
static jint netty_kqueue_native_offsetofKEventFilter(JNIEnv* env, jclass clazz) {
|
||||
return offsetof(struct kevent, filter);
|
||||
}
|
||||
|
||||
static jint netty_kqueue_native_offsetofKEventFFlags(JNIEnv* env, jclass clazz) {
|
||||
return offsetof(struct kevent, fflags);
|
||||
}
|
||||
|
||||
static jint netty_kqueue_native_offsetofKeventData(JNIEnv* env, jclass clazz) {
|
||||
return offsetof(struct kevent, data);
|
||||
}
|
||||
|
||||
static jshort netty_kqueue_native_evfiltRead(JNIEnv* env, jclass clazz) {
|
||||
return EVFILT_READ;
|
||||
}
|
||||
|
||||
static jshort netty_kqueue_native_evfiltWrite(JNIEnv* env, jclass clazz) {
|
||||
return EVFILT_WRITE;
|
||||
}
|
||||
|
||||
static jshort netty_kqueue_native_evfiltUser(JNIEnv* env, jclass clazz) {
|
||||
return EVFILT_USER;
|
||||
}
|
||||
|
||||
static jshort netty_kqueue_native_evAdd(JNIEnv* env, jclass clazz) {
|
||||
return EV_ADD;
|
||||
}
|
||||
|
||||
static jshort netty_kqueue_native_evEnable(JNIEnv* env, jclass clazz) {
|
||||
return EV_ENABLE;
|
||||
}
|
||||
|
||||
static jshort netty_kqueue_native_evDisable(JNIEnv* env, jclass clazz) {
|
||||
return EV_DISABLE;
|
||||
}
|
||||
|
||||
static jshort netty_kqueue_native_evDelete(JNIEnv* env, jclass clazz) {
|
||||
return EV_DELETE;
|
||||
}
|
||||
|
||||
static jshort netty_kqueue_native_evClear(JNIEnv* env, jclass clazz) {
|
||||
return EV_CLEAR;
|
||||
}
|
||||
|
||||
static jshort netty_kqueue_native_evEOF(JNIEnv* env, jclass clazz) {
|
||||
return EV_EOF;
|
||||
}
|
||||
|
||||
static jshort netty_kqueue_native_evError(JNIEnv* env, jclass clazz) {
|
||||
return EV_ERROR;
|
||||
}
|
||||
|
||||
// JNI Method Registration Table Begin
|
||||
static const JNINativeMethod statically_referenced_fixed_method_table[] = {
|
||||
{ "evfiltRead", "()S", (void *) netty_kqueue_native_evfiltRead },
|
||||
{ "evfiltWrite", "()S", (void *) netty_kqueue_native_evfiltWrite },
|
||||
{ "evfiltUser", "()S", (void *) netty_kqueue_native_evfiltUser },
|
||||
{ "evAdd", "()S", (void *) netty_kqueue_native_evAdd },
|
||||
{ "evEnable", "()S", (void *) netty_kqueue_native_evEnable },
|
||||
{ "evDisable", "()S", (void *) netty_kqueue_native_evDisable },
|
||||
{ "evDelete", "()S", (void *) netty_kqueue_native_evDelete },
|
||||
{ "evClear", "()S", (void *) netty_kqueue_native_evClear },
|
||||
{ "evEOF", "()S", (void *) netty_kqueue_native_evEOF },
|
||||
{ "evError", "()S", (void *) netty_kqueue_native_evError }
|
||||
};
|
||||
static const jint statically_referenced_fixed_method_table_size = sizeof(statically_referenced_fixed_method_table) / sizeof(statically_referenced_fixed_method_table[0]);
|
||||
static const JNINativeMethod fixed_method_table[] = {
|
||||
{ "kqueueCreate", "()I", (void *) netty_kqueue_native_kqueueCreate },
|
||||
{ "keventTriggerUserEvent", "(II)I", (void *) netty_kqueue_native_keventTriggerUserEvent },
|
||||
{ "keventAddUserEvent", "(II)I", (void *) netty_kqueue_native_keventAddUserEvent },
|
||||
{ "keventWait", "(IJIJIII)I", (void *) netty_kqueue_native_keventWait },
|
||||
{ "sizeofKEvent", "()I", (void *) netty_kqueue_native_sizeofKEvent },
|
||||
{ "offsetofKEventIdent", "()I", (void *) netty_kqueue_native_offsetofKEventIdent },
|
||||
{ "offsetofKEventFlags", "()I", (void *) netty_kqueue_native_offsetofKEventFlags },
|
||||
{ "offsetofKEventFFlags", "()I", (void *) netty_kqueue_native_offsetofKEventFFlags },
|
||||
{ "offsetofKEventFilter", "()I", (void *) netty_kqueue_native_offsetofKEventFilter },
|
||||
{ "offsetofKeventData", "()I", (void *) netty_kqueue_native_offsetofKeventData }
|
||||
};
|
||||
static const jint fixed_method_table_size = sizeof(fixed_method_table) / sizeof(fixed_method_table[0]);
|
||||
// JNI Method Registration Table End
|
||||
|
||||
static jint netty_kqueue_native_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) {
|
||||
// We must register the statically referenced methods first!
|
||||
if (netty_unix_util_register_natives(env,
|
||||
packagePrefix,
|
||||
"io/netty/channel/kqueue/KQueueStaticallyReferencedJniMethods",
|
||||
statically_referenced_fixed_method_table,
|
||||
statically_referenced_fixed_method_table_size) != 0) {
|
||||
return JNI_ERR;
|
||||
}
|
||||
// Register the methods which are not referenced by static member variables
|
||||
if (netty_unix_util_register_natives(env, packagePrefix, "io/netty/channel/kqueue/Native", fixed_method_table, fixed_method_table_size) != 0) {
|
||||
return JNI_ERR;
|
||||
}
|
||||
// Load all c modules that we depend upon
|
||||
if (netty_unix_limits_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
|
||||
return JNI_ERR;
|
||||
}
|
||||
if (netty_unix_errors_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
|
||||
return JNI_ERR;
|
||||
}
|
||||
if (netty_unix_filedescriptor_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
|
||||
return JNI_ERR;
|
||||
}
|
||||
if (netty_unix_socket_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
|
||||
return JNI_ERR;
|
||||
}
|
||||
if (netty_kqueue_bsdsocket_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
|
||||
return JNI_ERR;
|
||||
}
|
||||
if (netty_kqueue_eventarray_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
|
||||
return JNI_ERR;
|
||||
}
|
||||
// Initialize this module
|
||||
|
||||
if (!netty_unix_util_initialize_wait_clock(&waitClockId)) {
|
||||
fprintf(stderr, "FATAL: could not find a clock for clock_gettime!\n");
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
|
||||
static void netty_kqueue_native_JNI_OnUnLoad(JNIEnv* env) {
|
||||
netty_unix_limits_JNI_OnUnLoad(env);
|
||||
netty_unix_errors_JNI_OnUnLoad(env);
|
||||
netty_unix_filedescriptor_JNI_OnUnLoad(env);
|
||||
netty_unix_socket_JNI_OnUnLoad(env);
|
||||
netty_kqueue_bsdsocket_JNI_OnUnLoad(env);
|
||||
netty_kqueue_eventarray_JNI_OnUnLoad(env);
|
||||
}
|
||||
|
||||
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||
JNIEnv* env;
|
||||
if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) {
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
Dl_info dlinfo;
|
||||
jint status = 0;
|
||||
// We need to use an address of a function that is uniquely part of this library, so choose a static
|
||||
// function. See https://github.com/netty/netty/issues/4840.
|
||||
if (!dladdr((void*) netty_kqueue_native_JNI_OnUnLoad, &dlinfo)) {
|
||||
fprintf(stderr, "FATAL: transport-native-kqueue JNI call to dladdr failed!\n");
|
||||
return JNI_ERR;
|
||||
}
|
||||
char* packagePrefix = netty_unix_util_parse_package_prefix(dlinfo.dli_fname, "netty-transport-native-kqueue", &status);
|
||||
if (status == JNI_ERR) {
|
||||
fprintf(stderr, "FATAL: transport-native-kqueue JNI encountered unexpected dlinfo.dli_fname: %s\n", dlinfo.dli_fname);
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
jint ret = netty_kqueue_native_JNI_OnLoad(env, packagePrefix);
|
||||
|
||||
if (packagePrefix != NULL) {
|
||||
free(packagePrefix);
|
||||
packagePrefix = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void JNI_OnUnload(JavaVM* vm, void* reserved) {
|
||||
JNIEnv* env;
|
||||
if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) {
|
||||
// Something is wrong but nothing we can do about this :(
|
||||
return;
|
||||
}
|
||||
netty_kqueue_native_JNI_OnUnLoad(env);
|
||||
}
|
@ -0,0 +1,470 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.AbstractChannel;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelConfig;
|
||||
import io.netty.channel.ChannelMetadata;
|
||||
import io.netty.channel.EventLoop;
|
||||
import io.netty.channel.RecvByteBufAllocator;
|
||||
import io.netty.channel.socket.ChannelInputShutdownEvent;
|
||||
import io.netty.channel.socket.ChannelInputShutdownReadComplete;
|
||||
import io.netty.channel.unix.FileDescriptor;
|
||||
import io.netty.channel.unix.UnixChannel;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.NotYetConnectedException;
|
||||
import java.nio.channels.UnresolvedAddressException;
|
||||
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
|
||||
abstract class AbstractKQueueChannel extends AbstractChannel implements UnixChannel {
|
||||
private static final ChannelMetadata METADATA = new ChannelMetadata(false);
|
||||
final BsdSocket socket;
|
||||
private boolean readFilterEnabled = true;
|
||||
private boolean writeFilterEnabled;
|
||||
boolean readReadyRunnablePending;
|
||||
boolean inputClosedSeenErrorOnRead;
|
||||
/**
|
||||
* This member variable means we don't have to have a map in {@link KQueueEventLoop} which associates the FDs
|
||||
* from kqueue to instances of this class. This field will be initialized by JNI when modifying kqueue events.
|
||||
* If there is no global reference when JNI gets a kqueue evSet call (aka this field is 0) then a global reference
|
||||
* will be created and the address will be saved in this member variable. Then when we process a kevent in Java
|
||||
* we can ask JNI to give us the {@link AbstractKQueueChannel} that corresponds to that event.
|
||||
*/
|
||||
long jniSelfPtr;
|
||||
|
||||
protected volatile boolean active;
|
||||
|
||||
AbstractKQueueChannel(Channel parent, BsdSocket fd, boolean active) {
|
||||
this(parent, fd, active, false);
|
||||
}
|
||||
|
||||
AbstractKQueueChannel(Channel parent, BsdSocket fd, boolean active, boolean writeFilterEnabled) {
|
||||
super(parent);
|
||||
socket = checkNotNull(fd, "fd");
|
||||
this.active = active;
|
||||
this.writeFilterEnabled = writeFilterEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final FileDescriptor fd() {
|
||||
return socket;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return active;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelMetadata metadata() {
|
||||
return METADATA;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doClose() throws Exception {
|
||||
active = false;
|
||||
// Even if we allow half closed sockets we should give up on reading. Otherwise we may allow a read attempt on a
|
||||
// socket which has not even been connected yet. This has been observed to block during unit tests.
|
||||
inputClosedSeenErrorOnRead = true;
|
||||
// The FD will be closed, which will take of deleting from kqueue.
|
||||
readFilterEnabled = writeFilterEnabled = false;
|
||||
try {
|
||||
((KQueueEventLoop) eventLoop()).remove(this);
|
||||
} finally {
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doDisconnect() throws Exception {
|
||||
doClose();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isCompatible(EventLoop loop) {
|
||||
return loop instanceof KQueueEventLoop;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return socket.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doDeregister() throws Exception {
|
||||
// Make sure we unregister our filters from kqueue!
|
||||
readFilter(false);
|
||||
writeFilter(false);
|
||||
|
||||
((KQueueEventLoop) eventLoop()).remove(this);
|
||||
|
||||
// Set the filters back to the initial state in case this channel is registered with another event loop.
|
||||
readFilterEnabled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void doBeginRead() throws Exception {
|
||||
// Channel.read() or ChannelHandlerContext.read() was called
|
||||
final AbstractKQueueUnsafe unsafe = (AbstractKQueueUnsafe) unsafe();
|
||||
unsafe.readPending = true;
|
||||
|
||||
// We must set the read flag here as it is possible the user didn't read in the last read loop, the
|
||||
// executeReadReadyRunnable could read nothing, and if the user doesn't explicitly call read they will
|
||||
// never get data after this.
|
||||
readFilter(true);
|
||||
|
||||
// If auto read was toggled off on the last read loop then we may not be notified
|
||||
// again if we didn't consume all the data. So we force a read operation here if there maybe more data.
|
||||
if (unsafe.maybeMoreDataToRead) {
|
||||
unsafe.executeReadReadyRunnable(config());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRegister() throws Exception {
|
||||
// Just in case the previous EventLoop was shutdown abruptly, or an event is still pending on the old EventLoop
|
||||
// make sure the readReadyRunnablePending variable is reset so we will be able to execute the Runnable on the
|
||||
// new EventLoop.
|
||||
readReadyRunnablePending = false;
|
||||
// Add the write event first so we get notified of connection refused on the client side!
|
||||
if (writeFilterEnabled) {
|
||||
evSet0(Native.EVFILT_WRITE, Native.EV_ADD_CLEAR_ENABLE);
|
||||
}
|
||||
if (readFilterEnabled) {
|
||||
evSet0(Native.EVFILT_READ, Native.EV_ADD_CLEAR_ENABLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected abstract AbstractKQueueUnsafe newUnsafe();
|
||||
|
||||
@Override
|
||||
public abstract KQueueChannelConfig config();
|
||||
|
||||
/**
|
||||
* Returns an off-heap copy of the specified {@link ByteBuf}, and releases the original one.
|
||||
*/
|
||||
protected final ByteBuf newDirectBuffer(ByteBuf buf) {
|
||||
return newDirectBuffer(buf, buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an off-heap copy of the specified {@link ByteBuf}, and releases the specified holder.
|
||||
* The caller must ensure that the holder releases the original {@link ByteBuf} when the holder is released by
|
||||
* this method.
|
||||
*/
|
||||
protected final ByteBuf newDirectBuffer(Object holder, ByteBuf buf) {
|
||||
final int readableBytes = buf.readableBytes();
|
||||
if (readableBytes == 0) {
|
||||
ReferenceCountUtil.safeRelease(holder);
|
||||
return Unpooled.EMPTY_BUFFER;
|
||||
}
|
||||
|
||||
final ByteBufAllocator alloc = alloc();
|
||||
if (alloc.isDirectBufferPooled()) {
|
||||
return newDirectBuffer0(holder, buf, alloc, readableBytes);
|
||||
}
|
||||
|
||||
final ByteBuf directBuf = ByteBufUtil.threadLocalDirectBuffer();
|
||||
if (directBuf == null) {
|
||||
return newDirectBuffer0(holder, buf, alloc, readableBytes);
|
||||
}
|
||||
|
||||
directBuf.writeBytes(buf, buf.readerIndex(), readableBytes);
|
||||
ReferenceCountUtil.safeRelease(holder);
|
||||
return directBuf;
|
||||
}
|
||||
|
||||
private static ByteBuf newDirectBuffer0(Object holder, ByteBuf buf, ByteBufAllocator alloc, int capacity) {
|
||||
final ByteBuf directBuf = alloc.directBuffer(capacity);
|
||||
directBuf.writeBytes(buf, buf.readerIndex(), capacity);
|
||||
ReferenceCountUtil.safeRelease(holder);
|
||||
return directBuf;
|
||||
}
|
||||
|
||||
protected static void checkResolvable(InetSocketAddress addr) {
|
||||
if (addr.isUnresolved()) {
|
||||
throw new UnresolvedAddressException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read bytes into the given {@link ByteBuf} and return the amount.
|
||||
*/
|
||||
protected final int doReadBytes(ByteBuf byteBuf) throws Exception {
|
||||
int writerIndex = byteBuf.writerIndex();
|
||||
int localReadAmount;
|
||||
unsafe().recvBufAllocHandle().attemptedBytesRead(byteBuf.writableBytes());
|
||||
if (byteBuf.hasMemoryAddress()) {
|
||||
localReadAmount = socket.readAddress(byteBuf.memoryAddress(), writerIndex, byteBuf.capacity());
|
||||
} else {
|
||||
ByteBuffer buf = byteBuf.internalNioBuffer(writerIndex, byteBuf.writableBytes());
|
||||
localReadAmount = socket.read(buf, buf.position(), buf.limit());
|
||||
}
|
||||
if (localReadAmount > 0) {
|
||||
byteBuf.writerIndex(writerIndex + localReadAmount);
|
||||
}
|
||||
return localReadAmount;
|
||||
}
|
||||
|
||||
protected final int doWriteBytes(ByteBuf buf, int writeSpinCount) throws Exception {
|
||||
int readableBytes = buf.readableBytes();
|
||||
int writtenBytes = 0;
|
||||
if (buf.hasMemoryAddress()) {
|
||||
long memoryAddress = buf.memoryAddress();
|
||||
int readerIndex = buf.readerIndex();
|
||||
int writerIndex = buf.writerIndex();
|
||||
for (int i = writeSpinCount; i > 0; --i) {
|
||||
int localFlushedAmount = socket.writeAddress(memoryAddress, readerIndex, writerIndex);
|
||||
if (localFlushedAmount > 0) {
|
||||
writtenBytes += localFlushedAmount;
|
||||
if (writtenBytes == readableBytes) {
|
||||
return writtenBytes;
|
||||
}
|
||||
readerIndex += localFlushedAmount;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ByteBuffer nioBuf;
|
||||
if (buf.nioBufferCount() == 1) {
|
||||
nioBuf = buf.internalNioBuffer(buf.readerIndex(), buf.readableBytes());
|
||||
} else {
|
||||
nioBuf = buf.nioBuffer();
|
||||
}
|
||||
for (int i = writeSpinCount; i > 0; --i) {
|
||||
int pos = nioBuf.position();
|
||||
int limit = nioBuf.limit();
|
||||
int localFlushedAmount = socket.write(nioBuf, pos, limit);
|
||||
if (localFlushedAmount > 0) {
|
||||
nioBuf.position(pos + localFlushedAmount);
|
||||
writtenBytes += localFlushedAmount;
|
||||
if (writtenBytes == readableBytes) {
|
||||
return writtenBytes;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (writtenBytes < readableBytes) {
|
||||
// Returned EAGAIN need to wait until we are allowed to write again.
|
||||
writeFilter(true);
|
||||
}
|
||||
return writtenBytes;
|
||||
}
|
||||
|
||||
final boolean shouldBreakReadReady(ChannelConfig config) {
|
||||
return socket.isInputShutdown() && (inputClosedSeenErrorOnRead || !isAllowHalfClosure(config));
|
||||
}
|
||||
|
||||
final boolean isAllowHalfClosure(ChannelConfig config) {
|
||||
return config instanceof KQueueSocketChannelConfig &&
|
||||
((KQueueSocketChannelConfig) config).isAllowHalfClosure();
|
||||
}
|
||||
|
||||
final void clearReadFilter() {
|
||||
// Only clear if registered with an EventLoop as otherwise
|
||||
if (isRegistered()) {
|
||||
final EventLoop loop = eventLoop();
|
||||
final AbstractKQueueUnsafe unsafe = (AbstractKQueueUnsafe) unsafe();
|
||||
if (loop.inEventLoop()) {
|
||||
unsafe.clearReadFilter0();
|
||||
} else {
|
||||
// schedule a task to clear the EPOLLIN as it is not safe to modify it directly
|
||||
loop.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!unsafe.readPending && !config().isAutoRead()) {
|
||||
// Still no read triggered so clear it now
|
||||
unsafe.clearReadFilter0();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// The EventLoop is not registered atm so just update the flags so the correct value
|
||||
// will be used once the channel is registered
|
||||
readFilterEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
void readFilter(boolean readFilterEnabled) throws IOException {
|
||||
if (this.readFilterEnabled != readFilterEnabled) {
|
||||
this.readFilterEnabled = readFilterEnabled;
|
||||
evSet(Native.EVFILT_READ, readFilterEnabled ? Native.EV_ADD_CLEAR_ENABLE : Native.EV_DELETE_DISABLE);
|
||||
}
|
||||
}
|
||||
|
||||
void writeFilter(boolean writeFilterEnabled) throws IOException {
|
||||
if (this.writeFilterEnabled != writeFilterEnabled) {
|
||||
this.writeFilterEnabled = writeFilterEnabled;
|
||||
evSet(Native.EVFILT_WRITE, writeFilterEnabled ? Native.EV_ADD_CLEAR_ENABLE : Native.EV_DELETE_DISABLE);
|
||||
}
|
||||
}
|
||||
|
||||
private void evSet(short filter, short flags) {
|
||||
if (isOpen() && isRegistered()) {
|
||||
evSet0(filter, flags);
|
||||
}
|
||||
}
|
||||
|
||||
private void evSet0(short filter, short flags) {
|
||||
((KQueueEventLoop) eventLoop()).evSet(this, filter, flags, 0);
|
||||
}
|
||||
|
||||
abstract class AbstractKQueueUnsafe extends AbstractUnsafe {
|
||||
boolean readPending;
|
||||
boolean maybeMoreDataToRead;
|
||||
private KQueueRecvByteAllocatorHandle allocHandle;
|
||||
private final Runnable readReadyRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
readReadyRunnablePending = false;
|
||||
readReady(recvBufAllocHandle());
|
||||
}
|
||||
};
|
||||
|
||||
final void readReady(long numberBytesPending) {
|
||||
KQueueRecvByteAllocatorHandle allocHandle = recvBufAllocHandle();
|
||||
allocHandle.numberBytesPending(numberBytesPending);
|
||||
readReady(allocHandle);
|
||||
}
|
||||
|
||||
abstract void readReady(KQueueRecvByteAllocatorHandle allocHandle);
|
||||
|
||||
final void readReadyBefore() { maybeMoreDataToRead = false; }
|
||||
|
||||
final void readReadyFinally(ChannelConfig config) {
|
||||
maybeMoreDataToRead = allocHandle.maybeMoreDataToRead();
|
||||
// Check if there is a readPending which was not processed yet.
|
||||
// This could be for two reasons:
|
||||
// * The user called Channel.read() or ChannelHandlerContext.read() in channelRead(...) method
|
||||
// * The user called Channel.read() or ChannelHandlerContext.read() in channelReadComplete(...) method
|
||||
//
|
||||
// See https://github.com/netty/netty/issues/2254
|
||||
if (!readPending && !config.isAutoRead()) {
|
||||
clearReadFilter0();
|
||||
} else if (readPending && maybeMoreDataToRead) {
|
||||
// trigger a read again as there may be something left to read and because of ET we
|
||||
// will not get notified again until we read everything from the socket
|
||||
//
|
||||
// It is possible the last fireChannelRead call could cause the user to call read() again, or if
|
||||
// autoRead is true the call to channelReadComplete would also call read, but maybeMoreDataToRead is set
|
||||
// to false before every read operation to prevent re-entry into readReady() we will not read from
|
||||
// the underlying OS again unless the user happens to call read again.
|
||||
executeReadReadyRunnable(config);
|
||||
}
|
||||
}
|
||||
|
||||
void writeReady() {
|
||||
if (socket.isOutputShutdown()) {
|
||||
return;
|
||||
}
|
||||
// directly call super.flush0() to force a flush now
|
||||
super.flush0();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown the input side of the channel.
|
||||
*/
|
||||
void shutdownInput(boolean readEOF) {
|
||||
if (!socket.isInputShutdown()) {
|
||||
if (isAllowHalfClosure(config())) {
|
||||
try {
|
||||
socket.shutdown(true, false);
|
||||
} catch (IOException ignored) {
|
||||
// We attempted to shutdown and failed, which means the input has already effectively been
|
||||
// shutdown.
|
||||
fireEventAndClose(ChannelInputShutdownEvent.INSTANCE);
|
||||
return;
|
||||
} catch (NotYetConnectedException ignore) {
|
||||
// We attempted to shutdown and failed, which means the input has already effectively been
|
||||
// shutdown.
|
||||
}
|
||||
pipeline().fireUserEventTriggered(ChannelInputShutdownEvent.INSTANCE);
|
||||
} else {
|
||||
close(voidPromise());
|
||||
}
|
||||
} else if (!readEOF) {
|
||||
inputClosedSeenErrorOnRead = true;
|
||||
pipeline().fireUserEventTriggered(ChannelInputShutdownReadComplete.INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
final void readEOF() {
|
||||
// This must happen before we attempt to read. This will ensure reading continues until an error occurs.
|
||||
final KQueueRecvByteAllocatorHandle allocHandle = recvBufAllocHandle();
|
||||
allocHandle.readEOF();
|
||||
|
||||
if (isActive()) {
|
||||
// If it is still active, we need to call readReady as otherwise we may miss to
|
||||
// read pending data from the underlying file descriptor.
|
||||
// See https://github.com/netty/netty/issues/3709
|
||||
readReady(allocHandle);
|
||||
} else {
|
||||
// Just to be safe make sure the input marked as closed.
|
||||
shutdownInput(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueRecvByteAllocatorHandle recvBufAllocHandle() {
|
||||
if (allocHandle == null) {
|
||||
allocHandle = new KQueueRecvByteAllocatorHandle(
|
||||
(RecvByteBufAllocator.ExtendedHandle) super.recvBufAllocHandle());
|
||||
}
|
||||
return allocHandle;
|
||||
}
|
||||
|
||||
final void executeReadReadyRunnable(ChannelConfig config) {
|
||||
if (readReadyRunnablePending || !isActive() || shouldBreakReadReady(config)) {
|
||||
return;
|
||||
}
|
||||
readReadyRunnablePending = true;
|
||||
eventLoop().execute(readReadyRunnable);
|
||||
}
|
||||
|
||||
protected final void clearReadFilter0() {
|
||||
assert eventLoop().inEventLoop();
|
||||
try {
|
||||
readPending = false;
|
||||
readFilter(false);
|
||||
} catch (IOException e) {
|
||||
// When this happens there is something completely wrong with either the filedescriptor or epoll,
|
||||
// so fire the exception through the pipeline and close the Channel.
|
||||
pipeline().fireExceptionCaught(e);
|
||||
unsafe().close(unsafe().voidPromise());
|
||||
}
|
||||
}
|
||||
|
||||
private void fireEventAndClose(Object evt) {
|
||||
pipeline().fireUserEventTriggered(evt);
|
||||
close(voidPromise());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelConfig;
|
||||
import io.netty.channel.ChannelMetadata;
|
||||
import io.netty.channel.ChannelOutboundBuffer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import io.netty.channel.EventLoop;
|
||||
import io.netty.channel.ServerChannel;
|
||||
import io.netty.util.internal.UnstableApi;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
|
||||
@UnstableApi
|
||||
public abstract class AbstractKQueueServerChannel extends AbstractKQueueChannel implements ServerChannel {
|
||||
private static final ChannelMetadata METADATA = new ChannelMetadata(false, 16);
|
||||
|
||||
AbstractKQueueServerChannel(BsdSocket fd, boolean active) {
|
||||
super(null, fd, active);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelMetadata metadata() {
|
||||
return METADATA;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isCompatible(EventLoop loop) {
|
||||
return loop instanceof KQueueEventLoop;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InetSocketAddress remoteAddress0() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractKQueueUnsafe newUnsafe() {
|
||||
return new KQueueServerSocketUnsafe();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doWrite(ChannelOutboundBuffer in) throws Exception {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object filterOutboundMessage(Object msg) throws Exception {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
abstract Channel newChildChannel(int fd, byte[] remote, int offset, int len) throws Exception;
|
||||
|
||||
final class KQueueServerSocketUnsafe extends AbstractKQueueUnsafe {
|
||||
// Will hold the remote address after accept(...) was successful.
|
||||
// We need 24 bytes for the address as maximum + 1 byte for storing the capacity.
|
||||
// So use 26 bytes as it's a power of two.
|
||||
private final byte[] acceptedAddress = new byte[26];
|
||||
|
||||
@Override
|
||||
public void connect(SocketAddress socketAddress, SocketAddress socketAddress2, ChannelPromise channelPromise) {
|
||||
// Connect not supported by ServerChannel implementations
|
||||
channelPromise.setFailure(new UnsupportedOperationException());
|
||||
}
|
||||
|
||||
@Override
|
||||
void readReady(KQueueRecvByteAllocatorHandle allocHandle) {
|
||||
assert eventLoop().inEventLoop();
|
||||
final ChannelConfig config = config();
|
||||
if (shouldBreakReadReady(config)) {
|
||||
clearReadFilter0();
|
||||
return;
|
||||
}
|
||||
final ChannelPipeline pipeline = pipeline();
|
||||
allocHandle.reset(config);
|
||||
allocHandle.attemptedBytesRead(1);
|
||||
readReadyBefore();
|
||||
|
||||
Throwable exception = null;
|
||||
try {
|
||||
try {
|
||||
do {
|
||||
int acceptFd = socket.accept(acceptedAddress);
|
||||
if (acceptFd == -1) {
|
||||
// this means everything was handled for now
|
||||
allocHandle.lastBytesRead(-1);
|
||||
break;
|
||||
}
|
||||
allocHandle.lastBytesRead(1);
|
||||
allocHandle.incMessagesRead(1);
|
||||
|
||||
readPending = false;
|
||||
pipeline.fireChannelRead(newChildChannel(acceptFd, acceptedAddress, 1,
|
||||
acceptedAddress[0]));
|
||||
} while (allocHandle.continueReading());
|
||||
} catch (Throwable t) {
|
||||
exception = t;
|
||||
}
|
||||
allocHandle.readComplete();
|
||||
pipeline.fireChannelReadComplete();
|
||||
|
||||
if (exception != null) {
|
||||
pipeline.fireExceptionCaught(exception);
|
||||
}
|
||||
} finally {
|
||||
readReadyFinally(config);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,808 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.buffer.CompositeByteBuf;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelConfig;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelMetadata;
|
||||
import io.netty.channel.ChannelOutboundBuffer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import io.netty.channel.ConnectTimeoutException;
|
||||
import io.netty.channel.DefaultFileRegion;
|
||||
import io.netty.channel.EventLoop;
|
||||
import io.netty.channel.FileRegion;
|
||||
import io.netty.channel.socket.DuplexChannel;
|
||||
import io.netty.channel.unix.IovArray;
|
||||
import io.netty.channel.unix.SocketWritableByteChannel;
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import io.netty.util.internal.ThrowableUtil;
|
||||
import io.netty.util.internal.UnstableApi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.SocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.nio.channels.ConnectionPendingException;
|
||||
import java.nio.channels.WritableByteChannel;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static io.netty.channel.unix.Limits.IOV_MAX;
|
||||
|
||||
@UnstableApi
|
||||
public abstract class AbstractKQueueStreamChannel extends AbstractKQueueChannel implements DuplexChannel {
|
||||
private static final ChannelMetadata METADATA = new ChannelMetadata(false, 16);
|
||||
private static final ClosedChannelException DO_CLOSE_CLOSED_CHANNEL_EXCEPTION = ThrowableUtil.unknownStackTrace(
|
||||
new ClosedChannelException(), AbstractKQueueStreamChannel.class, "doClose()");
|
||||
private static final String EXPECTED_TYPES =
|
||||
" (expected: " + StringUtil.simpleClassName(ByteBuf.class) + ", " +
|
||||
StringUtil.simpleClassName(DefaultFileRegion.class) + ')';
|
||||
|
||||
private ChannelPromise connectPromise;
|
||||
private ScheduledFuture<?> connectTimeoutFuture;
|
||||
private SocketAddress requestedRemoteAddress;
|
||||
private WritableByteChannel byteChannel;
|
||||
|
||||
AbstractKQueueStreamChannel(Channel parent, BsdSocket fd, boolean active) {
|
||||
super(parent, fd, active, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractKQueueUnsafe newUnsafe() {
|
||||
return new KQueueStreamUnsafe();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelMetadata metadata() {
|
||||
return METADATA;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write bytes form the given {@link ByteBuf} to the underlying {@link java.nio.channels.Channel}.
|
||||
* @param buf the {@link ByteBuf} from which the bytes should be written
|
||||
*/
|
||||
private boolean writeBytes(ChannelOutboundBuffer in, ByteBuf buf, int writeSpinCount) throws Exception {
|
||||
int readableBytes = buf.readableBytes();
|
||||
if (readableBytes == 0) {
|
||||
in.remove();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (buf.hasMemoryAddress() || buf.nioBufferCount() == 1) {
|
||||
int writtenBytes = doWriteBytes(buf, writeSpinCount);
|
||||
in.removeBytes(writtenBytes);
|
||||
return writtenBytes == readableBytes;
|
||||
} else {
|
||||
ByteBuffer[] nioBuffers = buf.nioBuffers();
|
||||
return writeBytesMultiple(in, nioBuffers, nioBuffers.length, readableBytes, writeSpinCount);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean writeBytesMultiple(
|
||||
ChannelOutboundBuffer in, IovArray array, int writeSpinCount) throws IOException {
|
||||
|
||||
long expectedWrittenBytes = array.size();
|
||||
final long initialExpectedWrittenBytes = expectedWrittenBytes;
|
||||
|
||||
int cnt = array.count();
|
||||
|
||||
assert expectedWrittenBytes != 0;
|
||||
assert cnt != 0;
|
||||
|
||||
boolean done = false;
|
||||
int offset = 0;
|
||||
int end = offset + cnt;
|
||||
for (int i = writeSpinCount; i > 0; --i) {
|
||||
long localWrittenBytes = socket.writevAddresses(array.memoryAddress(offset), cnt);
|
||||
if (localWrittenBytes == 0) {
|
||||
break;
|
||||
}
|
||||
expectedWrittenBytes -= localWrittenBytes;
|
||||
|
||||
if (expectedWrittenBytes == 0) {
|
||||
// Written everything, just break out here (fast-path)
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
|
||||
do {
|
||||
long bytes = array.processWritten(offset, localWrittenBytes);
|
||||
if (bytes == -1) {
|
||||
// incomplete write
|
||||
break;
|
||||
} else {
|
||||
offset++;
|
||||
cnt--;
|
||||
localWrittenBytes -= bytes;
|
||||
}
|
||||
} while (offset < end && localWrittenBytes > 0);
|
||||
}
|
||||
in.removeBytes(initialExpectedWrittenBytes - expectedWrittenBytes);
|
||||
return done;
|
||||
}
|
||||
|
||||
private boolean writeBytesMultiple(
|
||||
ChannelOutboundBuffer in, ByteBuffer[] nioBuffers,
|
||||
int nioBufferCnt, long expectedWrittenBytes, int writeSpinCount) throws IOException {
|
||||
|
||||
assert expectedWrittenBytes != 0;
|
||||
final long initialExpectedWrittenBytes = expectedWrittenBytes;
|
||||
|
||||
boolean done = false;
|
||||
int offset = 0;
|
||||
int end = offset + nioBufferCnt;
|
||||
for (int i = writeSpinCount; i > 0; --i) {
|
||||
long localWrittenBytes = socket.writev(nioBuffers, offset, nioBufferCnt);
|
||||
if (localWrittenBytes == 0) {
|
||||
break;
|
||||
}
|
||||
expectedWrittenBytes -= localWrittenBytes;
|
||||
|
||||
if (expectedWrittenBytes == 0) {
|
||||
// Written everything, just break out here (fast-path)
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
do {
|
||||
ByteBuffer buffer = nioBuffers[offset];
|
||||
int pos = buffer.position();
|
||||
int bytes = buffer.limit() - pos;
|
||||
if (bytes > localWrittenBytes) {
|
||||
buffer.position(pos + (int) localWrittenBytes);
|
||||
// incomplete write
|
||||
break;
|
||||
} else {
|
||||
offset++;
|
||||
nioBufferCnt--;
|
||||
localWrittenBytes -= bytes;
|
||||
}
|
||||
} while (offset < end && localWrittenBytes > 0);
|
||||
}
|
||||
|
||||
in.removeBytes(initialExpectedWrittenBytes - expectedWrittenBytes);
|
||||
return done;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a {@link DefaultFileRegion}
|
||||
*
|
||||
* @param region the {@link DefaultFileRegion} from which the bytes should be written
|
||||
* @return amount the amount of written bytes
|
||||
*/
|
||||
private boolean writeDefaultFileRegion(
|
||||
ChannelOutboundBuffer in, DefaultFileRegion region, int writeSpinCount) throws Exception {
|
||||
final long regionCount = region.count();
|
||||
if (region.transferred() >= regionCount) {
|
||||
in.remove();
|
||||
return true;
|
||||
}
|
||||
|
||||
final long baseOffset = region.position();
|
||||
boolean done = false;
|
||||
long flushedAmount = 0;
|
||||
|
||||
for (int i = writeSpinCount; i > 0; --i) {
|
||||
final long offset = region.transferred();
|
||||
final long localFlushedAmount = socket.sendFile(region, baseOffset, offset, regionCount - offset);
|
||||
if (localFlushedAmount == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
flushedAmount += localFlushedAmount;
|
||||
if (region.transferred() >= regionCount) {
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (flushedAmount > 0) {
|
||||
in.progress(flushedAmount);
|
||||
}
|
||||
|
||||
if (done) {
|
||||
in.remove();
|
||||
}
|
||||
return done;
|
||||
}
|
||||
|
||||
private boolean writeFileRegion(
|
||||
ChannelOutboundBuffer in, FileRegion region, final int writeSpinCount) throws Exception {
|
||||
if (region.transferred() >= region.count()) {
|
||||
in.remove();
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean done = false;
|
||||
long flushedAmount = 0;
|
||||
|
||||
if (byteChannel == null) {
|
||||
byteChannel = new KQueueSocketWritableByteChannel();
|
||||
}
|
||||
for (int i = writeSpinCount; i > 0; --i) {
|
||||
final long localFlushedAmount = region.transferTo(byteChannel, region.transferred());
|
||||
if (localFlushedAmount == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
flushedAmount += localFlushedAmount;
|
||||
if (region.transferred() >= region.count()) {
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (flushedAmount > 0) {
|
||||
in.progress(flushedAmount);
|
||||
}
|
||||
|
||||
if (done) {
|
||||
in.remove();
|
||||
}
|
||||
return done;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doWrite(ChannelOutboundBuffer in) throws Exception {
|
||||
int writeSpinCount = config().getWriteSpinCount();
|
||||
for (;;) {
|
||||
final int msgCount = in.size();
|
||||
|
||||
if (msgCount == 0) {
|
||||
// Wrote all messages.
|
||||
writeFilter(false);
|
||||
// Return here so we not set the EPOLLOUT flag.
|
||||
return;
|
||||
}
|
||||
|
||||
// Do gathering write if the outbounf buffer entries start with more than one ByteBuf.
|
||||
if (msgCount > 1 && in.current() instanceof ByteBuf) {
|
||||
if (!doWriteMultiple(in, writeSpinCount)) {
|
||||
// Break the loop and so set EPOLLOUT flag.
|
||||
break;
|
||||
}
|
||||
|
||||
// We do not break the loop here even if the outbound buffer was flushed completely,
|
||||
// because a user might have triggered another write and flush when we notify his or her
|
||||
// listeners.
|
||||
} else { // msgCount == 1
|
||||
if (!doWriteSingle(in, writeSpinCount)) {
|
||||
// Break the loop and so set EPOLLOUT flag.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Underlying descriptor can not accept all data currently, so set the EPOLLOUT flag to be woken up
|
||||
// when it can accept more data.
|
||||
writeFilter(true);
|
||||
}
|
||||
|
||||
protected boolean doWriteSingle(ChannelOutboundBuffer in, int writeSpinCount) throws Exception {
|
||||
// The outbound buffer contains only one message or it contains a file region.
|
||||
Object msg = in.current();
|
||||
if (msg instanceof ByteBuf) {
|
||||
if (!writeBytes(in, (ByteBuf) msg, writeSpinCount)) {
|
||||
// was not able to write everything so break here we will get notified later again once
|
||||
// the network stack can handle more writes.
|
||||
return false;
|
||||
}
|
||||
} else if (msg instanceof DefaultFileRegion) {
|
||||
if (!writeDefaultFileRegion(in, (DefaultFileRegion) msg, writeSpinCount)) {
|
||||
// was not able to write everything so break here we will get notified later again once
|
||||
// the network stack can handle more writes.
|
||||
return false;
|
||||
}
|
||||
} else if (msg instanceof FileRegion) {
|
||||
if (!writeFileRegion(in, (FileRegion) msg, writeSpinCount)) {
|
||||
// was not able to write everything so break here we will get notified later again once
|
||||
// the network stack can handle more writes.
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Should never reach here.
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean doWriteMultiple(ChannelOutboundBuffer in, int writeSpinCount) throws Exception {
|
||||
if (PlatformDependent.hasUnsafe()) {
|
||||
// this means we can cast to IovArray and write the IovArray directly.
|
||||
IovArray array = ((KQueueEventLoop) eventLoop()).cleanArray();
|
||||
in.forEachFlushedMessage(array);
|
||||
|
||||
int cnt = array.count();
|
||||
if (cnt >= 1) {
|
||||
// TODO: Handle the case where cnt == 1 specially.
|
||||
if (!writeBytesMultiple(in, array, writeSpinCount)) {
|
||||
// was not able to write everything so break here we will get notified later again once
|
||||
// the network stack can handle more writes.
|
||||
return false;
|
||||
}
|
||||
} else { // cnt == 0, which means the outbound buffer contained empty buffers only.
|
||||
in.removeBytes(0);
|
||||
}
|
||||
} else {
|
||||
ByteBuffer[] buffers = in.nioBuffers();
|
||||
int cnt = in.nioBufferCount();
|
||||
if (cnt >= 1) {
|
||||
// TODO: Handle the case where cnt == 1 specially.
|
||||
if (!writeBytesMultiple(in, buffers, cnt, in.nioBufferSize(), writeSpinCount)) {
|
||||
// was not able to write everything so break here we will get notified later again once
|
||||
// the network stack can handle more writes.
|
||||
return false;
|
||||
}
|
||||
} else { // cnt == 0, which means the outbound buffer contained empty buffers only.
|
||||
in.removeBytes(0);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object filterOutboundMessage(Object msg) {
|
||||
if (msg instanceof ByteBuf) {
|
||||
ByteBuf buf = (ByteBuf) msg;
|
||||
if (!buf.hasMemoryAddress() && (PlatformDependent.hasUnsafe() || !buf.isDirect())) {
|
||||
if (buf instanceof CompositeByteBuf) {
|
||||
// Special handling of CompositeByteBuf to reduce memory copies if some of the Components
|
||||
// in the CompositeByteBuf are backed by a memoryAddress.
|
||||
CompositeByteBuf comp = (CompositeByteBuf) buf;
|
||||
if (!comp.isDirect() || comp.nioBufferCount() > IOV_MAX) {
|
||||
// more then 1024 buffers for gathering writes so just do a memory copy.
|
||||
buf = newDirectBuffer(buf);
|
||||
assert buf.hasMemoryAddress();
|
||||
}
|
||||
} else {
|
||||
// We can only handle buffers with memory address so we need to copy if a non direct is
|
||||
// passed to write.
|
||||
buf = newDirectBuffer(buf);
|
||||
assert buf.hasMemoryAddress();
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
if (msg instanceof FileRegion) {
|
||||
return msg;
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException(
|
||||
"unsupported message type: " + StringUtil.simpleClassName(msg) + EXPECTED_TYPES);
|
||||
}
|
||||
|
||||
private void shutdownOutput0(final ChannelPromise promise) {
|
||||
try {
|
||||
socket.shutdown(false, true);
|
||||
promise.setSuccess();
|
||||
} catch (Throwable cause) {
|
||||
promise.setFailure(cause);
|
||||
}
|
||||
}
|
||||
|
||||
private void shutdownInput0(final ChannelPromise promise) {
|
||||
try {
|
||||
socket.shutdown(true, false);
|
||||
promise.setSuccess();
|
||||
} catch (Throwable cause) {
|
||||
promise.setFailure(cause);
|
||||
}
|
||||
}
|
||||
|
||||
private void shutdown0(final ChannelPromise promise) {
|
||||
try {
|
||||
socket.shutdown(true, true);
|
||||
promise.setSuccess();
|
||||
} catch (Throwable cause) {
|
||||
promise.setFailure(cause);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOutputShutdown() {
|
||||
return socket.isOutputShutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInputShutdown() {
|
||||
return socket.isInputShutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShutdown() {
|
||||
return socket.isShutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture shutdownOutput() {
|
||||
return shutdownOutput(newPromise());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture shutdownOutput(final ChannelPromise promise) {
|
||||
Executor closeExecutor = ((KQueueStreamUnsafe) unsafe()).prepareToClose();
|
||||
if (closeExecutor != null) {
|
||||
closeExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
shutdownOutput0(promise);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
EventLoop loop = eventLoop();
|
||||
if (loop.inEventLoop()) {
|
||||
shutdownOutput0(promise);
|
||||
} else {
|
||||
loop.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
shutdownOutput0(promise);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return promise;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture shutdownInput() {
|
||||
return shutdownInput(newPromise());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture shutdownInput(final ChannelPromise promise) {
|
||||
Executor closeExecutor = ((KQueueStreamUnsafe) unsafe()).prepareToClose();
|
||||
if (closeExecutor != null) {
|
||||
closeExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
shutdownInput0(promise);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
EventLoop loop = eventLoop();
|
||||
if (loop.inEventLoop()) {
|
||||
shutdownInput0(promise);
|
||||
} else {
|
||||
loop.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
shutdownInput0(promise);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return promise;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture shutdown() {
|
||||
return shutdown(newPromise());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture shutdown(final ChannelPromise promise) {
|
||||
Executor closeExecutor = ((KQueueStreamUnsafe) unsafe()).prepareToClose();
|
||||
if (closeExecutor != null) {
|
||||
closeExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
shutdown0(promise);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
EventLoop loop = eventLoop();
|
||||
if (loop.inEventLoop()) {
|
||||
shutdown0(promise);
|
||||
} else {
|
||||
loop.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
shutdown0(promise);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return promise;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doClose() throws Exception {
|
||||
ChannelPromise promise = connectPromise;
|
||||
if (promise != null) {
|
||||
// Use tryFailure() instead of setFailure() to avoid the race against cancel().
|
||||
promise.tryFailure(DO_CLOSE_CLOSED_CHANNEL_EXCEPTION);
|
||||
connectPromise = null;
|
||||
}
|
||||
|
||||
ScheduledFuture<?> future = connectTimeoutFuture;
|
||||
if (future != null) {
|
||||
future.cancel(false);
|
||||
connectTimeoutFuture = null;
|
||||
}
|
||||
// Calling super.doClose() first so splceTo(...) will fail on next call.
|
||||
super.doClose();
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the remote peer
|
||||
*/
|
||||
protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
|
||||
if (localAddress != null) {
|
||||
socket.bind(localAddress);
|
||||
}
|
||||
|
||||
boolean success = false;
|
||||
try {
|
||||
boolean connected = socket.connect(remoteAddress);
|
||||
if (!connected) {
|
||||
writeFilter(true);
|
||||
}
|
||||
success = true;
|
||||
return connected;
|
||||
} finally {
|
||||
if (!success) {
|
||||
doClose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class KQueueStreamUnsafe extends AbstractKQueueUnsafe {
|
||||
// Overridden here just to be able to access this method from AbstractKQueueStreamChannel
|
||||
@Override
|
||||
protected Executor prepareToClose() {
|
||||
return super.prepareToClose();
|
||||
}
|
||||
|
||||
@Override
|
||||
void readReady(final KQueueRecvByteAllocatorHandle allocHandle) {
|
||||
final ChannelConfig config = config();
|
||||
if (shouldBreakReadReady(config)) {
|
||||
clearReadFilter0();
|
||||
return;
|
||||
}
|
||||
final ChannelPipeline pipeline = pipeline();
|
||||
final ByteBufAllocator allocator = config.getAllocator();
|
||||
allocHandle.reset(config);
|
||||
readReadyBefore();
|
||||
|
||||
ByteBuf byteBuf = null;
|
||||
boolean close = false;
|
||||
try {
|
||||
do {
|
||||
// we use a direct buffer here as the native implementations only be able
|
||||
// to handle direct buffers.
|
||||
byteBuf = allocHandle.allocate(allocator);
|
||||
allocHandle.lastBytesRead(doReadBytes(byteBuf));
|
||||
if (allocHandle.lastBytesRead() <= 0) {
|
||||
// nothing was read, release the buffer.
|
||||
byteBuf.release();
|
||||
byteBuf = null;
|
||||
close = allocHandle.lastBytesRead() < 0;
|
||||
break;
|
||||
}
|
||||
allocHandle.incMessagesRead(1);
|
||||
readPending = false;
|
||||
pipeline.fireChannelRead(byteBuf);
|
||||
byteBuf = null;
|
||||
|
||||
if (shouldBreakReadReady(config)) {
|
||||
// We need to do this for two reasons:
|
||||
//
|
||||
// - If the input was shutdown in between (which may be the case when the user did it in the
|
||||
// fireChannelRead(...) method we should not try to read again to not produce any
|
||||
// miss-leading exceptions.
|
||||
//
|
||||
// - If the user closes the channel we need to ensure we not try to read from it again as
|
||||
// the filedescriptor may be re-used already by the OS if the system is handling a lot of
|
||||
// concurrent connections and so needs a lot of filedescriptors. If not do this we risk
|
||||
// reading data from a filedescriptor that belongs to another socket then the socket that
|
||||
// was "wrapped" by this Channel implementation.
|
||||
break;
|
||||
}
|
||||
} while (allocHandle.continueReading());
|
||||
|
||||
allocHandle.readComplete();
|
||||
pipeline.fireChannelReadComplete();
|
||||
|
||||
if (close) {
|
||||
shutdownInput(false);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
handleReadException(pipeline, byteBuf, t, close, allocHandle);
|
||||
} finally {
|
||||
readReadyFinally(config);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void writeReady() {
|
||||
if (connectPromise != null) {
|
||||
// pending connect which is now complete so handle it.
|
||||
finishConnect();
|
||||
} else {
|
||||
super.writeReady();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect(
|
||||
final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {
|
||||
if (!promise.setUncancellable() || !ensureOpen(promise)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (connectPromise != null) {
|
||||
throw new ConnectionPendingException();
|
||||
}
|
||||
|
||||
boolean wasActive = isActive();
|
||||
if (doConnect(remoteAddress, localAddress)) {
|
||||
fulfillConnectPromise(promise, wasActive);
|
||||
} else {
|
||||
connectPromise = promise;
|
||||
requestedRemoteAddress = remoteAddress;
|
||||
|
||||
// Schedule connect timeout.
|
||||
int connectTimeoutMillis = config().getConnectTimeoutMillis();
|
||||
if (connectTimeoutMillis > 0) {
|
||||
connectTimeoutFuture = eventLoop().schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ChannelPromise connectPromise = AbstractKQueueStreamChannel.this.connectPromise;
|
||||
ConnectTimeoutException cause =
|
||||
new ConnectTimeoutException("connection timed out: " + remoteAddress);
|
||||
if (connectPromise != null && connectPromise.tryFailure(cause)) {
|
||||
close(voidPromise());
|
||||
}
|
||||
}
|
||||
}, connectTimeoutMillis, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
promise.addListener(new ChannelFutureListener() {
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture future) throws Exception {
|
||||
if (future.isCancelled()) {
|
||||
if (connectTimeoutFuture != null) {
|
||||
connectTimeoutFuture.cancel(false);
|
||||
}
|
||||
connectPromise = null;
|
||||
close(voidPromise());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
closeIfClosed();
|
||||
promise.tryFailure(annotateConnectException(t, remoteAddress));
|
||||
}
|
||||
}
|
||||
|
||||
private void fulfillConnectPromise(ChannelPromise promise, boolean wasActive) {
|
||||
if (promise == null) {
|
||||
// Closed via cancellation and the promise has been notified already.
|
||||
return;
|
||||
}
|
||||
active = true;
|
||||
|
||||
// Get the state as trySuccess() may trigger an ChannelFutureListener that will close the Channel.
|
||||
// We still need to ensure we call fireChannelActive() in this case.
|
||||
boolean active = isActive();
|
||||
|
||||
// trySuccess() will return false if a user cancelled the connection attempt.
|
||||
boolean promiseSet = promise.trySuccess();
|
||||
|
||||
// Regardless if the connection attempt was cancelled, channelActive() event should be triggered,
|
||||
// because what happened is what happened.
|
||||
if (!wasActive && active) {
|
||||
pipeline().fireChannelActive();
|
||||
}
|
||||
|
||||
// If a user cancelled the connection attempt, close the channel, which is followed by channelInactive().
|
||||
if (!promiseSet) {
|
||||
close(voidPromise());
|
||||
}
|
||||
}
|
||||
|
||||
private void fulfillConnectPromise(ChannelPromise promise, Throwable cause) {
|
||||
if (promise == null) {
|
||||
// Closed via cancellation and the promise has been notified already.
|
||||
return;
|
||||
}
|
||||
|
||||
// Use tryFailure() instead of setFailure() to avoid the race against cancel().
|
||||
promise.tryFailure(cause);
|
||||
closeIfClosed();
|
||||
}
|
||||
|
||||
private void finishConnect() {
|
||||
// Note this method is invoked by the event loop only if the connection attempt was
|
||||
// neither cancelled nor timed out.
|
||||
|
||||
assert eventLoop().inEventLoop();
|
||||
|
||||
boolean connectStillInProgress = false;
|
||||
try {
|
||||
boolean wasActive = isActive();
|
||||
if (!doFinishConnect()) {
|
||||
connectStillInProgress = true;
|
||||
return;
|
||||
}
|
||||
fulfillConnectPromise(connectPromise, wasActive);
|
||||
} catch (Throwable t) {
|
||||
fulfillConnectPromise(connectPromise, annotateConnectException(t, requestedRemoteAddress));
|
||||
} finally {
|
||||
if (!connectStillInProgress) {
|
||||
// Check for null as the connectTimeoutFuture is only created if a connectTimeoutMillis > 0 is used
|
||||
// See https://github.com/netty/netty/issues/1770
|
||||
if (connectTimeoutFuture != null) {
|
||||
connectTimeoutFuture.cancel(false);
|
||||
}
|
||||
connectPromise = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean doFinishConnect() throws Exception {
|
||||
if (socket.finishConnect()) {
|
||||
writeFilter(false);
|
||||
return true;
|
||||
} else {
|
||||
writeFilter(true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleReadException(ChannelPipeline pipeline, ByteBuf byteBuf, Throwable cause, boolean close,
|
||||
KQueueRecvByteAllocatorHandle allocHandle) {
|
||||
if (byteBuf != null) {
|
||||
if (byteBuf.isReadable()) {
|
||||
readPending = false;
|
||||
pipeline.fireChannelRead(byteBuf);
|
||||
} else {
|
||||
byteBuf.release();
|
||||
}
|
||||
}
|
||||
allocHandle.readComplete();
|
||||
pipeline.fireChannelReadComplete();
|
||||
pipeline.fireExceptionCaught(cause);
|
||||
if (close || cause instanceof IOException) {
|
||||
shutdownInput(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class KQueueSocketWritableByteChannel extends SocketWritableByteChannel {
|
||||
KQueueSocketWritableByteChannel() {
|
||||
super(socket);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ByteBufAllocator alloc() {
|
||||
return AbstractKQueueStreamChannel.this.alloc();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.util.internal.ObjectUtil;
|
||||
import io.netty.util.internal.UnstableApi;
|
||||
|
||||
@UnstableApi
|
||||
public final class AcceptFilter {
|
||||
static final AcceptFilter PLATFORM_UNSUPPORTED = new AcceptFilter("", "");
|
||||
private final String filterName;
|
||||
private final String filterArgs;
|
||||
|
||||
public AcceptFilter(String filterName, String filterArgs) {
|
||||
this.filterName = ObjectUtil.checkNotNull(filterName, "filterName");
|
||||
this.filterArgs = ObjectUtil.checkNotNull(filterArgs, "filterArgs");
|
||||
}
|
||||
|
||||
public String filterName() {
|
||||
return filterName;
|
||||
}
|
||||
|
||||
public String filterArgs() {
|
||||
return filterArgs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof AcceptFilter)) {
|
||||
return false;
|
||||
}
|
||||
AcceptFilter rhs = (AcceptFilter) o;
|
||||
return filterName.equals(rhs.filterName) && filterArgs.equals(rhs.filterArgs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * (31 + filterName.hashCode()) + filterArgs.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterName + ", " + filterArgs;
|
||||
}
|
||||
}
|
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.channel.DefaultFileRegion;
|
||||
import io.netty.channel.unix.Errors;
|
||||
import io.netty.channel.unix.PeerCredentials;
|
||||
import io.netty.channel.unix.Socket;
|
||||
import io.netty.util.internal.ThrowableUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
|
||||
import static io.netty.channel.kqueue.AcceptFilter.PLATFORM_UNSUPPORTED;
|
||||
import static io.netty.channel.unix.Errors.ERRNO_EPIPE_NEGATIVE;
|
||||
import static io.netty.channel.unix.Errors.ioResult;
|
||||
import static io.netty.channel.unix.Errors.newConnectionResetException;
|
||||
|
||||
/**
|
||||
* A socket which provides access BSD native methods.
|
||||
*/
|
||||
final class BsdSocket extends Socket {
|
||||
private static final Errors.NativeIoException SENDFILE_CONNECTION_RESET_EXCEPTION;
|
||||
private static final ClosedChannelException SENDFILE_CLOSED_CHANNEL_EXCEPTION = ThrowableUtil.unknownStackTrace(
|
||||
new ClosedChannelException(), Native.class, "sendfile(..)");
|
||||
|
||||
// These limits are just based on observations. I couldn't find anything in header files which formally
|
||||
// define these limits.
|
||||
private static final int APPLE_SND_LOW_AT_MAX = 1 << 17;
|
||||
private static final int FREEBSD_SND_LOW_AT_MAX = 1 << 15;
|
||||
static final int BSD_SND_LOW_AT_MAX = Math.min(APPLE_SND_LOW_AT_MAX, FREEBSD_SND_LOW_AT_MAX);
|
||||
|
||||
static {
|
||||
SENDFILE_CONNECTION_RESET_EXCEPTION = newConnectionResetException("syscall:sendfile",
|
||||
ERRNO_EPIPE_NEGATIVE);
|
||||
}
|
||||
|
||||
BsdSocket(int fd) {
|
||||
super(fd);
|
||||
}
|
||||
|
||||
void setAcceptFilter(AcceptFilter acceptFilter) throws IOException {
|
||||
setAcceptFilter(intValue(), acceptFilter.filterName(), acceptFilter.filterArgs());
|
||||
}
|
||||
|
||||
void setTcpNoPush(boolean tcpNoPush) throws IOException {
|
||||
setTcpNoPush(intValue(), tcpNoPush ? 1 : 0);
|
||||
}
|
||||
|
||||
void setSndLowAt(int lowAt) throws IOException {
|
||||
setSndLowAt(intValue(), lowAt);
|
||||
}
|
||||
|
||||
boolean isTcpNoPush() throws IOException {
|
||||
return getTcpNoPush(intValue()) != 0;
|
||||
}
|
||||
|
||||
int getSndLowAt() throws IOException {
|
||||
return getSndLowAt(intValue());
|
||||
}
|
||||
|
||||
AcceptFilter getAcceptFilter() throws IOException {
|
||||
String[] result = getAcceptFilter(intValue());
|
||||
return result == null ? PLATFORM_UNSUPPORTED : new AcceptFilter(result[0], result[1]);
|
||||
}
|
||||
|
||||
PeerCredentials getPeerCredentials() throws IOException {
|
||||
return getPeerCredentials(intValue());
|
||||
}
|
||||
|
||||
long sendFile(DefaultFileRegion src, long baseOffset, long offset, long length) throws IOException {
|
||||
// Open the file-region as it may be created via the lazy constructor. This is needed as we directly access
|
||||
// the FileChannel field directly via JNI
|
||||
src.open();
|
||||
|
||||
long res = sendFile(intValue(), src, baseOffset, offset, length);
|
||||
if (res >= 0) {
|
||||
return res;
|
||||
}
|
||||
return ioResult("sendfile", (int) res, SENDFILE_CONNECTION_RESET_EXCEPTION, SENDFILE_CLOSED_CHANNEL_EXCEPTION);
|
||||
}
|
||||
|
||||
public static BsdSocket newSocketStream() {
|
||||
return new BsdSocket(newSocketStream0());
|
||||
}
|
||||
|
||||
public static BsdSocket newSocketDgram() {
|
||||
return new BsdSocket(newSocketDgram0());
|
||||
}
|
||||
|
||||
public static BsdSocket newSocketDomain() {
|
||||
return new BsdSocket(newSocketDomain0());
|
||||
}
|
||||
|
||||
private static native long sendFile(int socketFd, DefaultFileRegion src, long baseOffset,
|
||||
long offset, long length) throws IOException;
|
||||
|
||||
private static native String[] getAcceptFilter(int fd) throws IOException;
|
||||
private static native int getTcpNoPush(int fd) throws IOException;
|
||||
private static native int getSndLowAt(int fd) throws IOException;
|
||||
private static native PeerCredentials getPeerCredentials(int fd) throws IOException;
|
||||
|
||||
private static native void setAcceptFilter(int fd, String filterName, String filterArgs) throws IOException;
|
||||
private static native void setTcpNoPush(int fd, int tcpNoPush) throws IOException;
|
||||
private static native void setSndLowAt(int fd, int lowAt) throws IOException;
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.channel.unix.FileDescriptor;
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
import io.netty.util.internal.UnstableApi;
|
||||
|
||||
/**
|
||||
* If KQueue is available the JNI resources will be loaded when this class loads.
|
||||
*/
|
||||
@UnstableApi
|
||||
public final class KQueue {
|
||||
private static final Throwable UNAVAILABILITY_CAUSE;
|
||||
|
||||
static {
|
||||
Throwable cause = null;
|
||||
FileDescriptor kqueueFd = null;
|
||||
try {
|
||||
kqueueFd = Native.newKQueue();
|
||||
} catch (Throwable t) {
|
||||
cause = t;
|
||||
} finally {
|
||||
if (kqueueFd != null) {
|
||||
try {
|
||||
kqueueFd.close();
|
||||
} catch (Exception ignore) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cause != null) {
|
||||
UNAVAILABILITY_CAUSE = cause;
|
||||
} else {
|
||||
UNAVAILABILITY_CAUSE = PlatformDependent.hasUnsafe() ? null :
|
||||
new IllegalStateException("sun.misc.Unsafe not available");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if and only if the
|
||||
* <a href="http://netty.io/wiki/native-transports.html">{@code netty-transport-native-kqueue}</a> is available.
|
||||
*/
|
||||
public static boolean isAvailable() {
|
||||
return UNAVAILABILITY_CAUSE == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that <a href="http://netty.io/wiki/native-transports.html">{@code netty-transport-native-kqueue}</a> is
|
||||
* available.
|
||||
*
|
||||
* @throws UnsatisfiedLinkError if unavailable
|
||||
*/
|
||||
public static void ensureAvailability() {
|
||||
if (UNAVAILABILITY_CAUSE != null) {
|
||||
throw (Error) new UnsatisfiedLinkError(
|
||||
"failed to load the required native library").initCause(UNAVAILABILITY_CAUSE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cause of unavailability of
|
||||
* <a href="http://netty.io/wiki/native-transports.html">{@code netty-transport-native-kqueue}</a>.
|
||||
*
|
||||
* @return the cause if unavailable. {@code null} if available.
|
||||
*/
|
||||
public static Throwable unavailabilityCause() {
|
||||
return UNAVAILABILITY_CAUSE;
|
||||
}
|
||||
|
||||
private KQueue() { }
|
||||
}
|
@ -0,0 +1,156 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.DefaultChannelConfig;
|
||||
import io.netty.channel.MessageSizeEstimator;
|
||||
import io.netty.channel.RecvByteBufAllocator;
|
||||
import io.netty.channel.WriteBufferWaterMark;
|
||||
import io.netty.util.internal.UnstableApi;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static io.netty.channel.kqueue.KQueueChannelOption.RCV_ALLOC_TRANSPORT_PROVIDES_GUESS;
|
||||
|
||||
@UnstableApi
|
||||
public class KQueueChannelConfig extends DefaultChannelConfig {
|
||||
final AbstractKQueueChannel channel;
|
||||
private volatile boolean transportProvidesGuess;
|
||||
|
||||
KQueueChannelConfig(AbstractKQueueChannel channel) {
|
||||
super(channel);
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public Map<ChannelOption<?>, Object> getOptions() {
|
||||
return getOptions(super.getOptions(), RCV_ALLOC_TRANSPORT_PROVIDES_GUESS);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T getOption(ChannelOption<T> option) {
|
||||
if (option == RCV_ALLOC_TRANSPORT_PROVIDES_GUESS) {
|
||||
return (T) Boolean.valueOf(getRcvAllocTransportProvidesGuess());
|
||||
}
|
||||
return super.getOption(option);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean setOption(ChannelOption<T> option, T value) {
|
||||
validate(option, value);
|
||||
|
||||
if (option == RCV_ALLOC_TRANSPORT_PROVIDES_GUESS) {
|
||||
setRcvAllocTransportProvidesGuess((Boolean) value);
|
||||
} else {
|
||||
return super.setOption(option, value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is {@code true} then the {@link RecvByteBufAllocator.Handle#guess()} will be overriden to always attempt
|
||||
* to read as many bytes as kqueue says are available.
|
||||
*/
|
||||
public KQueueChannelConfig setRcvAllocTransportProvidesGuess(boolean transportProvidesGuess) {
|
||||
this.transportProvidesGuess = transportProvidesGuess;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is {@code true} then the {@link RecvByteBufAllocator.Handle#guess()} will be overriden to always attempt
|
||||
* to read as many bytes as kqueue says are available.
|
||||
*/
|
||||
public boolean getRcvAllocTransportProvidesGuess() {
|
||||
return transportProvidesGuess;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueChannelConfig setConnectTimeoutMillis(int connectTimeoutMillis) {
|
||||
super.setConnectTimeoutMillis(connectTimeoutMillis);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public KQueueChannelConfig setMaxMessagesPerRead(int maxMessagesPerRead) {
|
||||
super.setMaxMessagesPerRead(maxMessagesPerRead);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueChannelConfig setWriteSpinCount(int writeSpinCount) {
|
||||
super.setWriteSpinCount(writeSpinCount);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueChannelConfig setAllocator(ByteBufAllocator allocator) {
|
||||
super.setAllocator(allocator);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator) {
|
||||
if (!(allocator.newHandle() instanceof RecvByteBufAllocator.ExtendedHandle)) {
|
||||
throw new IllegalArgumentException("allocator.newHandle() must return an object of type: " +
|
||||
RecvByteBufAllocator.ExtendedHandle.class);
|
||||
}
|
||||
super.setRecvByteBufAllocator(allocator);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueChannelConfig setAutoRead(boolean autoRead) {
|
||||
super.setAutoRead(autoRead);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public KQueueChannelConfig setWriteBufferHighWaterMark(int writeBufferHighWaterMark) {
|
||||
super.setWriteBufferHighWaterMark(writeBufferHighWaterMark);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public KQueueChannelConfig setWriteBufferLowWaterMark(int writeBufferLowWaterMark) {
|
||||
super.setWriteBufferLowWaterMark(writeBufferLowWaterMark);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueChannelConfig setWriteBufferWaterMark(WriteBufferWaterMark writeBufferWaterMark) {
|
||||
super.setWriteBufferWaterMark(writeBufferWaterMark);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueChannelConfig setMessageSizeEstimator(MessageSizeEstimator estimator) {
|
||||
super.setMessageSizeEstimator(estimator);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void autoReadCleared() {
|
||||
channel.clearReadFilter();
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.RecvByteBufAllocator;
|
||||
import io.netty.channel.unix.UnixChannelOption;
|
||||
import io.netty.util.internal.UnstableApi;
|
||||
|
||||
@UnstableApi
|
||||
public final class KQueueChannelOption<T> extends UnixChannelOption<T> {
|
||||
public static final ChannelOption<Integer> SO_SNDLOWAT = valueOf(KQueueChannelOption.class, "SO_SNDLOWAT");
|
||||
public static final ChannelOption<Boolean> TCP_NOPUSH = valueOf(KQueueChannelOption.class, "TCP_NOPUSH");
|
||||
public static final ChannelOption<AcceptFilter> SO_ACCEPTFILTER =
|
||||
valueOf(KQueueChannelOption.class, "SO_ACCEPTFILTER");
|
||||
/**
|
||||
* If this is {@code true} then the {@link RecvByteBufAllocator.Handle#guess()} will be overriden to always attempt
|
||||
* to read as many bytes as kqueue says are available.
|
||||
*/
|
||||
public static final ChannelOption<Boolean> RCV_ALLOC_TRANSPORT_PROVIDES_GUESS =
|
||||
valueOf(KQueueChannelOption.class, "RCV_ALLOC_TRANSPORT_PROVIDES_GUESS");
|
||||
|
||||
@SuppressWarnings({ "unused", "deprecation" })
|
||||
private KQueueChannelOption() {
|
||||
}
|
||||
}
|
@ -0,0 +1,552 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.buffer.CompositeByteBuf;
|
||||
import io.netty.channel.AddressedEnvelope;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelMetadata;
|
||||
import io.netty.channel.ChannelOutboundBuffer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import io.netty.channel.DefaultAddressedEnvelope;
|
||||
import io.netty.channel.socket.DatagramChannel;
|
||||
import io.netty.channel.socket.DatagramChannelConfig;
|
||||
import io.netty.channel.socket.DatagramPacket;
|
||||
import io.netty.channel.unix.DatagramSocketAddress;
|
||||
import io.netty.channel.unix.IovArray;
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import io.netty.util.internal.UnstableApi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.SocketException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.NotYetConnectedException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static io.netty.channel.kqueue.BsdSocket.newSocketDgram;
|
||||
import static io.netty.channel.unix.Limits.IOV_MAX;
|
||||
|
||||
@UnstableApi
|
||||
public final class KQueueDatagramChannel extends AbstractKQueueChannel implements DatagramChannel {
|
||||
private static final ChannelMetadata METADATA = new ChannelMetadata(true);
|
||||
private static final String EXPECTED_TYPES =
|
||||
" (expected: " + StringUtil.simpleClassName(DatagramPacket.class) + ", " +
|
||||
StringUtil.simpleClassName(AddressedEnvelope.class) + '<' +
|
||||
StringUtil.simpleClassName(ByteBuf.class) + ", " +
|
||||
StringUtil.simpleClassName(InetSocketAddress.class) + ">, " +
|
||||
StringUtil.simpleClassName(ByteBuf.class) + ')';
|
||||
|
||||
private volatile InetSocketAddress local;
|
||||
private volatile InetSocketAddress remote;
|
||||
private volatile boolean connected;
|
||||
private final KQueueDatagramChannelConfig config;
|
||||
|
||||
public KQueueDatagramChannel() {
|
||||
super(null, newSocketDgram(), false);
|
||||
config = new KQueueDatagramChannelConfig(this);
|
||||
}
|
||||
|
||||
KQueueDatagramChannel(BsdSocket socket, boolean active) {
|
||||
super(null, socket, active);
|
||||
config = new KQueueDatagramChannelConfig(this);
|
||||
// As we create an EpollDatagramChannel from a FileDescriptor we should try to obtain the remote and local
|
||||
// address from it. This is needed as the FileDescriptor may be bound already.
|
||||
local = socket.localAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress remoteAddress() {
|
||||
return (InetSocketAddress) super.remoteAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress localAddress() {
|
||||
return (InetSocketAddress) super.localAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelMetadata metadata() {
|
||||
return METADATA;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public boolean isActive() {
|
||||
return socket.isOpen() && (config.getActiveOnOpen() && isRegistered() || active);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return connected;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture joinGroup(InetAddress multicastAddress) {
|
||||
return joinGroup(multicastAddress, newPromise());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture joinGroup(InetAddress multicastAddress, ChannelPromise promise) {
|
||||
try {
|
||||
return joinGroup(
|
||||
multicastAddress,
|
||||
NetworkInterface.getByInetAddress(localAddress().getAddress()),
|
||||
null, promise);
|
||||
} catch (SocketException e) {
|
||||
promise.setFailure(e);
|
||||
}
|
||||
return promise;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture joinGroup(
|
||||
InetSocketAddress multicastAddress, NetworkInterface networkInterface) {
|
||||
return joinGroup(multicastAddress, networkInterface, newPromise());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture joinGroup(
|
||||
InetSocketAddress multicastAddress, NetworkInterface networkInterface,
|
||||
ChannelPromise promise) {
|
||||
return joinGroup(multicastAddress.getAddress(), networkInterface, null, promise);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture joinGroup(
|
||||
InetAddress multicastAddress, NetworkInterface networkInterface, InetAddress source) {
|
||||
return joinGroup(multicastAddress, networkInterface, source, newPromise());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture joinGroup(
|
||||
final InetAddress multicastAddress, final NetworkInterface networkInterface,
|
||||
final InetAddress source, final ChannelPromise promise) {
|
||||
|
||||
if (multicastAddress == null) {
|
||||
throw new NullPointerException("multicastAddress");
|
||||
}
|
||||
|
||||
if (networkInterface == null) {
|
||||
throw new NullPointerException("networkInterface");
|
||||
}
|
||||
|
||||
promise.setFailure(new UnsupportedOperationException("Multicast not supported"));
|
||||
return promise;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture leaveGroup(InetAddress multicastAddress) {
|
||||
return leaveGroup(multicastAddress, newPromise());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture leaveGroup(InetAddress multicastAddress, ChannelPromise promise) {
|
||||
try {
|
||||
return leaveGroup(
|
||||
multicastAddress, NetworkInterface.getByInetAddress(localAddress().getAddress()), null, promise);
|
||||
} catch (SocketException e) {
|
||||
promise.setFailure(e);
|
||||
}
|
||||
return promise;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture leaveGroup(
|
||||
InetSocketAddress multicastAddress, NetworkInterface networkInterface) {
|
||||
return leaveGroup(multicastAddress, networkInterface, newPromise());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture leaveGroup(
|
||||
InetSocketAddress multicastAddress,
|
||||
NetworkInterface networkInterface, ChannelPromise promise) {
|
||||
return leaveGroup(multicastAddress.getAddress(), networkInterface, null, promise);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture leaveGroup(
|
||||
InetAddress multicastAddress, NetworkInterface networkInterface, InetAddress source) {
|
||||
return leaveGroup(multicastAddress, networkInterface, source, newPromise());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture leaveGroup(
|
||||
final InetAddress multicastAddress, final NetworkInterface networkInterface, final InetAddress source,
|
||||
final ChannelPromise promise) {
|
||||
if (multicastAddress == null) {
|
||||
throw new NullPointerException("multicastAddress");
|
||||
}
|
||||
if (networkInterface == null) {
|
||||
throw new NullPointerException("networkInterface");
|
||||
}
|
||||
|
||||
promise.setFailure(new UnsupportedOperationException("Multicast not supported"));
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture block(
|
||||
InetAddress multicastAddress, NetworkInterface networkInterface,
|
||||
InetAddress sourceToBlock) {
|
||||
return block(multicastAddress, networkInterface, sourceToBlock, newPromise());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture block(
|
||||
final InetAddress multicastAddress, final NetworkInterface networkInterface,
|
||||
final InetAddress sourceToBlock, final ChannelPromise promise) {
|
||||
if (multicastAddress == null) {
|
||||
throw new NullPointerException("multicastAddress");
|
||||
}
|
||||
if (sourceToBlock == null) {
|
||||
throw new NullPointerException("sourceToBlock");
|
||||
}
|
||||
|
||||
if (networkInterface == null) {
|
||||
throw new NullPointerException("networkInterface");
|
||||
}
|
||||
promise.setFailure(new UnsupportedOperationException("Multicast not supported"));
|
||||
return promise;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture block(InetAddress multicastAddress, InetAddress sourceToBlock) {
|
||||
return block(multicastAddress, sourceToBlock, newPromise());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture block(
|
||||
InetAddress multicastAddress, InetAddress sourceToBlock, ChannelPromise promise) {
|
||||
try {
|
||||
return block(
|
||||
multicastAddress,
|
||||
NetworkInterface.getByInetAddress(localAddress().getAddress()),
|
||||
sourceToBlock, promise);
|
||||
} catch (Throwable e) {
|
||||
promise.setFailure(e);
|
||||
}
|
||||
return promise;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractKQueueUnsafe newUnsafe() {
|
||||
return new KQueueDatagramChannelUnsafe();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InetSocketAddress localAddress0() {
|
||||
return local;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InetSocketAddress remoteAddress0() {
|
||||
return remote;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doBind(SocketAddress localAddress) throws Exception {
|
||||
InetSocketAddress addr = (InetSocketAddress) localAddress;
|
||||
checkResolvable(addr);
|
||||
socket.bind(addr);
|
||||
local = socket.localAddress();
|
||||
active = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doWrite(ChannelOutboundBuffer in) throws Exception {
|
||||
for (;;) {
|
||||
Object msg = in.current();
|
||||
if (msg == null) {
|
||||
// Wrote all messages.
|
||||
writeFilter(false);
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
boolean done = false;
|
||||
for (int i = config().getWriteSpinCount(); i > 0; --i) {
|
||||
if (doWriteMessage(msg)) {
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (done) {
|
||||
in.remove();
|
||||
} else {
|
||||
// Did not write all messages.
|
||||
writeFilter(true);
|
||||
break;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// Continue on write error as a DatagramChannel can write to multiple remote peers
|
||||
//
|
||||
// See https://github.com/netty/netty/issues/2665
|
||||
in.remove(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean doWriteMessage(Object msg) throws Exception {
|
||||
final ByteBuf data;
|
||||
InetSocketAddress remoteAddress;
|
||||
if (msg instanceof AddressedEnvelope) {
|
||||
@SuppressWarnings("unchecked")
|
||||
AddressedEnvelope<ByteBuf, InetSocketAddress> envelope =
|
||||
(AddressedEnvelope<ByteBuf, InetSocketAddress>) msg;
|
||||
data = envelope.content();
|
||||
remoteAddress = envelope.recipient();
|
||||
} else {
|
||||
data = (ByteBuf) msg;
|
||||
remoteAddress = null;
|
||||
}
|
||||
|
||||
final int dataLen = data.readableBytes();
|
||||
if (dataLen == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (remoteAddress == null) {
|
||||
remoteAddress = remote;
|
||||
if (remoteAddress == null) {
|
||||
throw new NotYetConnectedException();
|
||||
}
|
||||
}
|
||||
|
||||
final int writtenBytes;
|
||||
if (data.hasMemoryAddress()) {
|
||||
long memoryAddress = data.memoryAddress();
|
||||
writtenBytes = socket.sendToAddress(memoryAddress, data.readerIndex(), data.writerIndex(),
|
||||
remoteAddress.getAddress(), remoteAddress.getPort());
|
||||
} else if (data instanceof CompositeByteBuf) {
|
||||
IovArray array = ((KQueueEventLoop) eventLoop()).cleanArray();
|
||||
array.add(data);
|
||||
int cnt = array.count();
|
||||
assert cnt != 0;
|
||||
|
||||
writtenBytes = socket.sendToAddresses(array.memoryAddress(0),
|
||||
cnt, remoteAddress.getAddress(), remoteAddress.getPort());
|
||||
} else {
|
||||
ByteBuffer nioData = data.internalNioBuffer(data.readerIndex(), data.readableBytes());
|
||||
writtenBytes = socket.sendTo(nioData, nioData.position(), nioData.limit(),
|
||||
remoteAddress.getAddress(), remoteAddress.getPort());
|
||||
}
|
||||
|
||||
return writtenBytes > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object filterOutboundMessage(Object msg) {
|
||||
if (msg instanceof DatagramPacket) {
|
||||
DatagramPacket packet = (DatagramPacket) msg;
|
||||
ByteBuf content = packet.content();
|
||||
if (content.hasMemoryAddress()) {
|
||||
return msg;
|
||||
}
|
||||
|
||||
if (content.isDirect() && content instanceof CompositeByteBuf) {
|
||||
// Special handling of CompositeByteBuf to reduce memory copies if some of the Components
|
||||
// in the CompositeByteBuf are backed by a memoryAddress.
|
||||
CompositeByteBuf comp = (CompositeByteBuf) content;
|
||||
if (comp.isDirect() && comp.nioBufferCount() <= IOV_MAX) {
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
// We can only handle direct buffers so we need to copy if a non direct is
|
||||
// passed to write.
|
||||
return new DatagramPacket(newDirectBuffer(packet, content), packet.recipient());
|
||||
}
|
||||
|
||||
if (msg instanceof ByteBuf) {
|
||||
ByteBuf buf = (ByteBuf) msg;
|
||||
if (!buf.hasMemoryAddress() && (PlatformDependent.hasUnsafe() || !buf.isDirect())) {
|
||||
if (buf instanceof CompositeByteBuf) {
|
||||
// Special handling of CompositeByteBuf to reduce memory copies if some of the Components
|
||||
// in the CompositeByteBuf are backed by a memoryAddress.
|
||||
CompositeByteBuf comp = (CompositeByteBuf) buf;
|
||||
if (!comp.isDirect() || comp.nioBufferCount() > IOV_MAX) {
|
||||
// more then 1024 buffers for gathering writes so just do a memory copy.
|
||||
buf = newDirectBuffer(buf);
|
||||
assert buf.hasMemoryAddress();
|
||||
}
|
||||
} else {
|
||||
// We can only handle buffers with memory address so we need to copy if a non direct is
|
||||
// passed to write.
|
||||
buf = newDirectBuffer(buf);
|
||||
assert buf.hasMemoryAddress();
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
if (msg instanceof AddressedEnvelope) {
|
||||
@SuppressWarnings("unchecked")
|
||||
AddressedEnvelope<Object, SocketAddress> e = (AddressedEnvelope<Object, SocketAddress>) msg;
|
||||
if (e.content() instanceof ByteBuf &&
|
||||
(e.recipient() == null || e.recipient() instanceof InetSocketAddress)) {
|
||||
|
||||
ByteBuf content = (ByteBuf) e.content();
|
||||
if (content.hasMemoryAddress()) {
|
||||
return e;
|
||||
}
|
||||
if (content instanceof CompositeByteBuf) {
|
||||
// Special handling of CompositeByteBuf to reduce memory copies if some of the Components
|
||||
// in the CompositeByteBuf are backed by a memoryAddress.
|
||||
CompositeByteBuf comp = (CompositeByteBuf) content;
|
||||
if (comp.isDirect() && comp.nioBufferCount() <= IOV_MAX) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
// We can only handle direct buffers so we need to copy if a non direct is
|
||||
// passed to write.
|
||||
return new DefaultAddressedEnvelope<ByteBuf, InetSocketAddress>(
|
||||
newDirectBuffer(e, content), (InetSocketAddress) e.recipient());
|
||||
}
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException(
|
||||
"unsupported message type: " + StringUtil.simpleClassName(msg) + EXPECTED_TYPES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueDatagramChannelConfig config() {
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doDisconnect() throws Exception {
|
||||
connected = false;
|
||||
}
|
||||
|
||||
final class KQueueDatagramChannelUnsafe extends AbstractKQueueUnsafe {
|
||||
private final List<Object> readBuf = new ArrayList<Object>();
|
||||
|
||||
@Override
|
||||
public void connect(SocketAddress remote, SocketAddress local, ChannelPromise channelPromise) {
|
||||
boolean success = false;
|
||||
try {
|
||||
try {
|
||||
boolean wasActive = isActive();
|
||||
InetSocketAddress remoteAddress = (InetSocketAddress) remote;
|
||||
if (local != null) {
|
||||
InetSocketAddress localAddress = (InetSocketAddress) local;
|
||||
doBind(localAddress);
|
||||
}
|
||||
|
||||
checkResolvable(remoteAddress);
|
||||
KQueueDatagramChannel.this.remote = remoteAddress;
|
||||
KQueueDatagramChannel.this.local = socket.localAddress();
|
||||
success = true;
|
||||
|
||||
// First notify the promise before notifying the handler.
|
||||
channelPromise.trySuccess();
|
||||
|
||||
// Regardless if the connection attempt was cancelled, channelActive() event should be triggered,
|
||||
// because what happened is what happened.
|
||||
if (!wasActive && isActive()) {
|
||||
pipeline().fireChannelActive();
|
||||
}
|
||||
} finally {
|
||||
if (!success) {
|
||||
doClose();
|
||||
} else {
|
||||
connected = true;
|
||||
}
|
||||
}
|
||||
} catch (Throwable cause) {
|
||||
channelPromise.tryFailure(cause);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void readReady(KQueueRecvByteAllocatorHandle allocHandle) {
|
||||
assert eventLoop().inEventLoop();
|
||||
final DatagramChannelConfig config = config();
|
||||
if (shouldBreakReadReady(config)) {
|
||||
clearReadFilter0();
|
||||
return;
|
||||
}
|
||||
final ChannelPipeline pipeline = pipeline();
|
||||
final ByteBufAllocator allocator = config.getAllocator();
|
||||
allocHandle.reset(config);
|
||||
readReadyBefore();
|
||||
|
||||
Throwable exception = null;
|
||||
try {
|
||||
ByteBuf data = null;
|
||||
try {
|
||||
do {
|
||||
data = allocHandle.allocate(allocator);
|
||||
allocHandle.attemptedBytesRead(data.writableBytes());
|
||||
final DatagramSocketAddress remoteAddress;
|
||||
if (data.hasMemoryAddress()) {
|
||||
// has a memory address so use optimized call
|
||||
remoteAddress = socket.recvFromAddress(data.memoryAddress(), data.writerIndex(),
|
||||
data.capacity());
|
||||
} else {
|
||||
ByteBuffer nioData = data.internalNioBuffer(data.writerIndex(), data.writableBytes());
|
||||
remoteAddress = socket.recvFrom(nioData, nioData.position(), nioData.limit());
|
||||
}
|
||||
|
||||
if (remoteAddress == null) {
|
||||
allocHandle.lastBytesRead(-1);
|
||||
data.release();
|
||||
data = null;
|
||||
break;
|
||||
}
|
||||
|
||||
allocHandle.incMessagesRead(1);
|
||||
allocHandle.lastBytesRead(remoteAddress.receivedAmount());
|
||||
data.writerIndex(data.writerIndex() + allocHandle.lastBytesRead());
|
||||
|
||||
readBuf.add(new DatagramPacket(data, (InetSocketAddress) localAddress(), remoteAddress));
|
||||
data = null;
|
||||
} while (allocHandle.continueReading());
|
||||
} catch (Throwable t) {
|
||||
if (data != null) {
|
||||
data.release();
|
||||
}
|
||||
exception = t;
|
||||
}
|
||||
|
||||
int size = readBuf.size();
|
||||
for (int i = 0; i < size; i ++) {
|
||||
readPending = false;
|
||||
pipeline.fireChannelRead(readBuf.get(i));
|
||||
}
|
||||
readBuf.clear();
|
||||
allocHandle.readComplete();
|
||||
pipeline.fireChannelReadComplete();
|
||||
|
||||
if (exception != null) {
|
||||
pipeline.fireExceptionCaught(exception);
|
||||
}
|
||||
} finally {
|
||||
readReadyFinally(config);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,387 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.channel.ChannelException;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.FixedRecvByteBufAllocator;
|
||||
import io.netty.channel.MessageSizeEstimator;
|
||||
import io.netty.channel.RecvByteBufAllocator;
|
||||
import io.netty.channel.WriteBufferWaterMark;
|
||||
import io.netty.channel.socket.DatagramChannelConfig;
|
||||
import io.netty.util.internal.UnstableApi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.util.Map;
|
||||
|
||||
import static io.netty.channel.ChannelOption.DATAGRAM_CHANNEL_ACTIVE_ON_REGISTRATION;
|
||||
import static io.netty.channel.ChannelOption.IP_MULTICAST_ADDR;
|
||||
import static io.netty.channel.ChannelOption.IP_MULTICAST_IF;
|
||||
import static io.netty.channel.ChannelOption.IP_MULTICAST_LOOP_DISABLED;
|
||||
import static io.netty.channel.ChannelOption.IP_MULTICAST_TTL;
|
||||
import static io.netty.channel.ChannelOption.IP_TOS;
|
||||
import static io.netty.channel.ChannelOption.SO_BROADCAST;
|
||||
import static io.netty.channel.ChannelOption.SO_RCVBUF;
|
||||
import static io.netty.channel.ChannelOption.SO_REUSEADDR;
|
||||
import static io.netty.channel.ChannelOption.SO_SNDBUF;
|
||||
import static io.netty.channel.unix.UnixChannelOption.SO_REUSEPORT;
|
||||
|
||||
@UnstableApi
|
||||
public final class KQueueDatagramChannelConfig extends KQueueChannelConfig implements DatagramChannelConfig {
|
||||
private static final RecvByteBufAllocator DEFAULT_RCVBUF_ALLOCATOR = new FixedRecvByteBufAllocator(2048);
|
||||
private final KQueueDatagramChannel datagramChannel;
|
||||
private boolean activeOnOpen;
|
||||
|
||||
KQueueDatagramChannelConfig(KQueueDatagramChannel channel) {
|
||||
super(channel);
|
||||
this.datagramChannel = channel;
|
||||
setRecvByteBufAllocator(DEFAULT_RCVBUF_ALLOCATOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public Map<ChannelOption<?>, Object> getOptions() {
|
||||
return getOptions(
|
||||
super.getOptions(),
|
||||
SO_BROADCAST, SO_RCVBUF, SO_SNDBUF, SO_REUSEADDR, IP_MULTICAST_LOOP_DISABLED,
|
||||
IP_MULTICAST_ADDR, IP_MULTICAST_IF, IP_MULTICAST_TTL,
|
||||
IP_TOS, DATAGRAM_CHANNEL_ACTIVE_ON_REGISTRATION, SO_REUSEPORT);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "deprecation" })
|
||||
@Override
|
||||
public <T> T getOption(ChannelOption<T> option) {
|
||||
if (option == SO_BROADCAST) {
|
||||
return (T) Boolean.valueOf(isBroadcast());
|
||||
}
|
||||
if (option == SO_RCVBUF) {
|
||||
return (T) Integer.valueOf(getReceiveBufferSize());
|
||||
}
|
||||
if (option == SO_SNDBUF) {
|
||||
return (T) Integer.valueOf(getSendBufferSize());
|
||||
}
|
||||
if (option == SO_REUSEADDR) {
|
||||
return (T) Boolean.valueOf(isReuseAddress());
|
||||
}
|
||||
if (option == IP_MULTICAST_LOOP_DISABLED) {
|
||||
return (T) Boolean.valueOf(isLoopbackModeDisabled());
|
||||
}
|
||||
if (option == IP_MULTICAST_ADDR) {
|
||||
return (T) getInterface();
|
||||
}
|
||||
if (option == IP_MULTICAST_IF) {
|
||||
return (T) getNetworkInterface();
|
||||
}
|
||||
if (option == IP_MULTICAST_TTL) {
|
||||
return (T) Integer.valueOf(getTimeToLive());
|
||||
}
|
||||
if (option == IP_TOS) {
|
||||
return (T) Integer.valueOf(getTrafficClass());
|
||||
}
|
||||
if (option == DATAGRAM_CHANNEL_ACTIVE_ON_REGISTRATION) {
|
||||
return (T) Boolean.valueOf(activeOnOpen);
|
||||
}
|
||||
if (option == SO_REUSEPORT) {
|
||||
return (T) Boolean.valueOf(isReusePort());
|
||||
}
|
||||
return super.getOption(option);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public <T> boolean setOption(ChannelOption<T> option, T value) {
|
||||
validate(option, value);
|
||||
|
||||
if (option == SO_BROADCAST) {
|
||||
setBroadcast((Boolean) value);
|
||||
} else if (option == SO_RCVBUF) {
|
||||
setReceiveBufferSize((Integer) value);
|
||||
} else if (option == SO_SNDBUF) {
|
||||
setSendBufferSize((Integer) value);
|
||||
} else if (option == SO_REUSEADDR) {
|
||||
setReuseAddress((Boolean) value);
|
||||
} else if (option == IP_MULTICAST_LOOP_DISABLED) {
|
||||
setLoopbackModeDisabled((Boolean) value);
|
||||
} else if (option == IP_MULTICAST_ADDR) {
|
||||
setInterface((InetAddress) value);
|
||||
} else if (option == IP_MULTICAST_IF) {
|
||||
setNetworkInterface((NetworkInterface) value);
|
||||
} else if (option == IP_MULTICAST_TTL) {
|
||||
setTimeToLive((Integer) value);
|
||||
} else if (option == IP_TOS) {
|
||||
setTrafficClass((Integer) value);
|
||||
} else if (option == DATAGRAM_CHANNEL_ACTIVE_ON_REGISTRATION) {
|
||||
setActiveOnOpen((Boolean) value);
|
||||
} else if (option == SO_REUSEPORT) {
|
||||
setReusePort((Boolean) value);
|
||||
} else {
|
||||
return super.setOption(option, value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void setActiveOnOpen(boolean activeOnOpen) {
|
||||
if (channel.isRegistered()) {
|
||||
throw new IllegalStateException("Can only changed before channel was registered");
|
||||
}
|
||||
this.activeOnOpen = activeOnOpen;
|
||||
}
|
||||
|
||||
boolean getActiveOnOpen() {
|
||||
return activeOnOpen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the SO_REUSEPORT option is set.
|
||||
*/
|
||||
public boolean isReusePort() {
|
||||
try {
|
||||
return datagramChannel.socket.isReusePort();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the SO_REUSEPORT option on the underlying Channel. This will allow to bind multiple
|
||||
* {@link KQueueSocketChannel}s to the same port and so accept connections with multiple threads.
|
||||
*
|
||||
* Be aware this method needs be called before {@link KQueueDatagramChannel#bind(java.net.SocketAddress)} to have
|
||||
* any affect.
|
||||
*/
|
||||
public KQueueDatagramChannelConfig setReusePort(boolean reusePort) {
|
||||
try {
|
||||
datagramChannel.socket.setReusePort(reusePort);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueDatagramChannelConfig setRcvAllocTransportProvidesGuess(boolean transportProvidesGuess) {
|
||||
super.setRcvAllocTransportProvidesGuess(transportProvidesGuess);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueDatagramChannelConfig setMessageSizeEstimator(MessageSizeEstimator estimator) {
|
||||
super.setMessageSizeEstimator(estimator);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public KQueueDatagramChannelConfig setWriteBufferLowWaterMark(int writeBufferLowWaterMark) {
|
||||
super.setWriteBufferLowWaterMark(writeBufferLowWaterMark);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public KQueueDatagramChannelConfig setWriteBufferHighWaterMark(int writeBufferHighWaterMark) {
|
||||
super.setWriteBufferHighWaterMark(writeBufferHighWaterMark);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueDatagramChannelConfig setWriteBufferWaterMark(WriteBufferWaterMark writeBufferWaterMark) {
|
||||
super.setWriteBufferWaterMark(writeBufferWaterMark);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueDatagramChannelConfig setAutoClose(boolean autoClose) {
|
||||
super.setAutoClose(autoClose);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueDatagramChannelConfig setAutoRead(boolean autoRead) {
|
||||
super.setAutoRead(autoRead);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueDatagramChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator) {
|
||||
super.setRecvByteBufAllocator(allocator);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueDatagramChannelConfig setWriteSpinCount(int writeSpinCount) {
|
||||
super.setWriteSpinCount(writeSpinCount);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueDatagramChannelConfig setAllocator(ByteBufAllocator allocator) {
|
||||
super.setAllocator(allocator);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueDatagramChannelConfig setConnectTimeoutMillis(int connectTimeoutMillis) {
|
||||
super.setConnectTimeoutMillis(connectTimeoutMillis);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public KQueueDatagramChannelConfig setMaxMessagesPerRead(int maxMessagesPerRead) {
|
||||
super.setMaxMessagesPerRead(maxMessagesPerRead);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSendBufferSize() {
|
||||
try {
|
||||
return datagramChannel.socket.getSendBufferSize();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueDatagramChannelConfig setSendBufferSize(int sendBufferSize) {
|
||||
try {
|
||||
datagramChannel.socket.setSendBufferSize(sendBufferSize);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getReceiveBufferSize() {
|
||||
try {
|
||||
return datagramChannel.socket.getReceiveBufferSize();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueDatagramChannelConfig setReceiveBufferSize(int receiveBufferSize) {
|
||||
try {
|
||||
datagramChannel.socket.setReceiveBufferSize(receiveBufferSize);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTrafficClass() {
|
||||
try {
|
||||
return datagramChannel.socket.getTrafficClass();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueDatagramChannelConfig setTrafficClass(int trafficClass) {
|
||||
try {
|
||||
datagramChannel.socket.setTrafficClass(trafficClass);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReuseAddress() {
|
||||
try {
|
||||
return datagramChannel.socket.isReuseAddress();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueDatagramChannelConfig setReuseAddress(boolean reuseAddress) {
|
||||
try {
|
||||
datagramChannel.socket.setReuseAddress(reuseAddress);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBroadcast() {
|
||||
try {
|
||||
return datagramChannel.socket.isBroadcast();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueDatagramChannelConfig setBroadcast(boolean broadcast) {
|
||||
try {
|
||||
datagramChannel.socket.setBroadcast(broadcast);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoopbackModeDisabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatagramChannelConfig setLoopbackModeDisabled(boolean loopbackModeDisabled) {
|
||||
throw new UnsupportedOperationException("Multicast not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTimeToLive() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueDatagramChannelConfig setTimeToLive(int ttl) {
|
||||
throw new UnsupportedOperationException("Multicast not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetAddress getInterface() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueDatagramChannelConfig setInterface(InetAddress interfaceAddress) {
|
||||
throw new UnsupportedOperationException("Multicast not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public NetworkInterface getNetworkInterface() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueDatagramChannelConfig setNetworkInterface(NetworkInterface networkInterface) {
|
||||
throw new UnsupportedOperationException("Multicast not supported");
|
||||
}
|
||||
}
|
@ -0,0 +1,183 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelConfig;
|
||||
import io.netty.channel.ChannelOutboundBuffer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.unix.DomainSocketAddress;
|
||||
import io.netty.channel.unix.DomainSocketChannel;
|
||||
import io.netty.channel.unix.FileDescriptor;
|
||||
import io.netty.channel.unix.PeerCredentials;
|
||||
import io.netty.util.internal.UnstableApi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.SocketAddress;
|
||||
|
||||
import static io.netty.channel.kqueue.BsdSocket.newSocketDomain;
|
||||
|
||||
@UnstableApi
|
||||
public final class KQueueDomainSocketChannel extends AbstractKQueueStreamChannel implements DomainSocketChannel {
|
||||
private final KQueueDomainSocketChannelConfig config = new KQueueDomainSocketChannelConfig(this);
|
||||
|
||||
private volatile DomainSocketAddress local;
|
||||
private volatile DomainSocketAddress remote;
|
||||
|
||||
public KQueueDomainSocketChannel() {
|
||||
super(null, newSocketDomain(), false);
|
||||
}
|
||||
|
||||
KQueueDomainSocketChannel(Channel parent, BsdSocket fd) {
|
||||
super(parent, fd, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractKQueueUnsafe newUnsafe() {
|
||||
return new KQueueDomainUnsafe();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DomainSocketAddress localAddress0() {
|
||||
return local;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DomainSocketAddress remoteAddress0() {
|
||||
return remote;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doBind(SocketAddress localAddress) throws Exception {
|
||||
socket.bind(localAddress);
|
||||
local = (DomainSocketAddress) localAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueDomainSocketChannelConfig config() {
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
|
||||
if (super.doConnect(remoteAddress, localAddress)) {
|
||||
local = (DomainSocketAddress) localAddress;
|
||||
remote = (DomainSocketAddress) remoteAddress;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainSocketAddress remoteAddress() {
|
||||
return (DomainSocketAddress) super.remoteAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainSocketAddress localAddress() {
|
||||
return (DomainSocketAddress) super.localAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doWriteSingle(ChannelOutboundBuffer in, int writeSpinCount) throws Exception {
|
||||
Object msg = in.current();
|
||||
if (msg instanceof FileDescriptor && socket.sendFd(((FileDescriptor) msg).intValue()) > 0) {
|
||||
// File descriptor was written, so remove it.
|
||||
in.remove();
|
||||
return true;
|
||||
}
|
||||
return super.doWriteSingle(in, writeSpinCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object filterOutboundMessage(Object msg) {
|
||||
if (msg instanceof FileDescriptor) {
|
||||
return msg;
|
||||
}
|
||||
return super.filterOutboundMessage(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unix credentials (uid, gid, pid) of the peer
|
||||
* <a href=http://man7.org/linux/man-pages/man7/socket.7.html>SO_PEERCRED</a>
|
||||
*/
|
||||
@UnstableApi
|
||||
public PeerCredentials peerCredentials() throws IOException {
|
||||
return socket.getPeerCredentials();
|
||||
}
|
||||
|
||||
private final class KQueueDomainUnsafe extends KQueueStreamUnsafe {
|
||||
@Override
|
||||
void readReady(KQueueRecvByteAllocatorHandle allocHandle) {
|
||||
switch (config().getReadMode()) {
|
||||
case BYTES:
|
||||
super.readReady(allocHandle);
|
||||
break;
|
||||
case FILE_DESCRIPTORS:
|
||||
readReadyFd();
|
||||
break;
|
||||
default:
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
private void readReadyFd() {
|
||||
if (socket.isInputShutdown()) {
|
||||
super.clearReadFilter0();
|
||||
return;
|
||||
}
|
||||
final ChannelConfig config = config();
|
||||
final KQueueRecvByteAllocatorHandle allocHandle = recvBufAllocHandle();
|
||||
|
||||
final ChannelPipeline pipeline = pipeline();
|
||||
allocHandle.reset(config);
|
||||
readReadyBefore();
|
||||
|
||||
try {
|
||||
readLoop: do {
|
||||
// lastBytesRead represents the fd. We use lastBytesRead because it must be set so that the
|
||||
// KQueueRecvByteAllocatorHandle knows if it should try to read again or not when autoRead is
|
||||
// enabled.
|
||||
int recvFd = socket.recvFd();
|
||||
switch(recvFd) {
|
||||
case 0:
|
||||
allocHandle.lastBytesRead(0);
|
||||
break readLoop;
|
||||
case -1:
|
||||
allocHandle.lastBytesRead(-1);
|
||||
close(voidPromise());
|
||||
return;
|
||||
default:
|
||||
allocHandle.lastBytesRead(1);
|
||||
allocHandle.incMessagesRead(1);
|
||||
readPending = false;
|
||||
pipeline.fireChannelRead(new FileDescriptor(recvFd));
|
||||
break;
|
||||
}
|
||||
} while (allocHandle.continueReading());
|
||||
|
||||
allocHandle.readComplete();
|
||||
pipeline.fireChannelReadComplete();
|
||||
} catch (Throwable t) {
|
||||
allocHandle.readComplete();
|
||||
pipeline.fireChannelReadComplete();
|
||||
pipeline.fireExceptionCaught(t);
|
||||
} finally {
|
||||
readReadyFinally(config);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.MessageSizeEstimator;
|
||||
import io.netty.channel.RecvByteBufAllocator;
|
||||
import io.netty.channel.WriteBufferWaterMark;
|
||||
import io.netty.channel.unix.DomainSocketChannelConfig;
|
||||
import io.netty.channel.unix.DomainSocketReadMode;
|
||||
import io.netty.util.internal.UnstableApi;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static io.netty.channel.unix.UnixChannelOption.DOMAIN_SOCKET_READ_MODE;
|
||||
|
||||
@UnstableApi
|
||||
public final class KQueueDomainSocketChannelConfig extends KQueueChannelConfig implements DomainSocketChannelConfig {
|
||||
private volatile DomainSocketReadMode mode = DomainSocketReadMode.BYTES;
|
||||
|
||||
KQueueDomainSocketChannelConfig(AbstractKQueueChannel channel) {
|
||||
super(channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<ChannelOption<?>, Object> getOptions() {
|
||||
return getOptions(super.getOptions(), DOMAIN_SOCKET_READ_MODE);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T getOption(ChannelOption<T> option) {
|
||||
if (option == DOMAIN_SOCKET_READ_MODE) {
|
||||
return (T) getReadMode();
|
||||
}
|
||||
return super.getOption(option);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean setOption(ChannelOption<T> option, T value) {
|
||||
validate(option, value);
|
||||
|
||||
if (option == DOMAIN_SOCKET_READ_MODE) {
|
||||
setReadMode((DomainSocketReadMode) value);
|
||||
} else {
|
||||
return super.setOption(option, value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueDomainSocketChannelConfig setRcvAllocTransportProvidesGuess(boolean transportProvidesGuess) {
|
||||
super.setRcvAllocTransportProvidesGuess(transportProvidesGuess);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public KQueueDomainSocketChannelConfig setMaxMessagesPerRead(int maxMessagesPerRead) {
|
||||
super.setMaxMessagesPerRead(maxMessagesPerRead);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueDomainSocketChannelConfig setConnectTimeoutMillis(int connectTimeoutMillis) {
|
||||
super.setConnectTimeoutMillis(connectTimeoutMillis);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueDomainSocketChannelConfig setWriteSpinCount(int writeSpinCount) {
|
||||
super.setWriteSpinCount(writeSpinCount);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueDomainSocketChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator) {
|
||||
super.setRecvByteBufAllocator(allocator);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueDomainSocketChannelConfig setAllocator(ByteBufAllocator allocator) {
|
||||
super.setAllocator(allocator);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueDomainSocketChannelConfig setAutoClose(boolean autoClose) {
|
||||
super.setAutoClose(autoClose);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueDomainSocketChannelConfig setMessageSizeEstimator(MessageSizeEstimator estimator) {
|
||||
super.setMessageSizeEstimator(estimator);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public KQueueDomainSocketChannelConfig setWriteBufferLowWaterMark(int writeBufferLowWaterMark) {
|
||||
super.setWriteBufferLowWaterMark(writeBufferLowWaterMark);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public KQueueDomainSocketChannelConfig setWriteBufferHighWaterMark(int writeBufferHighWaterMark) {
|
||||
super.setWriteBufferHighWaterMark(writeBufferHighWaterMark);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueDomainSocketChannelConfig setWriteBufferWaterMark(WriteBufferWaterMark writeBufferWaterMark) {
|
||||
super.setWriteBufferWaterMark(writeBufferWaterMark);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueDomainSocketChannelConfig setAutoRead(boolean autoRead) {
|
||||
super.setAutoRead(autoRead);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueDomainSocketChannelConfig setReadMode(DomainSocketReadMode mode) {
|
||||
if (mode == null) {
|
||||
throw new NullPointerException("mode");
|
||||
}
|
||||
this.mode = mode;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainSocketReadMode getReadMode() {
|
||||
return mode;
|
||||
}
|
||||
}
|
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
|
||||
/**
|
||||
* Represents an array of kevent structures, backed by offheap memory.
|
||||
*
|
||||
* struct kevent {
|
||||
* uintptr_t ident;
|
||||
* short keventFilter;
|
||||
* u_short flags;
|
||||
* u_int fflags;
|
||||
* intptr_t data;
|
||||
* void *udata;
|
||||
* };
|
||||
*/
|
||||
final class KQueueEventArray {
|
||||
private static final int KQUEUE_EVENT_SIZE = Native.sizeofKEvent();
|
||||
private static final int KQUEUE_IDENT_OFFSET = Native.offsetofKEventIdent();
|
||||
private static final int KQUEUE_FILTER_OFFSET = Native.offsetofKEventFilter();
|
||||
private static final int KQUEUE_FFLAGS_OFFSET = Native.offsetofKEventFFlags();
|
||||
private static final int KQUEUE_FLAGS_OFFSET = Native.offsetofKEventFlags();
|
||||
private static final int KQUEUE_DATA_OFFSET = Native.offsetofKeventData();
|
||||
|
||||
private long memoryAddress;
|
||||
private int size;
|
||||
private int capacity;
|
||||
|
||||
KQueueEventArray(int capacity) {
|
||||
if (capacity < 1) {
|
||||
throw new IllegalArgumentException("capacity must be >= 1 but was " + capacity);
|
||||
}
|
||||
memoryAddress = PlatformDependent.allocateMemory(capacity * KQUEUE_EVENT_SIZE);
|
||||
this.capacity = capacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@code memoryAddress} which points to the start of this {@link KQueueEventArray}.
|
||||
*/
|
||||
long memoryAddress() {
|
||||
return memoryAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the capacity of the {@link KQueueEventArray} which represent the maximum number of {@code kevent}s
|
||||
* that can be stored in it.
|
||||
*/
|
||||
int capacity() {
|
||||
return capacity;
|
||||
}
|
||||
|
||||
int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
size = 0;
|
||||
}
|
||||
|
||||
void evSet(AbstractKQueueChannel ch, short filter, short flags, int fflags) {
|
||||
checkSize();
|
||||
evSet(getKEventOffset(size++), ch, ch.socket.intValue(), filter, flags, fflags);
|
||||
}
|
||||
|
||||
private void checkSize() {
|
||||
if (size == capacity) {
|
||||
realloc(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increase the storage of this {@link KQueueEventArray}.
|
||||
*/
|
||||
void realloc(boolean throwIfFail) {
|
||||
// Double the capacity while it is "sufficiently small", and otherwise increase by 50%.
|
||||
int newLength = capacity <= 65536 ? capacity << 1 : capacity + capacity >> 1;
|
||||
long newMemoryAddress = PlatformDependent.reallocateMemory(memoryAddress, newLength * KQUEUE_EVENT_SIZE);
|
||||
if (newMemoryAddress != 0) {
|
||||
memoryAddress = newMemoryAddress;
|
||||
capacity = newLength;
|
||||
return;
|
||||
}
|
||||
if (throwIfFail) {
|
||||
throw new OutOfMemoryError("unable to allocate " + newLength + " new bytes! Existing capacity is: "
|
||||
+ capacity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Free this {@link KQueueEventArray}. Any usage after calling this method may segfault the JVM!
|
||||
*/
|
||||
void free() {
|
||||
PlatformDependent.freeMemory(memoryAddress);
|
||||
memoryAddress = size = capacity = 0;
|
||||
}
|
||||
|
||||
long getKEventOffset(int index) {
|
||||
return memoryAddress + index * KQUEUE_EVENT_SIZE;
|
||||
}
|
||||
|
||||
short flags(int index) {
|
||||
return PlatformDependent.getShort(getKEventOffset(index) + KQUEUE_FLAGS_OFFSET);
|
||||
}
|
||||
|
||||
short filter(int index) {
|
||||
return PlatformDependent.getShort(getKEventOffset(index) + KQUEUE_FILTER_OFFSET);
|
||||
}
|
||||
|
||||
short fflags(int index) {
|
||||
return PlatformDependent.getShort(getKEventOffset(index) + KQUEUE_FFLAGS_OFFSET);
|
||||
}
|
||||
|
||||
int fd(int index) {
|
||||
return PlatformDependent.getInt(getKEventOffset(index) + KQUEUE_IDENT_OFFSET);
|
||||
}
|
||||
|
||||
long data(int index) {
|
||||
return PlatformDependent.getLong(getKEventOffset(index) + KQUEUE_DATA_OFFSET);
|
||||
}
|
||||
|
||||
AbstractKQueueChannel channel(int index) {
|
||||
return getChannel(getKEventOffset(index));
|
||||
}
|
||||
|
||||
private static native void evSet(long keventAddress, AbstractKQueueChannel ch,
|
||||
int ident, short filter, short flags, int fflags);
|
||||
private static native AbstractKQueueChannel getChannel(long keventAddress);
|
||||
static native void deleteGlobalRefs(long channelAddressStart, long channelAddressEnd);
|
||||
}
|
@ -0,0 +1,370 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.channel.EventLoop;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.SelectStrategy;
|
||||
import io.netty.channel.SingleThreadEventLoop;
|
||||
import io.netty.channel.kqueue.AbstractKQueueChannel.AbstractKQueueUnsafe;
|
||||
import io.netty.channel.unix.FileDescriptor;
|
||||
import io.netty.channel.unix.IovArray;
|
||||
import io.netty.util.IntSupplier;
|
||||
import io.netty.util.concurrent.RejectedExecutionHandler;
|
||||
import io.netty.util.internal.ObjectUtil;
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
import io.netty.util.internal.logging.InternalLogger;
|
||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
|
||||
|
||||
import static io.netty.channel.kqueue.KQueueEventArray.deleteGlobalRefs;
|
||||
import static java.lang.Math.min;
|
||||
|
||||
/**
|
||||
* {@link EventLoop} which uses kqueue under the covers. Only works on BSD!
|
||||
*/
|
||||
final class KQueueEventLoop extends SingleThreadEventLoop {
|
||||
private static final InternalLogger logger = InternalLoggerFactory.getInstance(KQueueEventLoop.class);
|
||||
private static final AtomicIntegerFieldUpdater<KQueueEventLoop> WAKEN_UP_UPDATER =
|
||||
AtomicIntegerFieldUpdater.newUpdater(KQueueEventLoop.class, "wakenUp");
|
||||
private static final int KQUEUE_WAKE_UP_IDENT = 0;
|
||||
|
||||
static {
|
||||
// Ensure JNI is initialized by the time this class is loaded by this time!
|
||||
// We use unix-common methods in this class which are backed by JNI methods.
|
||||
KQueue.ensureAvailability();
|
||||
}
|
||||
|
||||
private final NativeLongArray jniChannelPointers;
|
||||
private final boolean allowGrowing;
|
||||
private final FileDescriptor kqueueFd;
|
||||
private final KQueueEventArray changeList;
|
||||
private final KQueueEventArray eventList;
|
||||
private final SelectStrategy selectStrategy;
|
||||
private final IovArray iovArray = new IovArray();
|
||||
private final IntSupplier selectNowSupplier = new IntSupplier() {
|
||||
@Override
|
||||
public int get() throws Exception {
|
||||
return kqueueWaitNow();
|
||||
}
|
||||
};
|
||||
private final Callable<Integer> pendingTasksCallable = new Callable<Integer>() {
|
||||
@Override
|
||||
public Integer call() throws Exception {
|
||||
return KQueueEventLoop.super.pendingTasks();
|
||||
}
|
||||
};
|
||||
|
||||
private volatile int wakenUp;
|
||||
private volatile int ioRatio = 50;
|
||||
|
||||
KQueueEventLoop(EventLoopGroup parent, Executor executor, int maxEvents,
|
||||
SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {
|
||||
super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);
|
||||
selectStrategy = ObjectUtil.checkNotNull(strategy, "strategy");
|
||||
this.kqueueFd = Native.newKQueue();
|
||||
if (maxEvents == 0) {
|
||||
allowGrowing = true;
|
||||
maxEvents = 4096;
|
||||
} else {
|
||||
allowGrowing = false;
|
||||
}
|
||||
changeList = new KQueueEventArray(maxEvents);
|
||||
eventList = new KQueueEventArray(maxEvents);
|
||||
jniChannelPointers = new NativeLongArray(4096);
|
||||
int result = Native.keventAddUserEvent(kqueueFd.intValue(), KQUEUE_WAKE_UP_IDENT);
|
||||
if (result < 0) {
|
||||
cleanup();
|
||||
throw new IllegalStateException("kevent failed to add user event with errno: " + (-result));
|
||||
}
|
||||
}
|
||||
|
||||
void evSet(AbstractKQueueChannel ch, short filter, short flags, int fflags) {
|
||||
changeList.evSet(ch, filter, flags, fflags);
|
||||
}
|
||||
|
||||
void remove(AbstractKQueueChannel ch) throws IOException {
|
||||
assert inEventLoop();
|
||||
if (ch.jniSelfPtr == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
jniChannelPointers.add(ch.jniSelfPtr);
|
||||
ch.jniSelfPtr = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a cleared {@link IovArray} that can be used for writes in this {@link EventLoop}.
|
||||
*/
|
||||
IovArray cleanArray() {
|
||||
iovArray.clear();
|
||||
return iovArray;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void wakeup(boolean inEventLoop) {
|
||||
if (!inEventLoop && WAKEN_UP_UPDATER.compareAndSet(this, 0, 1)) {
|
||||
wakeup();
|
||||
}
|
||||
}
|
||||
|
||||
private void wakeup() {
|
||||
Native.keventTriggerUserEvent(kqueueFd.intValue(), KQUEUE_WAKE_UP_IDENT);
|
||||
// Note that the result may return an error (e.g. errno = EBADF after the event loop has been shutdown).
|
||||
// So it is not very practical to assert the return value is always >= 0.
|
||||
}
|
||||
|
||||
private int kqueueWait(boolean oldWakeup) throws IOException {
|
||||
// TODO(scott): do we need to loop here ... we already loop in keventWait to ensure we wait the expected time.
|
||||
// We also do the same thing in EPOLL ... do we need to loop there?
|
||||
|
||||
// If a task was submitted when wakenUp value was 1, the task didn't get a chance to produce wakeup event.
|
||||
// So we need to check task queue again before calling epoll_wait. If we don't, the task might be pended
|
||||
// until epoll_wait was timed out. It might be pended until idle timeout if IdleStateHandler existed
|
||||
// in pipeline.
|
||||
if (oldWakeup && hasTasks()) {
|
||||
return kqueueWaitNow();
|
||||
}
|
||||
|
||||
long totalDelay = delayNanos(System.nanoTime());
|
||||
int delaySeconds = (int) min(totalDelay / 1000000000L, Integer.MAX_VALUE);
|
||||
return kqueueWait(delaySeconds, (int) min(totalDelay - delaySeconds * 1000000000L, Integer.MAX_VALUE));
|
||||
}
|
||||
|
||||
private int kqueueWaitNow() throws IOException {
|
||||
return kqueueWait(0, 0);
|
||||
}
|
||||
|
||||
private int kqueueWait(int timeoutSec, int timeoutNs) throws IOException {
|
||||
deleteJniChannelPointers();
|
||||
int numEvents = Native.keventWait(kqueueFd.intValue(), changeList, eventList, timeoutSec, timeoutNs);
|
||||
changeList.clear();
|
||||
return numEvents;
|
||||
}
|
||||
|
||||
private void deleteJniChannelPointers() {
|
||||
if (!jniChannelPointers.isEmpty()) {
|
||||
deleteGlobalRefs(jniChannelPointers.memoryAddress(), jniChannelPointers.memoryAddressEnd());
|
||||
jniChannelPointers.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void processReady(int ready) {
|
||||
for (int i = 0; i < ready; ++i) {
|
||||
final short filter = eventList.filter(i);
|
||||
final short flags = eventList.flags(i);
|
||||
if (filter == Native.EVFILT_USER || (flags & Native.EV_ERROR) != 0) {
|
||||
// EV_ERROR is returned if the FD is closed synchronously (which removes from kqueue) and then
|
||||
// we later attempt to delete the filters from kqueue.
|
||||
assert filter != Native.EVFILT_USER ||
|
||||
(filter == Native.EVFILT_USER && eventList.fd(i) == KQUEUE_WAKE_UP_IDENT);
|
||||
continue;
|
||||
}
|
||||
|
||||
AbstractKQueueChannel channel = eventList.channel(i);
|
||||
if (channel == null) {
|
||||
// This may happen if the channel has already been closed, and it will be removed from kqueue anyways.
|
||||
// We also handle EV_ERROR above to skip this even early if it is a result of a referencing a closed and
|
||||
// thus removed from kqueue FD.
|
||||
logger.warn("events[{}]=[{}, {}] had no channel!", i, eventList.fd(i), filter);
|
||||
continue;
|
||||
}
|
||||
|
||||
AbstractKQueueUnsafe unsafe = (AbstractKQueueUnsafe) channel.unsafe();
|
||||
// First check for EPOLLOUT as we may need to fail the connect ChannelPromise before try
|
||||
// to read from the file descriptor.
|
||||
if (filter == Native.EVFILT_WRITE) {
|
||||
unsafe.writeReady();
|
||||
} else if (filter == Native.EVFILT_READ) {
|
||||
// Check READ before EOF to ensure all data is read before shutting down the input.
|
||||
unsafe.readReady(eventList.data(i));
|
||||
}
|
||||
|
||||
// Check if EV_EOF was set, this will notify us for connection-reset in which case
|
||||
// we may close the channel directly or try to read more data depending on the state of the
|
||||
// Channel and also depending on the AbstractKQueueChannel subtype.
|
||||
if ((flags & Native.EV_EOF) != 0) {
|
||||
unsafe.readEOF();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run() {
|
||||
for (;;) {
|
||||
try {
|
||||
int strategy = selectStrategy.calculateStrategy(selectNowSupplier, hasTasks());
|
||||
switch (strategy) {
|
||||
case SelectStrategy.CONTINUE:
|
||||
continue;
|
||||
case SelectStrategy.SELECT:
|
||||
strategy = kqueueWait(WAKEN_UP_UPDATER.getAndSet(this, 0) == 1);
|
||||
|
||||
// 'wakenUp.compareAndSet(false, true)' is always evaluated
|
||||
// before calling 'selector.wakeup()' to reduce the wake-up
|
||||
// overhead. (Selector.wakeup() is an expensive operation.)
|
||||
//
|
||||
// However, there is a race condition in this approach.
|
||||
// The race condition is triggered when 'wakenUp' is set to
|
||||
// true too early.
|
||||
//
|
||||
// 'wakenUp' is set to true too early if:
|
||||
// 1) Selector is waken up between 'wakenUp.set(false)' and
|
||||
// 'selector.select(...)'. (BAD)
|
||||
// 2) Selector is waken up between 'selector.select(...)' and
|
||||
// 'if (wakenUp.get()) { ... }'. (OK)
|
||||
//
|
||||
// In the first case, 'wakenUp' is set to true and the
|
||||
// following 'selector.select(...)' will wake up immediately.
|
||||
// Until 'wakenUp' is set to false again in the next round,
|
||||
// 'wakenUp.compareAndSet(false, true)' will fail, and therefore
|
||||
// any attempt to wake up the Selector will fail, too, causing
|
||||
// the following 'selector.select(...)' call to block
|
||||
// unnecessarily.
|
||||
//
|
||||
// To fix this problem, we wake up the selector again if wakenUp
|
||||
// is true immediately after selector.select(...).
|
||||
// It is inefficient in that it wakes up the selector for both
|
||||
// the first case (BAD - wake-up required) and the second case
|
||||
// (OK - no wake-up required).
|
||||
|
||||
if (wakenUp == 1) {
|
||||
wakeup();
|
||||
}
|
||||
default:
|
||||
// fallthrough
|
||||
}
|
||||
|
||||
final int ioRatio = this.ioRatio;
|
||||
if (ioRatio == 100) {
|
||||
try {
|
||||
if (strategy > 0) {
|
||||
processReady(strategy);
|
||||
}
|
||||
} finally {
|
||||
runAllTasks();
|
||||
}
|
||||
} else {
|
||||
final long ioStartTime = System.nanoTime();
|
||||
|
||||
try {
|
||||
if (strategy > 0) {
|
||||
processReady(strategy);
|
||||
}
|
||||
} finally {
|
||||
final long ioTime = System.nanoTime() - ioStartTime;
|
||||
runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
|
||||
}
|
||||
}
|
||||
if (allowGrowing && strategy == eventList.capacity()) {
|
||||
//increase the size of the array as we needed the whole space for the events
|
||||
eventList.realloc(false);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
handleLoopException(t);
|
||||
}
|
||||
// Always handle shutdown even if the loop processing threw an exception.
|
||||
try {
|
||||
if (isShuttingDown()) {
|
||||
closeAll();
|
||||
if (confirmShutdown()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
handleLoopException(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Queue<Runnable> newTaskQueue(int maxPendingTasks) {
|
||||
// This event loop never calls takeTask()
|
||||
return PlatformDependent.newMpscQueue(maxPendingTasks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int pendingTasks() {
|
||||
// As we use a MpscQueue we need to ensure pendingTasks() is only executed from within the EventLoop as
|
||||
// otherwise we may see unexpected behavior (as size() is only allowed to be called by a single consumer).
|
||||
// See https://github.com/netty/netty/issues/5297
|
||||
return inEventLoop() ? super.pendingTasks() : submit(pendingTasksCallable).syncUninterruptibly().getNow();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the percentage of the desired amount of time spent for I/O in the event loop.
|
||||
*/
|
||||
public int getIoRatio() {
|
||||
return ioRatio;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the percentage of the desired amount of time spent for I/O in the event loop. The default value is
|
||||
* {@code 50}, which means the event loop will try to spend the same amount of time for I/O as for non-I/O tasks.
|
||||
*/
|
||||
public void setIoRatio(int ioRatio) {
|
||||
if (ioRatio <= 0 || ioRatio > 100) {
|
||||
throw new IllegalArgumentException("ioRatio: " + ioRatio + " (expected: 0 < ioRatio <= 100)");
|
||||
}
|
||||
this.ioRatio = ioRatio;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanup() {
|
||||
try {
|
||||
try {
|
||||
kqueueFd.close();
|
||||
} catch (IOException e) {
|
||||
logger.warn("Failed to close the kqueue fd.", e);
|
||||
}
|
||||
} finally {
|
||||
// Cleanup all native memory!
|
||||
|
||||
// The JNI channel pointers should already be deleted because we should wait on kevent before this method,
|
||||
// but lets just be sure we cleanup native memory.
|
||||
deleteJniChannelPointers();
|
||||
jniChannelPointers.free();
|
||||
|
||||
changeList.free();
|
||||
eventList.free();
|
||||
}
|
||||
}
|
||||
|
||||
private void closeAll() {
|
||||
try {
|
||||
kqueueWaitNow();
|
||||
} catch (IOException e) {
|
||||
// ignore on close
|
||||
}
|
||||
}
|
||||
|
||||
private static void handleLoopException(Throwable t) {
|
||||
logger.warn("Unexpected exception in the selector loop.", t);
|
||||
|
||||
// Prevent possible consecutive immediate failures that lead to
|
||||
// excessive CPU consumption.
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
// Ignore.
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.channel.DefaultSelectStrategyFactory;
|
||||
import io.netty.channel.EventLoop;
|
||||
import io.netty.channel.MultithreadEventLoopGroup;
|
||||
import io.netty.channel.SelectStrategyFactory;
|
||||
import io.netty.util.concurrent.EventExecutor;
|
||||
import io.netty.util.concurrent.EventExecutorChooserFactory;
|
||||
import io.netty.util.concurrent.RejectedExecutionHandler;
|
||||
import io.netty.util.concurrent.RejectedExecutionHandlers;
|
||||
import io.netty.util.internal.UnstableApi;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
@UnstableApi
|
||||
public final class KQueueEventLoopGroup extends MultithreadEventLoopGroup {
|
||||
static {
|
||||
// Ensure JNI is initialized by the time this class is loaded by this time!
|
||||
KQueue.ensureAvailability();
|
||||
}
|
||||
/**
|
||||
* Create a new instance using the default number of threads and the default {@link ThreadFactory}.
|
||||
*/
|
||||
public KQueueEventLoopGroup() {
|
||||
this(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance using the specified number of threads and the default {@link ThreadFactory}.
|
||||
*/
|
||||
public KQueueEventLoopGroup(int nThreads) {
|
||||
this(nThreads, (ThreadFactory) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance using the specified number of threads and the default {@link ThreadFactory}.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public KQueueEventLoopGroup(int nThreads, SelectStrategyFactory selectStrategyFactory) {
|
||||
this(nThreads, (ThreadFactory) null, selectStrategyFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance using the specified number of threads and the given {@link ThreadFactory}.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public KQueueEventLoopGroup(int nThreads, ThreadFactory threadFactory) {
|
||||
this(nThreads, threadFactory, 0);
|
||||
}
|
||||
|
||||
public KQueueEventLoopGroup(int nThreads, Executor executor) {
|
||||
this(nThreads, executor, DefaultSelectStrategyFactory.INSTANCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance using the specified number of threads and the given {@link ThreadFactory}.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public KQueueEventLoopGroup(int nThreads, ThreadFactory threadFactory,
|
||||
SelectStrategyFactory selectStrategyFactory) {
|
||||
this(nThreads, threadFactory, 0, selectStrategyFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance using the specified number of threads, the given {@link ThreadFactory} and the given
|
||||
* maximal amount of epoll events to handle per epollWait(...).
|
||||
*
|
||||
* @deprecated Use {@link #KQueueEventLoopGroup(int)} or {@link #KQueueEventLoopGroup(int, ThreadFactory)}
|
||||
*/
|
||||
@Deprecated
|
||||
public KQueueEventLoopGroup(int nThreads, ThreadFactory threadFactory, int maxEventsAtOnce) {
|
||||
this(nThreads, threadFactory, maxEventsAtOnce, DefaultSelectStrategyFactory.INSTANCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance using the specified number of threads, the given {@link ThreadFactory} and the given
|
||||
* maximal amount of epoll events to handle per epollWait(...).
|
||||
*
|
||||
* @deprecated Use {@link #KQueueEventLoopGroup(int)}, {@link #KQueueEventLoopGroup(int, ThreadFactory)}, or
|
||||
* {@link #KQueueEventLoopGroup(int, SelectStrategyFactory)}
|
||||
*/
|
||||
@Deprecated
|
||||
public KQueueEventLoopGroup(int nThreads, ThreadFactory threadFactory, int maxEventsAtOnce,
|
||||
SelectStrategyFactory selectStrategyFactory) {
|
||||
super(nThreads, threadFactory, maxEventsAtOnce, selectStrategyFactory, RejectedExecutionHandlers.reject());
|
||||
}
|
||||
|
||||
public KQueueEventLoopGroup(int nThreads, Executor executor, SelectStrategyFactory selectStrategyFactory) {
|
||||
super(nThreads, executor, 0, selectStrategyFactory, RejectedExecutionHandlers.reject());
|
||||
}
|
||||
|
||||
public KQueueEventLoopGroup(int nThreads, Executor executor, EventExecutorChooserFactory chooserFactory,
|
||||
SelectStrategyFactory selectStrategyFactory) {
|
||||
super(nThreads, executor, chooserFactory, 0, selectStrategyFactory, RejectedExecutionHandlers.reject());
|
||||
}
|
||||
|
||||
public KQueueEventLoopGroup(int nThreads, Executor executor, EventExecutorChooserFactory chooserFactory,
|
||||
SelectStrategyFactory selectStrategyFactory,
|
||||
RejectedExecutionHandler rejectedExecutionHandler) {
|
||||
super(nThreads, executor, chooserFactory, 0, selectStrategyFactory, rejectedExecutionHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the percentage of the desired amount of time spent for I/O in the child event loops. The default value is
|
||||
* {@code 50}, which means the event loop will try to spend the same amount of time for I/O as for non-I/O tasks.
|
||||
*/
|
||||
public void setIoRatio(int ioRatio) {
|
||||
for (EventExecutor e: this) {
|
||||
((KQueueEventLoop) e).setIoRatio(ioRatio);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
|
||||
return new KQueueEventLoop(this, executor, (Integer) args[0],
|
||||
((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]);
|
||||
}
|
||||
}
|
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.channel.ChannelConfig;
|
||||
import io.netty.channel.RecvByteBufAllocator;
|
||||
import io.netty.util.UncheckedBooleanSupplier;
|
||||
import io.netty.util.internal.ObjectUtil;
|
||||
|
||||
import static java.lang.Math.max;
|
||||
import static java.lang.Math.min;
|
||||
|
||||
final class KQueueRecvByteAllocatorHandle implements RecvByteBufAllocator.ExtendedHandle {
|
||||
private final RecvByteBufAllocator.ExtendedHandle delegate;
|
||||
private final UncheckedBooleanSupplier defaultMaybeMoreDataSupplier = new UncheckedBooleanSupplier() {
|
||||
@Override
|
||||
public boolean get() {
|
||||
return maybeMoreDataToRead();
|
||||
}
|
||||
};
|
||||
private boolean overrideGuess;
|
||||
private boolean readEOF;
|
||||
private long numberBytesPending;
|
||||
|
||||
KQueueRecvByteAllocatorHandle(RecvByteBufAllocator.ExtendedHandle handle) {
|
||||
this.delegate = ObjectUtil.checkNotNull(handle, "handle");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int guess() {
|
||||
return overrideGuess ? guess0() : delegate.guess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset(ChannelConfig config) {
|
||||
overrideGuess = ((KQueueChannelConfig) config).getRcvAllocTransportProvidesGuess();
|
||||
delegate.reset(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void incMessagesRead(int numMessages) {
|
||||
delegate.incMessagesRead(numMessages);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuf allocate(ByteBufAllocator alloc) {
|
||||
return overrideGuess ? alloc.ioBuffer(guess0()) : delegate.allocate(alloc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lastBytesRead(int bytes) {
|
||||
numberBytesPending = bytes < 0 ? 0 : max(0, numberBytesPending - bytes);
|
||||
delegate.lastBytesRead(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int lastBytesRead() {
|
||||
return delegate.lastBytesRead();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attemptedBytesRead(int bytes) {
|
||||
delegate.attemptedBytesRead(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int attemptedBytesRead() {
|
||||
return delegate.attemptedBytesRead();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readComplete() {
|
||||
delegate.readComplete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean continueReading(UncheckedBooleanSupplier maybeMoreDataSupplier) {
|
||||
return delegate.continueReading(maybeMoreDataSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean continueReading() {
|
||||
// We must override the supplier which determines if there maybe more data to read.
|
||||
return delegate.continueReading(defaultMaybeMoreDataSupplier);
|
||||
}
|
||||
|
||||
void readEOF() {
|
||||
readEOF = true;
|
||||
}
|
||||
|
||||
void numberBytesPending(long numberBytesPending) {
|
||||
this.numberBytesPending = numberBytesPending;
|
||||
}
|
||||
|
||||
boolean maybeMoreDataToRead() {
|
||||
/**
|
||||
* kqueue with EV_CLEAR flag set requires that we read until we consume "data" bytes
|
||||
* (see <a href="https://www.freebsd.org/cgi/man.cgi?kqueue">kqueue man</a>). However in order to
|
||||
* respect auto read we supporting reading to stop if auto read is off. If auto read is on we force reading to
|
||||
* continue to avoid a {@link StackOverflowError} between channelReadComplete and reading from the
|
||||
* channel. It is expected that the {@link #KQueueSocketChannel} implementations will track if all data was not
|
||||
* read, and will force a EVFILT_READ ready event.
|
||||
*
|
||||
* If EOF has been read we must read until we get an error.
|
||||
*/
|
||||
return numberBytesPending != 0 || readEOF;
|
||||
}
|
||||
|
||||
private int guess0() {
|
||||
return (int) min(numberBytesPending, Integer.MAX_VALUE);
|
||||
}
|
||||
}
|
@ -0,0 +1,201 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.channel.ChannelException;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.MessageSizeEstimator;
|
||||
import io.netty.channel.RecvByteBufAllocator;
|
||||
import io.netty.channel.WriteBufferWaterMark;
|
||||
import io.netty.channel.socket.ServerSocketChannelConfig;
|
||||
import io.netty.util.NetUtil;
|
||||
import io.netty.util.internal.UnstableApi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import static io.netty.channel.ChannelOption.SO_BACKLOG;
|
||||
import static io.netty.channel.ChannelOption.SO_RCVBUF;
|
||||
import static io.netty.channel.ChannelOption.SO_REUSEADDR;
|
||||
|
||||
@UnstableApi
|
||||
public class KQueueServerChannelConfig extends KQueueChannelConfig implements ServerSocketChannelConfig {
|
||||
protected final AbstractKQueueChannel channel;
|
||||
private volatile int backlog = NetUtil.SOMAXCONN;
|
||||
|
||||
KQueueServerChannelConfig(AbstractKQueueChannel channel) {
|
||||
super(channel);
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<ChannelOption<?>, Object> getOptions() {
|
||||
return getOptions(super.getOptions(), SO_RCVBUF, SO_REUSEADDR, SO_BACKLOG);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T getOption(ChannelOption<T> option) {
|
||||
if (option == SO_RCVBUF) {
|
||||
return (T) Integer.valueOf(getReceiveBufferSize());
|
||||
}
|
||||
if (option == SO_REUSEADDR) {
|
||||
return (T) Boolean.valueOf(isReuseAddress());
|
||||
}
|
||||
if (option == SO_BACKLOG) {
|
||||
return (T) Integer.valueOf(getBacklog());
|
||||
}
|
||||
return super.getOption(option);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean setOption(ChannelOption<T> option, T value) {
|
||||
validate(option, value);
|
||||
|
||||
if (option == SO_RCVBUF) {
|
||||
setReceiveBufferSize((Integer) value);
|
||||
} else if (option == SO_REUSEADDR) {
|
||||
setReuseAddress((Boolean) value);
|
||||
} else if (option == SO_BACKLOG) {
|
||||
setBacklog((Integer) value);
|
||||
} else {
|
||||
return super.setOption(option, value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isReuseAddress() {
|
||||
try {
|
||||
return channel.socket.isReuseAddress();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public KQueueServerChannelConfig setReuseAddress(boolean reuseAddress) {
|
||||
try {
|
||||
channel.socket.setReuseAddress(reuseAddress);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public int getReceiveBufferSize() {
|
||||
try {
|
||||
return channel.socket.getReceiveBufferSize();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public KQueueServerChannelConfig setReceiveBufferSize(int receiveBufferSize) {
|
||||
try {
|
||||
channel.socket.setReceiveBufferSize(receiveBufferSize);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public int getBacklog() {
|
||||
return backlog;
|
||||
}
|
||||
|
||||
public KQueueServerChannelConfig setBacklog(int backlog) {
|
||||
if (backlog < 0) {
|
||||
throw new IllegalArgumentException("backlog: " + backlog);
|
||||
}
|
||||
this.backlog = backlog;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueServerChannelConfig setRcvAllocTransportProvidesGuess(boolean transportProvidesGuess) {
|
||||
super.setRcvAllocTransportProvidesGuess(transportProvidesGuess);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueServerChannelConfig setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueServerChannelConfig setConnectTimeoutMillis(int connectTimeoutMillis) {
|
||||
super.setConnectTimeoutMillis(connectTimeoutMillis);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public KQueueServerChannelConfig setMaxMessagesPerRead(int maxMessagesPerRead) {
|
||||
super.setMaxMessagesPerRead(maxMessagesPerRead);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueServerChannelConfig setWriteSpinCount(int writeSpinCount) {
|
||||
super.setWriteSpinCount(writeSpinCount);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueServerChannelConfig setAllocator(ByteBufAllocator allocator) {
|
||||
super.setAllocator(allocator);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueServerChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator) {
|
||||
super.setRecvByteBufAllocator(allocator);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueServerChannelConfig setAutoRead(boolean autoRead) {
|
||||
super.setAutoRead(autoRead);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public KQueueServerChannelConfig setWriteBufferHighWaterMark(int writeBufferHighWaterMark) {
|
||||
super.setWriteBufferHighWaterMark(writeBufferHighWaterMark);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public KQueueServerChannelConfig setWriteBufferLowWaterMark(int writeBufferLowWaterMark) {
|
||||
super.setWriteBufferLowWaterMark(writeBufferLowWaterMark);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueServerChannelConfig setWriteBufferWaterMark(WriteBufferWaterMark writeBufferWaterMark) {
|
||||
super.setWriteBufferWaterMark(writeBufferWaterMark);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueServerChannelConfig setMessageSizeEstimator(MessageSizeEstimator estimator) {
|
||||
super.setMessageSizeEstimator(estimator);
|
||||
return this;
|
||||
}
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.unix.DomainSocketAddress;
|
||||
import io.netty.channel.unix.ServerDomainSocketChannel;
|
||||
import io.netty.util.internal.UnstableApi;
|
||||
import io.netty.util.internal.logging.InternalLogger;
|
||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.SocketAddress;
|
||||
|
||||
import static io.netty.channel.kqueue.BsdSocket.newSocketDomain;
|
||||
|
||||
@UnstableApi
|
||||
public final class KQueueServerDomainSocketChannel extends AbstractKQueueServerChannel
|
||||
implements ServerDomainSocketChannel {
|
||||
private static final InternalLogger logger = InternalLoggerFactory.getInstance(
|
||||
KQueueServerDomainSocketChannel.class);
|
||||
|
||||
private final KQueueServerChannelConfig config = new KQueueServerChannelConfig(this);
|
||||
private volatile DomainSocketAddress local;
|
||||
|
||||
public KQueueServerDomainSocketChannel() {
|
||||
super(newSocketDomain(), false);
|
||||
}
|
||||
|
||||
KQueueServerDomainSocketChannel(BsdSocket socket, boolean active) {
|
||||
super(socket, active);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Channel newChildChannel(int fd, byte[] addr, int offset, int len) throws Exception {
|
||||
return new KQueueDomainSocketChannel(this, new BsdSocket(fd));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DomainSocketAddress localAddress0() {
|
||||
return local;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doBind(SocketAddress localAddress) throws Exception {
|
||||
socket.bind(localAddress);
|
||||
socket.listen(config.getBacklog());
|
||||
local = (DomainSocketAddress) localAddress;
|
||||
active = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doClose() throws Exception {
|
||||
try {
|
||||
super.doClose();
|
||||
} finally {
|
||||
DomainSocketAddress local = this.local;
|
||||
if (local != null) {
|
||||
// Delete the socket file if possible.
|
||||
File socketFile = new File(local.path());
|
||||
boolean success = socketFile.delete();
|
||||
if (!success && logger.isDebugEnabled()) {
|
||||
logger.debug("Failed to delete a domain socket file: {}", local.path());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueServerChannelConfig config() {
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainSocketAddress remoteAddress() {
|
||||
return (DomainSocketAddress) super.remoteAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainSocketAddress localAddress() {
|
||||
return (DomainSocketAddress) super.localAddress();
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.EventLoop;
|
||||
import io.netty.channel.socket.ServerSocketChannel;
|
||||
import io.netty.util.internal.UnstableApi;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
|
||||
import static io.netty.channel.kqueue.BsdSocket.newSocketStream;
|
||||
import static io.netty.channel.unix.NativeInetAddress.address;
|
||||
|
||||
@UnstableApi
|
||||
public final class KQueueServerSocketChannel extends AbstractKQueueServerChannel implements ServerSocketChannel {
|
||||
private final KQueueServerSocketChannelConfig config;
|
||||
private volatile InetSocketAddress local;
|
||||
|
||||
public KQueueServerSocketChannel() {
|
||||
super(newSocketStream(), false);
|
||||
config = new KQueueServerSocketChannelConfig(this);
|
||||
}
|
||||
|
||||
KQueueServerSocketChannel(BsdSocket fd, boolean active) {
|
||||
super(fd, active);
|
||||
config = new KQueueServerSocketChannelConfig(this);
|
||||
// As we create an KQueueServerSocketChannel from a FileDescriptor we should try to obtain the remote and local
|
||||
// address from it. This is needed as the FileDescriptor may be bound already.
|
||||
local = fd.localAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isCompatible(EventLoop loop) {
|
||||
return loop instanceof KQueueEventLoop;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doBind(SocketAddress localAddress) throws Exception {
|
||||
InetSocketAddress addr = (InetSocketAddress) localAddress;
|
||||
checkResolvable(addr);
|
||||
socket.bind(addr);
|
||||
local = socket.localAddress();
|
||||
// TODO(scott): tcp fast open here!
|
||||
socket.listen(config.getBacklog());
|
||||
active = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress remoteAddress() {
|
||||
return (InetSocketAddress) super.remoteAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress localAddress() {
|
||||
return (InetSocketAddress) super.localAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueServerSocketChannelConfig config() {
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InetSocketAddress localAddress0() {
|
||||
return local;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Channel newChildChannel(int fd, byte[] address, int offset, int len) throws Exception {
|
||||
return new KQueueSocketChannel(this, new BsdSocket(fd), address(address, offset, len));
|
||||
}
|
||||
}
|
@ -0,0 +1,201 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.channel.ChannelException;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.MessageSizeEstimator;
|
||||
import io.netty.channel.RecvByteBufAllocator;
|
||||
import io.netty.channel.WriteBufferWaterMark;
|
||||
import io.netty.channel.socket.ServerSocketChannelConfig;
|
||||
import io.netty.util.internal.UnstableApi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import static io.netty.channel.kqueue.KQueueChannelOption.SO_ACCEPTFILTER;
|
||||
import static io.netty.channel.unix.UnixChannelOption.SO_REUSEPORT;
|
||||
|
||||
@UnstableApi
|
||||
public class KQueueServerSocketChannelConfig extends KQueueServerChannelConfig implements ServerSocketChannelConfig {
|
||||
KQueueServerSocketChannelConfig(KQueueServerSocketChannel channel) {
|
||||
super(channel);
|
||||
|
||||
// Use SO_REUSEADDR by default as java.nio does the same.
|
||||
//
|
||||
// See https://github.com/netty/netty/issues/2605
|
||||
setReuseAddress(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<ChannelOption<?>, Object> getOptions() {
|
||||
return getOptions(super.getOptions(), SO_REUSEPORT, SO_ACCEPTFILTER);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T getOption(ChannelOption<T> option) {
|
||||
if (option == SO_REUSEPORT) {
|
||||
return (T) Boolean.valueOf(isReusePort());
|
||||
}
|
||||
if (option == SO_ACCEPTFILTER) {
|
||||
return (T) getAcceptFilter();
|
||||
}
|
||||
return super.getOption(option);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean setOption(ChannelOption<T> option, T value) {
|
||||
validate(option, value);
|
||||
|
||||
if (option == SO_REUSEPORT) {
|
||||
setReusePort((Boolean) value);
|
||||
} else if (option == SO_ACCEPTFILTER) {
|
||||
setAcceptFilter((AcceptFilter) value);
|
||||
} else {
|
||||
return super.setOption(option, value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public KQueueServerSocketChannelConfig setReusePort(boolean reusePort) {
|
||||
try {
|
||||
channel.socket.setReusePort(reusePort);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isReusePort() {
|
||||
try {
|
||||
return channel.socket.isReusePort();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public KQueueServerSocketChannelConfig setAcceptFilter(AcceptFilter acceptFilter) {
|
||||
try {
|
||||
channel.socket.setAcceptFilter(acceptFilter);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public AcceptFilter getAcceptFilter() {
|
||||
try {
|
||||
return channel.socket.getAcceptFilter();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueServerSocketChannelConfig setRcvAllocTransportProvidesGuess(boolean transportProvidesGuess) {
|
||||
super.setRcvAllocTransportProvidesGuess(transportProvidesGuess);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueServerSocketChannelConfig setReuseAddress(boolean reuseAddress) {
|
||||
super.setReuseAddress(reuseAddress);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueServerSocketChannelConfig setReceiveBufferSize(int receiveBufferSize) {
|
||||
super.setReceiveBufferSize(receiveBufferSize);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueServerSocketChannelConfig setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueServerSocketChannelConfig setBacklog(int backlog) {
|
||||
super.setBacklog(backlog);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueServerSocketChannelConfig setConnectTimeoutMillis(int connectTimeoutMillis) {
|
||||
super.setConnectTimeoutMillis(connectTimeoutMillis);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public KQueueServerSocketChannelConfig setMaxMessagesPerRead(int maxMessagesPerRead) {
|
||||
super.setMaxMessagesPerRead(maxMessagesPerRead);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueServerSocketChannelConfig setWriteSpinCount(int writeSpinCount) {
|
||||
super.setWriteSpinCount(writeSpinCount);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueServerSocketChannelConfig setAllocator(ByteBufAllocator allocator) {
|
||||
super.setAllocator(allocator);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueServerSocketChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator) {
|
||||
super.setRecvByteBufAllocator(allocator);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueServerSocketChannelConfig setAutoRead(boolean autoRead) {
|
||||
super.setAutoRead(autoRead);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public KQueueServerSocketChannelConfig setWriteBufferHighWaterMark(int writeBufferHighWaterMark) {
|
||||
super.setWriteBufferHighWaterMark(writeBufferHighWaterMark);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public KQueueServerSocketChannelConfig setWriteBufferLowWaterMark(int writeBufferLowWaterMark) {
|
||||
super.setWriteBufferLowWaterMark(writeBufferLowWaterMark);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueServerSocketChannelConfig setWriteBufferWaterMark(WriteBufferWaterMark writeBufferWaterMark) {
|
||||
super.setWriteBufferWaterMark(writeBufferWaterMark);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueServerSocketChannelConfig setMessageSizeEstimator(MessageSizeEstimator estimator) {
|
||||
super.setMessageSizeEstimator(estimator);
|
||||
return this;
|
||||
}
|
||||
}
|
@ -0,0 +1,176 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.socket.ServerSocketChannel;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.util.concurrent.GlobalEventExecutor;
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
import io.netty.util.internal.UnstableApi;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.channels.AlreadyConnectedException;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@UnstableApi
|
||||
public final class KQueueSocketChannel extends AbstractKQueueStreamChannel implements SocketChannel {
|
||||
private final KQueueSocketChannelConfig config;
|
||||
|
||||
private volatile InetSocketAddress local;
|
||||
private volatile InetSocketAddress remote;
|
||||
private InetSocketAddress requestedRemote;
|
||||
|
||||
public KQueueSocketChannel() {
|
||||
super(null, BsdSocket.newSocketStream(), false);
|
||||
config = new KQueueSocketChannelConfig(this);
|
||||
}
|
||||
|
||||
KQueueSocketChannel(Channel parent, BsdSocket fd, InetSocketAddress remote) {
|
||||
super(parent, fd, true);
|
||||
config = new KQueueSocketChannelConfig(this);
|
||||
// Directly cache the remote and local addresses
|
||||
// See https://github.com/netty/netty/issues/2359
|
||||
this.remote = remote;
|
||||
local = fd.localAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress remoteAddress() {
|
||||
return (InetSocketAddress) super.remoteAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress localAddress() {
|
||||
return (InetSocketAddress) super.localAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SocketAddress localAddress0() {
|
||||
return local;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SocketAddress remoteAddress0() {
|
||||
return remote;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doBind(SocketAddress local) throws Exception {
|
||||
InetSocketAddress localAddress = (InetSocketAddress) local;
|
||||
socket.bind(localAddress);
|
||||
this.local = socket.localAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueSocketChannelConfig config() {
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerSocketChannel parent() {
|
||||
return (ServerSocketChannel) super.parent();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractKQueueUnsafe newUnsafe() {
|
||||
return new KQueueSocketChannelUnsafe();
|
||||
}
|
||||
|
||||
private static InetSocketAddress computeRemoteAddr(InetSocketAddress remoteAddr, InetSocketAddress osRemoteAddr) {
|
||||
if (osRemoteAddr != null) {
|
||||
if (PlatformDependent.javaVersion() >= 7) {
|
||||
try {
|
||||
// Only try to construct a new InetSocketAddress if we using java >= 7 as getHostString() does not
|
||||
// exists in earlier releases and so the retrieval of the hostname could block the EventLoop if a
|
||||
// reverse lookup would be needed.
|
||||
return new InetSocketAddress(InetAddress.getByAddress(remoteAddr.getHostString(),
|
||||
osRemoteAddr.getAddress().getAddress()),
|
||||
osRemoteAddr.getPort());
|
||||
} catch (UnknownHostException ignore) {
|
||||
// Should never happen but fallback to osRemoteAddr anyway.
|
||||
}
|
||||
}
|
||||
return osRemoteAddr;
|
||||
}
|
||||
return remoteAddr;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
|
||||
if (localAddress != null) {
|
||||
checkResolvable((InetSocketAddress) localAddress);
|
||||
}
|
||||
InetSocketAddress remoteAddr = (InetSocketAddress) remoteAddress;
|
||||
checkResolvable(remoteAddr);
|
||||
|
||||
if (remote != null) {
|
||||
// Check if already connected before trying to connect. This is needed as connect(...) will not return -1
|
||||
// and set errno to EISCONN if a previous connect(...) attempt was setting errno to EINPROGRESS and finished
|
||||
// later.
|
||||
throw new AlreadyConnectedException();
|
||||
}
|
||||
|
||||
boolean connected = super.doConnect(remoteAddress, localAddress);
|
||||
if (connected) {
|
||||
remote = computeRemoteAddr(remoteAddr, socket.remoteAddress());
|
||||
} else {
|
||||
// Store for later usage in doFinishConnect()
|
||||
requestedRemote = remoteAddr;
|
||||
}
|
||||
// We always need to set the localAddress even if not connected yet as the bind already took place.
|
||||
//
|
||||
// See https://github.com/netty/netty/issues/3463
|
||||
local = socket.localAddress();
|
||||
return connected;
|
||||
}
|
||||
|
||||
private final class KQueueSocketChannelUnsafe extends KQueueStreamUnsafe {
|
||||
@Override
|
||||
protected Executor prepareToClose() {
|
||||
try {
|
||||
// Check isOpen() first as otherwise it will throw a RuntimeException
|
||||
// when call getSoLinger() as the fd is not valid anymore.
|
||||
if (isOpen() && config().getSoLinger() > 0) {
|
||||
// We need to cancel this key of the channel so we may not end up in a eventloop spin
|
||||
// because we try to read or write until the actual close happens which may be later due
|
||||
// SO_LINGER handling.
|
||||
// See https://github.com/netty/netty/issues/4449
|
||||
((KQueueEventLoop) eventLoop()).remove(KQueueSocketChannel.this);
|
||||
return GlobalEventExecutor.INSTANCE;
|
||||
}
|
||||
} catch (Throwable ignore) {
|
||||
// Ignore the error as the underlying channel may be closed in the meantime and so
|
||||
// getSoLinger() may produce an exception. In this case we just return null.
|
||||
// See https://github.com/netty/netty/issues/4449
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean doFinishConnect() throws Exception {
|
||||
if (super.doFinishConnect()) {
|
||||
remote = computeRemoteAddr(requestedRemote, socket.remoteAddress());
|
||||
requestedRemote = null;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,386 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.channel.ChannelException;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.MessageSizeEstimator;
|
||||
import io.netty.channel.RecvByteBufAllocator;
|
||||
import io.netty.channel.WriteBufferWaterMark;
|
||||
import io.netty.channel.socket.SocketChannelConfig;
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
import io.netty.util.internal.UnstableApi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import static io.netty.channel.ChannelOption.ALLOW_HALF_CLOSURE;
|
||||
import static io.netty.channel.ChannelOption.IP_TOS;
|
||||
import static io.netty.channel.ChannelOption.SO_KEEPALIVE;
|
||||
import static io.netty.channel.ChannelOption.SO_LINGER;
|
||||
import static io.netty.channel.ChannelOption.SO_RCVBUF;
|
||||
import static io.netty.channel.ChannelOption.SO_REUSEADDR;
|
||||
import static io.netty.channel.ChannelOption.SO_SNDBUF;
|
||||
import static io.netty.channel.ChannelOption.TCP_NODELAY;
|
||||
import static io.netty.channel.kqueue.KQueueChannelOption.SO_SNDLOWAT;
|
||||
import static io.netty.channel.kqueue.KQueueChannelOption.TCP_NOPUSH;
|
||||
|
||||
@UnstableApi
|
||||
public final class KQueueSocketChannelConfig extends KQueueChannelConfig implements SocketChannelConfig {
|
||||
private final KQueueSocketChannel channel;
|
||||
private volatile boolean allowHalfClosure;
|
||||
|
||||
KQueueSocketChannelConfig(KQueueSocketChannel channel) {
|
||||
super(channel);
|
||||
this.channel = channel;
|
||||
if (PlatformDependent.canEnableTcpNoDelayByDefault()) {
|
||||
setTcpNoDelay(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<ChannelOption<?>, Object> getOptions() {
|
||||
return getOptions(
|
||||
super.getOptions(),
|
||||
SO_RCVBUF, SO_SNDBUF, TCP_NODELAY, SO_KEEPALIVE, SO_REUSEADDR, SO_LINGER, IP_TOS,
|
||||
ALLOW_HALF_CLOSURE, SO_SNDLOWAT, TCP_NOPUSH);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T getOption(ChannelOption<T> option) {
|
||||
if (option == SO_RCVBUF) {
|
||||
return (T) Integer.valueOf(getReceiveBufferSize());
|
||||
}
|
||||
if (option == SO_SNDBUF) {
|
||||
return (T) Integer.valueOf(getSendBufferSize());
|
||||
}
|
||||
if (option == TCP_NODELAY) {
|
||||
return (T) Boolean.valueOf(isTcpNoDelay());
|
||||
}
|
||||
if (option == SO_KEEPALIVE) {
|
||||
return (T) Boolean.valueOf(isKeepAlive());
|
||||
}
|
||||
if (option == SO_REUSEADDR) {
|
||||
return (T) Boolean.valueOf(isReuseAddress());
|
||||
}
|
||||
if (option == SO_LINGER) {
|
||||
return (T) Integer.valueOf(getSoLinger());
|
||||
}
|
||||
if (option == IP_TOS) {
|
||||
return (T) Integer.valueOf(getTrafficClass());
|
||||
}
|
||||
if (option == ALLOW_HALF_CLOSURE) {
|
||||
return (T) Boolean.valueOf(isAllowHalfClosure());
|
||||
}
|
||||
if (option == SO_SNDLOWAT) {
|
||||
return (T) Integer.valueOf(getSndLowAt());
|
||||
}
|
||||
if (option == TCP_NOPUSH) {
|
||||
return (T) Boolean.valueOf(isTcpNoPush());
|
||||
}
|
||||
return super.getOption(option);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean setOption(ChannelOption<T> option, T value) {
|
||||
validate(option, value);
|
||||
|
||||
if (option == SO_RCVBUF) {
|
||||
setReceiveBufferSize((Integer) value);
|
||||
} else if (option == SO_SNDBUF) {
|
||||
setSendBufferSize((Integer) value);
|
||||
} else if (option == TCP_NODELAY) {
|
||||
setTcpNoDelay((Boolean) value);
|
||||
} else if (option == SO_KEEPALIVE) {
|
||||
setKeepAlive((Boolean) value);
|
||||
} else if (option == SO_REUSEADDR) {
|
||||
setReuseAddress((Boolean) value);
|
||||
} else if (option == SO_LINGER) {
|
||||
setSoLinger((Integer) value);
|
||||
} else if (option == IP_TOS) {
|
||||
setTrafficClass((Integer) value);
|
||||
} else if (option == ALLOW_HALF_CLOSURE) {
|
||||
setAllowHalfClosure((Boolean) value);
|
||||
} else if (option == SO_SNDLOWAT) {
|
||||
setSndLowAt((Integer) value);
|
||||
} else if (option == TCP_NOPUSH) {
|
||||
setTcpNoPush((Boolean) value);
|
||||
} else {
|
||||
return super.setOption(option, value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getReceiveBufferSize() {
|
||||
try {
|
||||
return channel.socket.getReceiveBufferSize();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSendBufferSize() {
|
||||
try {
|
||||
return channel.socket.getSendBufferSize();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSoLinger() {
|
||||
try {
|
||||
return channel.socket.getSoLinger();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTrafficClass() {
|
||||
try {
|
||||
return channel.socket.getTrafficClass();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isKeepAlive() {
|
||||
try {
|
||||
return channel.socket.isKeepAlive();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReuseAddress() {
|
||||
try {
|
||||
return channel.socket.isReuseAddress();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTcpNoDelay() {
|
||||
try {
|
||||
return channel.socket.isTcpNoDelay();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public int getSndLowAt() {
|
||||
try {
|
||||
return channel.socket.getSndLowAt();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setSndLowAt(int sndLowAt) {
|
||||
try {
|
||||
channel.socket.setSndLowAt(sndLowAt);
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isTcpNoPush() {
|
||||
try {
|
||||
return channel.socket.isTcpNoPush();
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setTcpNoPush(boolean tcpNoPush) {
|
||||
try {
|
||||
channel.socket.setTcpNoPush(tcpNoPush);
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueSocketChannelConfig setKeepAlive(boolean keepAlive) {
|
||||
try {
|
||||
channel.socket.setKeepAlive(keepAlive);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueSocketChannelConfig setReceiveBufferSize(int receiveBufferSize) {
|
||||
try {
|
||||
channel.socket.setReceiveBufferSize(receiveBufferSize);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueSocketChannelConfig setReuseAddress(boolean reuseAddress) {
|
||||
try {
|
||||
channel.socket.setReuseAddress(reuseAddress);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueSocketChannelConfig setSendBufferSize(int sendBufferSize) {
|
||||
try {
|
||||
channel.socket.setSendBufferSize(sendBufferSize);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueSocketChannelConfig setSoLinger(int soLinger) {
|
||||
try {
|
||||
channel.socket.setSoLinger(soLinger);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueSocketChannelConfig setTcpNoDelay(boolean tcpNoDelay) {
|
||||
try {
|
||||
channel.socket.setTcpNoDelay(tcpNoDelay);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueSocketChannelConfig setTrafficClass(int trafficClass) {
|
||||
try {
|
||||
channel.socket.setTrafficClass(trafficClass);
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ChannelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAllowHalfClosure() {
|
||||
return allowHalfClosure;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueSocketChannelConfig setRcvAllocTransportProvidesGuess(boolean transportProvidesGuess) {
|
||||
super.setRcvAllocTransportProvidesGuess(transportProvidesGuess);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueSocketChannelConfig setPerformancePreferences(
|
||||
int connectionTime, int latency, int bandwidth) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueSocketChannelConfig setAllowHalfClosure(boolean allowHalfClosure) {
|
||||
this.allowHalfClosure = allowHalfClosure;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueSocketChannelConfig setConnectTimeoutMillis(int connectTimeoutMillis) {
|
||||
super.setConnectTimeoutMillis(connectTimeoutMillis);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public KQueueSocketChannelConfig setMaxMessagesPerRead(int maxMessagesPerRead) {
|
||||
super.setMaxMessagesPerRead(maxMessagesPerRead);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueSocketChannelConfig setWriteSpinCount(int writeSpinCount) {
|
||||
super.setWriteSpinCount(writeSpinCount);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueSocketChannelConfig setAllocator(ByteBufAllocator allocator) {
|
||||
super.setAllocator(allocator);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueSocketChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator) {
|
||||
super.setRecvByteBufAllocator(allocator);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueSocketChannelConfig setAutoRead(boolean autoRead) {
|
||||
super.setAutoRead(autoRead);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueSocketChannelConfig setAutoClose(boolean autoClose) {
|
||||
super.setAutoClose(autoClose);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public KQueueSocketChannelConfig setWriteBufferHighWaterMark(int writeBufferHighWaterMark) {
|
||||
super.setWriteBufferHighWaterMark(writeBufferHighWaterMark);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public KQueueSocketChannelConfig setWriteBufferLowWaterMark(int writeBufferLowWaterMark) {
|
||||
super.setWriteBufferLowWaterMark(writeBufferLowWaterMark);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueSocketChannelConfig setWriteBufferWaterMark(WriteBufferWaterMark writeBufferWaterMark) {
|
||||
super.setWriteBufferWaterMark(writeBufferWaterMark);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KQueueSocketChannelConfig setMessageSizeEstimator(MessageSizeEstimator estimator) {
|
||||
super.setMessageSizeEstimator(estimator);
|
||||
return this;
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
/**
|
||||
* This class is necessary to break the following cyclic dependency:
|
||||
* <ol>
|
||||
* <li>JNI_OnLoad</li>
|
||||
* <li>JNI Calls FindClass because RegisterNatives (used to register JNI methods) requires a class</li>
|
||||
* <li>FindClass loads the class, but static members variables of that class attempt to call a JNI method which has not
|
||||
* yet been registered.</li>
|
||||
* <li>java.lang.UnsatisfiedLinkError is thrown because native method has not yet been registered.</li>
|
||||
* </ol>
|
||||
* Static members which call JNI methods must not be declared in this class!
|
||||
*/
|
||||
final class KQueueStaticallyReferencedJniMethods {
|
||||
private KQueueStaticallyReferencedJniMethods() { }
|
||||
|
||||
static native short evAdd();
|
||||
static native short evEnable();
|
||||
static native short evDisable();
|
||||
static native short evDelete();
|
||||
static native short evClear();
|
||||
static native short evEOF();
|
||||
static native short evError();
|
||||
|
||||
static native short evfiltRead();
|
||||
static native short evfiltWrite();
|
||||
static native short evfiltUser();
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.channel.unix.FileDescriptor;
|
||||
import io.netty.util.internal.NativeLibraryLoader;
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
import io.netty.util.internal.SystemPropertyUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
||||
import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.evAdd;
|
||||
import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.evClear;
|
||||
import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.evDelete;
|
||||
import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.evDisable;
|
||||
import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.evEOF;
|
||||
import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.evEnable;
|
||||
import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.evError;
|
||||
import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.evfiltRead;
|
||||
import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.evfiltUser;
|
||||
import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.evfiltWrite;
|
||||
import static io.netty.channel.unix.Errors.newIOException;
|
||||
|
||||
/**
|
||||
* Navite helper methods
|
||||
* <p><strong>Internal usage only!</strong>
|
||||
*/
|
||||
final class Native {
|
||||
static {
|
||||
try {
|
||||
// First, try calling a side-effect free JNI method to see if the library was already
|
||||
// loaded by the application.
|
||||
sizeofKEvent();
|
||||
} catch (UnsatisfiedLinkError ignore) {
|
||||
// The library was not previously loaded, load it now.
|
||||
loadNativeLibrary();
|
||||
}
|
||||
}
|
||||
|
||||
static final short EV_ADD = evAdd();
|
||||
static final short EV_ENABLE = evEnable();
|
||||
static final short EV_DISABLE = evDisable();
|
||||
static final short EV_DELETE = evDelete();
|
||||
static final short EV_CLEAR = evClear();
|
||||
static final short EV_ERROR = evError();
|
||||
static final short EV_EOF = evEOF();
|
||||
|
||||
// Commonly used combinations of EV defines
|
||||
static final short EV_ADD_CLEAR_ENABLE = (short) (EV_ADD | EV_CLEAR | EV_ENABLE);
|
||||
static final short EV_DELETE_DISABLE = (short) (EV_DELETE | EV_DISABLE);
|
||||
|
||||
static final short EVFILT_READ = evfiltRead();
|
||||
static final short EVFILT_WRITE = evfiltWrite();
|
||||
static final short EVFILT_USER = evfiltUser();
|
||||
|
||||
static FileDescriptor newKQueue() {
|
||||
return new FileDescriptor(kqueueCreate());
|
||||
}
|
||||
|
||||
static int keventWait(int kqueueFd, KQueueEventArray changeList, KQueueEventArray eventList,
|
||||
int tvSec, int tvNsec) throws IOException {
|
||||
int ready = keventWait(kqueueFd, changeList.memoryAddress(), changeList.size(),
|
||||
eventList.memoryAddress(), eventList.capacity(), tvSec, tvNsec);
|
||||
if (ready < 0) {
|
||||
throw newIOException("kevent", ready);
|
||||
}
|
||||
return ready;
|
||||
}
|
||||
|
||||
private static native int kqueueCreate();
|
||||
private static native int keventWait(int kqueueFd, long changeListAddress, int changeListLength,
|
||||
long eventListAddress, int eventListLength, int tvSec, int tvNsec);
|
||||
static native int keventTriggerUserEvent(int kqueueFd, int ident);
|
||||
static native int keventAddUserEvent(int kqueueFd, int ident);
|
||||
|
||||
// kevent related
|
||||
static native int sizeofKEvent();
|
||||
static native int offsetofKEventIdent();
|
||||
static native int offsetofKEventFlags();
|
||||
static native int offsetofKEventFFlags();
|
||||
static native int offsetofKEventFilter();
|
||||
static native int offsetofKeventData();
|
||||
|
||||
private static void loadNativeLibrary() {
|
||||
String name = SystemPropertyUtil.get("os.name").toLowerCase(Locale.UK).trim();
|
||||
if (!name.startsWith("mac") && !name.contains("bsd") && !name.startsWith("darwin")) {
|
||||
throw new IllegalStateException("Only supported on BSD");
|
||||
}
|
||||
NativeLibraryLoader.load(SystemPropertyUtil.get("io.netty.packagePrefix", "").replace('.', '-') +
|
||||
"netty-transport-native-kqueue", PlatformDependent.getClassLoader(Native.class));
|
||||
}
|
||||
|
||||
private Native() {
|
||||
// utility
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
|
||||
import static io.netty.channel.unix.Limits.SIZEOF_JLONG;
|
||||
|
||||
final class NativeLongArray {
|
||||
private long memoryAddress;
|
||||
private int capacity;
|
||||
private int size;
|
||||
|
||||
NativeLongArray(int capacity) {
|
||||
if (capacity < 1) {
|
||||
throw new IllegalArgumentException("capacity must be >= 1 but was " + capacity);
|
||||
}
|
||||
memoryAddress = PlatformDependent.allocateMemory(capacity * SIZEOF_JLONG);
|
||||
this.capacity = capacity;
|
||||
}
|
||||
|
||||
void add(long value) {
|
||||
checkSize();
|
||||
PlatformDependent.putLong(memoryOffset(size++), value);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
size = 0;
|
||||
}
|
||||
|
||||
boolean isEmpty() {
|
||||
return size == 0;
|
||||
}
|
||||
|
||||
void free() {
|
||||
PlatformDependent.freeMemory(memoryAddress);
|
||||
memoryAddress = 0;
|
||||
}
|
||||
|
||||
long memoryAddress() {
|
||||
return memoryAddress;
|
||||
}
|
||||
|
||||
long memoryAddressEnd() {
|
||||
return memoryOffset(size);
|
||||
}
|
||||
|
||||
private long memoryOffset(int index) {
|
||||
return memoryAddress + index * SIZEOF_JLONG;
|
||||
}
|
||||
|
||||
private void checkSize() {
|
||||
if (size == capacity) {
|
||||
realloc();
|
||||
}
|
||||
}
|
||||
|
||||
private void realloc() {
|
||||
// Double the capacity while it is "sufficiently small", and otherwise increase by 50%.
|
||||
int newLength = capacity <= 65536 ? capacity << 1 : capacity + capacity >> 1;
|
||||
long newMemoryAddress = PlatformDependent.reallocateMemory(memoryAddress, newLength * SIZEOF_JLONG);
|
||||
if (newMemoryAddress == 0) {
|
||||
throw new OutOfMemoryError("unable to allocate " + newLength + " new bytes! Existing capacity is: "
|
||||
+ capacity);
|
||||
}
|
||||
memoryAddress = newMemoryAddress;
|
||||
capacity = newLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "memoryAddress: " + memoryAddress + " capacity: " + capacity + " size: " + size;
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* BSD specific transport.
|
||||
*/
|
||||
@UnstableApi
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.util.internal.UnstableApi;
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.channel.unix.DomainSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.UUID;
|
||||
|
||||
public class KQueueAbstractDomainSocketEchoTest extends KQueueDomainSocketEchoTest {
|
||||
@Override
|
||||
protected SocketAddress newSocketAddress() {
|
||||
return new DomainSocketAddress(System.getProperty("java.io.tmpdir") + UUID.randomUUID());
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.channel.ChannelException;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class KQueueChannelConfigTest {
|
||||
@Before
|
||||
public void before() {
|
||||
KQueue.ensureAvailability();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptionGetThrowsChannelException() throws Exception {
|
||||
KQueueSocketChannel channel = new KQueueSocketChannel();
|
||||
channel.config().getSoLinger();
|
||||
channel.fd().close();
|
||||
try {
|
||||
channel.config().getSoLinger();
|
||||
fail();
|
||||
} catch (ChannelException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptionSetThrowsChannelException() throws Exception {
|
||||
KQueueSocketChannel channel = new KQueueSocketChannel();
|
||||
channel.config().setKeepAlive(true);
|
||||
channel.fd().close();
|
||||
try {
|
||||
channel.config().setKeepAlive(true);
|
||||
fail();
|
||||
} catch (ChannelException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.testsuite.transport.TestsuitePermutation;
|
||||
import io.netty.testsuite.transport.socket.DatagramUnicastTest;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class KQueueDatagramUnicastTest extends DatagramUnicastTest {
|
||||
@Override
|
||||
protected List<TestsuitePermutation.BootstrapComboFactory<Bootstrap, Bootstrap>> newFactories() {
|
||||
return KQueueSocketTestPermutation.INSTANCE.datagram();
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.testsuite.transport.TestsuitePermutation;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
import java.util.List;
|
||||
|
||||
public class KQueueDomainSocketEchoTest extends KQueueSocketEchoTest {
|
||||
@Override
|
||||
protected SocketAddress newSocketAddress() {
|
||||
return KQueueSocketTestPermutation.newSocketAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<TestsuitePermutation.BootstrapComboFactory<ServerBootstrap, Bootstrap>> newFactories() {
|
||||
return KQueueSocketTestPermutation.INSTANCE.domainSocket();
|
||||
}
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.channel.unix.DomainSocketReadMode;
|
||||
import io.netty.channel.unix.FileDescriptor;
|
||||
import io.netty.testsuite.transport.TestsuitePermutation;
|
||||
import io.netty.testsuite.transport.socket.AbstractSocketTest;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
public class KQueueDomainSocketFdTest extends AbstractSocketTest {
|
||||
@Override
|
||||
protected SocketAddress newSocketAddress() {
|
||||
return KQueueSocketTestPermutation.newSocketAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<TestsuitePermutation.BootstrapComboFactory<ServerBootstrap, Bootstrap>> newFactories() {
|
||||
return KQueueSocketTestPermutation.INSTANCE.domainSocket();
|
||||
}
|
||||
|
||||
@Test(timeout = 30000)
|
||||
public void testSendRecvFd() throws Throwable {
|
||||
run();
|
||||
}
|
||||
|
||||
public void testSendRecvFd(ServerBootstrap sb, Bootstrap cb) throws Throwable {
|
||||
final BlockingQueue<Object> queue = new LinkedBlockingQueue<Object>(1);
|
||||
sb.childHandler(new ChannelInboundHandlerAdapter() {
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
// Create new channel and obtain a file descriptor from it.
|
||||
final KQueueDomainSocketChannel ch = new KQueueDomainSocketChannel();
|
||||
|
||||
ctx.writeAndFlush(ch.fd()).addListener(new ChannelFutureListener() {
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture future) throws Exception {
|
||||
if (!future.isSuccess()) {
|
||||
Throwable cause = future.cause();
|
||||
queue.offer(cause);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
cb.handler(new ChannelInboundHandlerAdapter() {
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
FileDescriptor fd = (FileDescriptor) msg;
|
||||
queue.offer(fd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
queue.add(cause);
|
||||
ctx.close();
|
||||
}
|
||||
});
|
||||
cb.option(KQueueChannelOption.DOMAIN_SOCKET_READ_MODE,
|
||||
DomainSocketReadMode.FILE_DESCRIPTORS);
|
||||
Channel sc = sb.bind().sync().channel();
|
||||
Channel cc = cb.connect().sync().channel();
|
||||
|
||||
Object received = queue.take();
|
||||
cc.close().sync();
|
||||
sc.close().sync();
|
||||
|
||||
if (received instanceof FileDescriptor) {
|
||||
FileDescriptor fd = (FileDescriptor) received;
|
||||
Assert.assertTrue(fd.isOpen());
|
||||
fd.close();
|
||||
Assert.assertFalse(fd.isOpen());
|
||||
Assert.assertNull(queue.poll());
|
||||
} else {
|
||||
throw (Throwable) received;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.testsuite.transport.TestsuitePermutation;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
import java.util.List;
|
||||
|
||||
public class KQueueDomainSocketFileRegionTest extends KQueueSocketFileRegionTest {
|
||||
@Override
|
||||
protected SocketAddress newSocketAddress() {
|
||||
return KQueueSocketTestPermutation.newSocketAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<TestsuitePermutation.BootstrapComboFactory<ServerBootstrap, Bootstrap>> newFactories() {
|
||||
return KQueueSocketTestPermutation.INSTANCE.domainSocket();
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.testsuite.transport.TestsuitePermutation;
|
||||
import io.netty.testsuite.transport.socket.SocketFixedLengthEchoTest;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
import java.util.List;
|
||||
|
||||
public class KQueueDomainSocketFixedLengthEchoTest extends SocketFixedLengthEchoTest {
|
||||
|
||||
@Override
|
||||
protected SocketAddress newSocketAddress() {
|
||||
return KQueueSocketTestPermutation.newSocketAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<TestsuitePermutation.BootstrapComboFactory<ServerBootstrap, Bootstrap>> newFactories() {
|
||||
return KQueueSocketTestPermutation.INSTANCE.domainSocket();
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.testsuite.transport.TestsuitePermutation;
|
||||
import io.netty.testsuite.transport.socket.SocketGatheringWriteTest;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
import java.util.List;
|
||||
|
||||
public class KQueueDomainSocketGatheringWriteTest extends SocketGatheringWriteTest {
|
||||
|
||||
@Override
|
||||
protected SocketAddress newSocketAddress() {
|
||||
return KQueueSocketTestPermutation.newSocketAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<TestsuitePermutation.BootstrapComboFactory<ServerBootstrap, Bootstrap>> newFactories() {
|
||||
return KQueueSocketTestPermutation.INSTANCE.domainSocket();
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.testsuite.transport.TestsuitePermutation;
|
||||
import io.netty.testsuite.transport.socket.SocketObjectEchoTest;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
import java.util.List;
|
||||
|
||||
public class KQueueDomainSocketObjectEchoTest extends SocketObjectEchoTest {
|
||||
@Override
|
||||
protected SocketAddress newSocketAddress() {
|
||||
return KQueueSocketTestPermutation.newSocketAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<TestsuitePermutation.BootstrapComboFactory<ServerBootstrap, Bootstrap>> newFactories() {
|
||||
return KQueueSocketTestPermutation.INSTANCE.domainSocket();
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.testsuite.transport.TestsuitePermutation;
|
||||
import io.netty.testsuite.transport.socket.SocketSslEchoTest;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
import java.util.List;
|
||||
|
||||
public class KQueueDomainSocketSslEchoTest extends SocketSslEchoTest {
|
||||
public KQueueDomainSocketSslEchoTest(
|
||||
SslContext serverCtx, SslContext clientCtx, Renegotiation renegotiation,
|
||||
boolean serverUsesDelegatedTaskExecutor, boolean clientUsesDelegatedTaskExecutor,
|
||||
boolean autoRead, boolean useChunkedWriteHandler, boolean useCompositeByteBuf) {
|
||||
|
||||
super(serverCtx, clientCtx, renegotiation,
|
||||
serverUsesDelegatedTaskExecutor, clientUsesDelegatedTaskExecutor,
|
||||
autoRead, useChunkedWriteHandler, useCompositeByteBuf);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SocketAddress newSocketAddress() {
|
||||
return KQueueSocketTestPermutation.newSocketAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<TestsuitePermutation.BootstrapComboFactory<ServerBootstrap, Bootstrap>> newFactories() {
|
||||
return KQueueSocketTestPermutation.INSTANCE.domainSocket();
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.testsuite.transport.TestsuitePermutation;
|
||||
import io.netty.testsuite.transport.socket.SocketSslGreetingTest;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
import java.util.List;
|
||||
|
||||
public class KQueueDomainSocketSslGreetingTest extends SocketSslGreetingTest {
|
||||
|
||||
public KQueueDomainSocketSslGreetingTest(SslContext serverCtx, SslContext clientCtx) {
|
||||
super(serverCtx, clientCtx);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SocketAddress newSocketAddress() {
|
||||
return KQueueSocketTestPermutation.newSocketAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<TestsuitePermutation.BootstrapComboFactory<ServerBootstrap, Bootstrap>> newFactories() {
|
||||
return KQueueSocketTestPermutation.INSTANCE.domainSocket();
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.testsuite.transport.TestsuitePermutation;
|
||||
import io.netty.testsuite.transport.socket.SocketStartTlsTest;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
import java.util.List;
|
||||
|
||||
public class KQueueDomainSocketStartTlsTest extends SocketStartTlsTest {
|
||||
|
||||
public KQueueDomainSocketStartTlsTest(SslContext serverCtx, SslContext clientCtx) {
|
||||
super(serverCtx, clientCtx);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SocketAddress newSocketAddress() {
|
||||
return KQueueSocketTestPermutation.newSocketAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<TestsuitePermutation.BootstrapComboFactory<ServerBootstrap, Bootstrap>> newFactories() {
|
||||
return KQueueSocketTestPermutation.INSTANCE.domainSocket();
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.testsuite.transport.TestsuitePermutation;
|
||||
import io.netty.testsuite.transport.socket.SocketStringEchoTest;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
import java.util.List;
|
||||
|
||||
public class KQueueDomainSocketStringEchoTest extends SocketStringEchoTest {
|
||||
@Override
|
||||
protected SocketAddress newSocketAddress() {
|
||||
return KQueueSocketTestPermutation.newSocketAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<TestsuitePermutation.BootstrapComboFactory<ServerBootstrap, Bootstrap>> newFactories() {
|
||||
return KQueueSocketTestPermutation.INSTANCE.domainSocket();
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.testsuite.transport.TestsuitePermutation;
|
||||
import io.netty.testsuite.transport.socket.SocketAutoReadTest;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class KQueueETSocketAutoReadTest extends SocketAutoReadTest {
|
||||
@Override
|
||||
protected List<TestsuitePermutation.BootstrapComboFactory<ServerBootstrap, Bootstrap>> newFactories() {
|
||||
return KQueueSocketTestPermutation.INSTANCE.socket();
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.testsuite.transport.TestsuitePermutation;
|
||||
import io.netty.testsuite.transport.socket.SocketExceptionHandlingTest;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class KQueueETSocketExceptionHandlingTest extends SocketExceptionHandlingTest {
|
||||
@Override
|
||||
protected List<TestsuitePermutation.BootstrapComboFactory<ServerBootstrap, Bootstrap>> newFactories() {
|
||||
return KQueueSocketTestPermutation.INSTANCE.socket();
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2017 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.testsuite.transport.TestsuitePermutation;
|
||||
import io.netty.testsuite.transport.socket.SocketHalfClosedTest;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class KQueueETSocketHalfClosedTest extends SocketHalfClosedTest {
|
||||
@Override
|
||||
protected List<TestsuitePermutation.BootstrapComboFactory<ServerBootstrap, Bootstrap>> newFactories() {
|
||||
return KQueueSocketTestPermutation.INSTANCE.socket();
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.testsuite.transport.TestsuitePermutation;
|
||||
import io.netty.testsuite.transport.socket.SocketReadPendingTest;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class KQueueETSocketReadPendingTest extends SocketReadPendingTest {
|
||||
@Override
|
||||
protected List<TestsuitePermutation.BootstrapComboFactory<ServerBootstrap, Bootstrap>> newFactories() {
|
||||
return KQueueSocketTestPermutation.INSTANCE.socket();
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
|
||||
public class KQueueRcvAllocatorOverrideSocketSslEchoTest extends KQueueSocketSslEchoTest {
|
||||
public KQueueRcvAllocatorOverrideSocketSslEchoTest(
|
||||
SslContext serverCtx, SslContext clientCtx, Renegotiation renegotiation,
|
||||
boolean serverUsesDelegatedTaskExecutor, boolean clientUsesDelegatedTaskExecutor,
|
||||
boolean autoRead, boolean useChunkedWriteHandler, boolean useCompositeByteBuf) {
|
||||
|
||||
super(serverCtx, clientCtx, renegotiation,
|
||||
serverUsesDelegatedTaskExecutor, clientUsesDelegatedTaskExecutor,
|
||||
autoRead, useChunkedWriteHandler, useCompositeByteBuf);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(ServerBootstrap bootstrap, Bootstrap bootstrap2, ByteBufAllocator allocator) {
|
||||
super.configure(bootstrap, bootstrap2, allocator);
|
||||
bootstrap.option(KQueueChannelOption.RCV_ALLOC_TRANSPORT_PROVIDES_GUESS, true);
|
||||
bootstrap.childOption(KQueueChannelOption.RCV_ALLOC_TRANSPORT_PROVIDES_GUESS, true);
|
||||
bootstrap2.option(KQueueChannelOption.RCV_ALLOC_TRANSPORT_PROVIDES_GUESS, true);
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assume.assumeThat;
|
||||
|
||||
public class KQueueServerSocketChannelConfigTest {
|
||||
|
||||
private static EventLoopGroup group;
|
||||
private static KQueueServerSocketChannel ch;
|
||||
|
||||
@BeforeClass
|
||||
public static void before() {
|
||||
group = new KQueueEventLoopGroup(1);
|
||||
ServerBootstrap bootstrap = new ServerBootstrap();
|
||||
ch = (KQueueServerSocketChannel) bootstrap.group(group)
|
||||
.channel(KQueueServerSocketChannel.class)
|
||||
.childHandler(new ChannelInboundHandlerAdapter())
|
||||
.bind(new InetSocketAddress(0)).syncUninterruptibly().channel();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void after() {
|
||||
try {
|
||||
ch.close().syncUninterruptibly();
|
||||
} finally {
|
||||
group.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReusePort() {
|
||||
ch.config().setReusePort(false);
|
||||
assertFalse(ch.config().isReusePort());
|
||||
ch.config().setReusePort(true);
|
||||
assertTrue(ch.config().isReusePort());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAcceptFilter() {
|
||||
AcceptFilter currentFilter = ch.config().getAcceptFilter();
|
||||
// Not all platforms support this option (e.g. MacOS doesn't) so test if we support the option first.
|
||||
assumeThat(currentFilter, not(AcceptFilter.PLATFORM_UNSUPPORTED));
|
||||
|
||||
AcceptFilter af = new AcceptFilter("test", "foo");
|
||||
ch.config().setAcceptFilter(af);
|
||||
assertEquals(af, ch.config().getAcceptFilter());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptionsDoesNotThrow() {
|
||||
// If there are some options that are not fully supported they shouldn't throw but instead return some "default"
|
||||
// object.
|
||||
assertFalse(ch.config().getOptions().isEmpty());
|
||||
}
|
||||
}
|
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright 2015 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.ChannelException;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.util.Random;
|
||||
|
||||
import static io.netty.channel.kqueue.BsdSocket.BSD_SND_LOW_AT_MAX;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.junit.Assume.assumeNoException;
|
||||
|
||||
public class KQueueSocketChannelConfigTest {
|
||||
|
||||
private static EventLoopGroup group;
|
||||
private static KQueueSocketChannel ch;
|
||||
private static Random rand;
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() {
|
||||
rand = new Random();
|
||||
group = new KQueueEventLoopGroup(1);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClass() {
|
||||
group.shutdownGracefully();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
Bootstrap bootstrap = new Bootstrap();
|
||||
ch = (KQueueSocketChannel) bootstrap.group(group)
|
||||
.channel(KQueueSocketChannel.class)
|
||||
.handler(new ChannelInboundHandlerAdapter())
|
||||
.bind(new InetSocketAddress(0)).syncUninterruptibly().channel();
|
||||
}
|
||||
|
||||
@After
|
||||
public void teardown() {
|
||||
ch.close().syncUninterruptibly();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRandomSndLowAt() {
|
||||
final int expected = Math.min(BSD_SND_LOW_AT_MAX, Math.abs(rand.nextInt()));
|
||||
final int actual;
|
||||
try {
|
||||
ch.config().setSndLowAt(expected);
|
||||
actual = ch.config().getSndLowAt();
|
||||
} catch (RuntimeException e) {
|
||||
assumeNoException(e);
|
||||
return; // Needed to prevent compile error for final variables to be used below
|
||||
}
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidHighSndLowAt() {
|
||||
try {
|
||||
ch.config().setSndLowAt(Integer.MIN_VALUE);
|
||||
} catch (ChannelException e) {
|
||||
return;
|
||||
} catch (RuntimeException e) {
|
||||
assumeNoException(e);
|
||||
}
|
||||
fail();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTcpNoPush() {
|
||||
ch.config().setTcpNoPush(false);
|
||||
assertFalse(ch.config().isTcpNoPush());
|
||||
ch.config().setTcpNoPush(true);
|
||||
assertTrue(ch.config().isTcpNoPush());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetOptionWhenClosed() {
|
||||
ch.close().syncUninterruptibly();
|
||||
try {
|
||||
ch.config().setSoLinger(0);
|
||||
fail();
|
||||
} catch (ChannelException e) {
|
||||
assertTrue(e.getCause() instanceof ClosedChannelException);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetOptionWhenClosed() {
|
||||
ch.close().syncUninterruptibly();
|
||||
try {
|
||||
ch.config().getSoLinger();
|
||||
fail();
|
||||
} catch (ChannelException e) {
|
||||
assertTrue(e.getCause() instanceof ClosedChannelException);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.testsuite.transport.TestsuitePermutation;
|
||||
import io.netty.testsuite.transport.socket.SocketChannelNotYetConnectedTest;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class KQueueSocketChannelNotYetConnectedTest extends SocketChannelNotYetConnectedTest {
|
||||
@Override
|
||||
protected List<TestsuitePermutation.BootstrapFactory<Bootstrap>> newFactories() {
|
||||
return KQueueSocketTestPermutation.INSTANCE.clientSocket();
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.testsuite.transport.TestsuitePermutation;
|
||||
import io.netty.testsuite.transport.socket.SocketConnectTest;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class KQueueSocketConnectTest extends SocketConnectTest {
|
||||
|
||||
@Override
|
||||
protected List<TestsuitePermutation.BootstrapComboFactory<ServerBootstrap, Bootstrap>> newFactories() {
|
||||
return KQueueSocketTestPermutation.INSTANCE.socket();
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.testsuite.transport.TestsuitePermutation;
|
||||
import io.netty.testsuite.transport.socket.SocketConnectionAttemptTest;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.List;
|
||||
|
||||
public class KQueueSocketConnectionAttemptTest extends SocketConnectionAttemptTest {
|
||||
@Override
|
||||
protected List<TestsuitePermutation.BootstrapFactory<Bootstrap>> newFactories() {
|
||||
return KQueueSocketTestPermutation.INSTANCE.clientSocket();
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.testsuite.transport.TestsuitePermutation;
|
||||
import io.netty.testsuite.transport.socket.SocketEchoTest;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class KQueueSocketEchoTest extends SocketEchoTest {
|
||||
|
||||
@Override
|
||||
protected List<TestsuitePermutation.BootstrapComboFactory<ServerBootstrap, Bootstrap>> newFactories() {
|
||||
return KQueueSocketTestPermutation.INSTANCE.socket();
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.testsuite.transport.TestsuitePermutation;
|
||||
import io.netty.testsuite.transport.socket.SocketFileRegionTest;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class KQueueSocketFileRegionTest extends SocketFileRegionTest {
|
||||
|
||||
@Override
|
||||
protected List<TestsuitePermutation.BootstrapComboFactory<ServerBootstrap, Bootstrap>> newFactories() {
|
||||
return KQueueSocketTestPermutation.INSTANCE.socket();
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.testsuite.transport.TestsuitePermutation;
|
||||
import io.netty.testsuite.transport.socket.SocketFixedLengthEchoTest;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class KQueueSocketFixedLengthEchoTest extends SocketFixedLengthEchoTest {
|
||||
|
||||
@Override
|
||||
protected List<TestsuitePermutation.BootstrapComboFactory<ServerBootstrap, Bootstrap>> newFactories() {
|
||||
return KQueueSocketTestPermutation.INSTANCE.socket();
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.testsuite.transport.TestsuitePermutation;
|
||||
import io.netty.testsuite.transport.socket.SocketGatheringWriteTest;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class KQueueSocketGatheringWriteTest extends SocketGatheringWriteTest {
|
||||
|
||||
@Override
|
||||
protected List<TestsuitePermutation.BootstrapComboFactory<ServerBootstrap, Bootstrap>> newFactories() {
|
||||
return KQueueSocketTestPermutation.INSTANCE.socket();
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.testsuite.transport.TestsuitePermutation;
|
||||
import io.netty.testsuite.transport.socket.SocketMultipleConnectTest;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class KQueueSocketMultipleConnectTest extends SocketMultipleConnectTest {
|
||||
@Override
|
||||
protected List<TestsuitePermutation.BootstrapComboFactory<ServerBootstrap, Bootstrap>> newFactories() {
|
||||
List<TestsuitePermutation.BootstrapComboFactory<ServerBootstrap, Bootstrap>> factories
|
||||
= new ArrayList<TestsuitePermutation.BootstrapComboFactory<ServerBootstrap, Bootstrap>>();
|
||||
for (TestsuitePermutation.BootstrapComboFactory<ServerBootstrap, Bootstrap> comboFactory
|
||||
: KQueueSocketTestPermutation.INSTANCE.socket()) {
|
||||
EventLoopGroup group = comboFactory.newClientInstance().config().group();
|
||||
if (group instanceof NioEventLoopGroup || group instanceof KQueueEventLoopGroup) {
|
||||
factories.add(comboFactory);
|
||||
}
|
||||
}
|
||||
return factories;
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.kqueue;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.testsuite.transport.TestsuitePermutation;
|
||||
import io.netty.testsuite.transport.socket.SocketObjectEchoTest;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class KQueueSocketObjectEchoTest extends SocketObjectEchoTest {
|
||||
|
||||
@Override
|
||||
protected List<TestsuitePermutation.BootstrapComboFactory<ServerBootstrap, Bootstrap>> newFactories() {
|
||||
return KQueueSocketTestPermutation.INSTANCE.socket();
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user