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:
Scott Mitchell 2017-01-19 08:31:34 -08:00
parent 2d38a4453c
commit 3cc4052963
145 changed files with 10346 additions and 1265 deletions

View File

@ -36,7 +36,11 @@
<profiles> <profiles>
<profile> <profile>
<id>full</id> <id>full</id>
<activation>
<property>
<name>uber</name>
</property>
</activation>
<!-- Only include in full profile as this will not work on Java9 yet --> <!-- Only include in full profile as this will not work on Java9 yet -->
<!-- https://issues.apache.org/jira/browse/JXR-133 --> <!-- https://issues.apache.org/jira/browse/JXR-133 -->
<build> <build>
@ -162,25 +166,6 @@
</plugins> </plugins>
</build> </build>
</profile> </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> </profiles>
<dependencies> <dependencies>
@ -347,6 +332,22 @@
<scope>compile</scope> <scope>compile</scope>
<optional>true</optional> <optional>true</optional>
</dependency> </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. --> <!-- Add optional dependencies explicitly to avoid Javadoc warnings and errors. -->
<dependency> <dependency>
@ -429,7 +430,7 @@
<goal>unpack-dependencies</goal> <goal>unpack-dependencies</goal>
</goals> </goals>
<configuration> <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> <includes>io/netty/**,META-INF/native/**</includes>
<includeScope>runtime</includeScope> <includeScope>runtime</includeScope>
<includeGroupIds>${project.groupId}</includeGroupIds> <includeGroupIds>${project.groupId}</includeGroupIds>

View File

@ -24,6 +24,9 @@ import io.netty.util.internal.logging.InternalLoggerFactory;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileReader; import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Inet4Address; import java.net.Inet4Address;
import java.net.Inet6Address; import java.net.Inet6Address;
import java.net.InetAddress; import java.net.InetAddress;
@ -271,12 +274,27 @@ public final class NetUtil {
logger.debug("{}: {}", file, somaxconn); logger.debug("{}: {}", file, somaxconn);
} }
} else { } else {
if (logger.isDebugEnabled()) { // Try to get from sysctl
logger.debug("{}: {} (non-existent)", file, somaxconn); 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) { } 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 { } finally {
if (in != null) { if (in != null) {
try { 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 * 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}. * property to {@code true} will disable IPv6 support. The default value of this property is {@code false}.

View File

@ -24,6 +24,7 @@ import java.security.cert.X509Certificate;
public final class EmptyArrays { public final class EmptyArrays {
public static final int[] EMPTY_INTS = {};
public static final byte[] EMPTY_BYTES = {}; public static final byte[] EMPTY_BYTES = {};
public static final char[] EMPTY_CHARS = {}; public static final char[] EMPTY_CHARS = {};
public static final Object[] EMPTY_OBJECTS = {}; public static final Object[] EMPTY_OBJECTS = {};

View File

@ -287,6 +287,10 @@ public final class PlatformDependent {
PlatformDependent0.freeMemory(address); 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. * Raises an exception bypassing compiler checks for checked exceptions.
*/ */

View File

@ -752,6 +752,10 @@ final class PlatformDependent0 {
UNSAFE.freeMemory(address); UNSAFE.freeMemory(address);
} }
static long reallocateMemory(long address, long newSize) {
return UNSAFE.reallocateMemory(address, newSize);
}
private PlatformDependent0() { private PlatformDependent0() {
} }
} }

View File

@ -51,7 +51,7 @@
<groupId>${project.groupId}</groupId> <groupId>${project.groupId}</groupId>
<artifactId>netty-transport-native-epoll</artifactId> <artifactId>netty-transport-native-epoll</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
<classifier>${epoll.classifier}</classifier> <classifier>${jni.classifier}</classifier>
</dependency> </dependency>
</dependencies> </dependencies>
<build> <build>

125
pom.xml
View File

@ -146,55 +146,72 @@
</os> </os>
</activation> </activation>
<modules> <modules>
<module>transport-native-unix-common-tests</module>
<module>transport-native-unix-common</module>
<module>transport-native-epoll</module> <module>transport-native-epoll</module>
</modules> </modules>
</profile> </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> <profile>
<id>restricted-release</id> <id>mac</id>
<build> <activation>
<plugins> <os>
<plugin> <family>mac</family>
<artifactId>maven-enforcer-plugin</artifactId> </os>
<executions> </activation>
<execution> <modules>
<id>enforce-release-environment</id> <module>transport-native-unix-common-tests</module>
<goals> <module>transport-native-unix-common</module>
<goal>enforce</goal> <module>transport-native-kqueue</module>
</goals> </modules>
<configuration> </profile>
<rules> <profile>
<requireProperty> <id>freebsd</id>
<regexMessage> <activation>
Release process must be performed on linux-x86_64. <os>
</regexMessage> <family>unix</family>
<property>os.detected.classifier</property> <name>freebsd</name>
<regex>^linux-x86_64$</regex> </os>
</requireProperty> </activation>
<requireFilesContent> <modules>
<message> <module>transport-native-unix-common-tests</module>
Release process must be performed on RHEL 6.8 or its derivatives. <module>transport-native-unix-common</module>
</message> <module>transport-native-kqueue</module>
<files> </modules>
<file>/etc/redhat-release</file> </profile>
</files> <profile>
<content>release 6.8</content> <id>openbsd</id>
</requireFilesContent> <activation>
</rules> <os>
</configuration> <family>unix</family>
</execution> <name>openbsd</name>
</executions> </os>
</plugin> </activation>
</plugins> <modules>
</build> <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>
<profile> <profile>
<!-- <!--
@ -252,7 +269,7 @@
<conscrypt.artifactId>conscrypt-openjdk-uber</conscrypt.artifactId> <conscrypt.artifactId>conscrypt-openjdk-uber</conscrypt.artifactId>
<conscrypt.version>1.0.0.RC7</conscrypt.version> <conscrypt.version>1.0.0.RC7</conscrypt.version>
<conscrypt.classifier /> <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.config>${project.basedir}/../common/src/test/resources/logback-test.xml</logging.config>
<logging.logLevel>debug</logging.logLevel> <logging.logLevel>debug</logging.logLevel>
<log4j2.version>2.6.2</log4j2.version> <log4j2.version>2.6.2</log4j2.version>
@ -291,9 +308,7 @@
<module>testsuite-autobahn</module> <module>testsuite-autobahn</module>
<module>testsuite-osgi</module> <module>testsuite-osgi</module>
<module>microbench</module> <module>microbench</module>
<module>all</module>
<module>bom</module> <module>bom</module>
<module>tarball</module>
</modules> </modules>
<dependencyManagement> <dependencyManagement>
@ -979,7 +994,7 @@
<version>2.5.3</version> <version>2.5.3</version>
<configuration> <configuration>
<useReleaseProfile>false</useReleaseProfile> <useReleaseProfile>false</useReleaseProfile>
<arguments>-P restricted-release,sonatype-oss-release,full</arguments> <arguments>-P restricted-release,sonatype-oss-release</arguments>
<autoVersionSubmodules>true</autoVersionSubmodules> <autoVersionSubmodules>true</autoVersionSubmodules>
<allowTimestampedSnapshots>false</allowTimestampedSnapshots> <allowTimestampedSnapshots>false</allowTimestampedSnapshots>
<tagNameFormat>netty-@{project.version}</tagNameFormat> <tagNameFormat>netty-@{project.version}</tagNameFormat>
@ -1110,18 +1125,6 @@
<pluginManagement> <pluginManagement>
<plugins> <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 --> <!-- keep surefire and failsafe in sync -->
<plugin> <plugin>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>

View File

@ -96,6 +96,11 @@
<profiles> <profiles>
<profile> <profile>
<id>full</id> <id>full</id>
<activation>
<property>
<name>uber</name>
</property>
</activation>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>

View File

@ -30,7 +30,10 @@ import java.nio.channels.ClosedChannelException;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue; 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 { public class SocketShutdownOutputBySelfTest extends AbstractClientSocketTest {
@ -43,9 +46,10 @@ public class SocketShutdownOutputBySelfTest extends AbstractClientSocketTest {
TestHandler h = new TestHandler(); TestHandler h = new TestHandler();
ServerSocket ss = new ServerSocket(); ServerSocket ss = new ServerSocket();
Socket s = null; Socket s = null;
SocketChannel ch = null;
try { try {
ss.bind(addr); ss.bind(addr);
SocketChannel ch = (SocketChannel) cb.handler(h).connect().sync().channel(); ch = (SocketChannel) cb.handler(h).connect().sync().channel();
assertTrue(ch.isActive()); assertTrue(ch.isActive());
assertFalse(ch.isOutputShutdown()); assertFalse(ch.isOutputShutdown());
@ -68,12 +72,15 @@ public class SocketShutdownOutputBySelfTest extends AbstractClientSocketTest {
assertTrue(h.ch.isOutputShutdown()); assertTrue(h.ch.isOutputShutdown());
// If half-closed, the peer should be able to write something. // 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()); assertEquals(1, (int) h.queue.take());
} finally { } finally {
if (s != null) { if (s != null) {
s.close(); s.close();
} }
if (ch != null) {
ch.close();
}
ss.close(); ss.close();
} }
} }

View File

@ -27,11 +27,81 @@
<packaging>jar</packaging> <packaging>jar</packaging>
<properties> <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 --> <!-- 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> <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> </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> <dependencies>
<dependency> <dependency>
<groupId>io.netty</groupId> <groupId>io.netty</groupId>
@ -43,6 +113,22 @@
<artifactId>netty-buffer</artifactId> <artifactId>netty-buffer</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </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> <dependency>
<groupId>io.netty</groupId> <groupId>io.netty</groupId>
<artifactId>netty-transport</artifactId> <artifactId>netty-transport</artifactId>
@ -54,6 +140,12 @@
<version>${project.version}</version> <version>${project.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-unix-common-tests</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>${project.groupId}</groupId> <groupId>${project.groupId}</groupId>
<artifactId>${tcnative.artifactId}</artifactId> <artifactId>${tcnative.artifactId}</artifactId>
@ -64,6 +156,29 @@
<build> <build>
<plugins> <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> <plugin>
<groupId>org.fusesource.hawtjni</groupId> <groupId>org.fusesource.hawtjni</groupId>
<artifactId>maven-hawtjni-plugin</artifactId> <artifactId>maven-hawtjni-plugin</artifactId>
@ -122,7 +237,7 @@
<index>true</index> <index>true</index>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile> <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive> </archive>
<classifier>${epoll.classifier}</classifier> <classifier>${jni.classifier}</classifier>
</configuration> </configuration>
</execution> </execution>
</executions> </executions>
@ -201,7 +316,7 @@
<value>${linux.sendmmsg.support}${glibc.sendmmsg.support}</value> <value>${linux.sendmmsg.support}${glibc.sendmmsg.support}</value>
<!-- If glibc and linux kernel are both not sufficient...then define the CFLAGS --> <!-- If glibc and linux kernel are both not sufficient...then define the CFLAGS -->
<regex>.*IO_NETTY_SENDMSSG_NOT_FOUND.*</regex> <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> <failIfNoMatch>false</failIfNoMatch>
</configuration> </configuration>
</execution> </execution>
@ -217,7 +332,7 @@
<value>${jni.compiler.args.cflags}</value> <value>${jni.compiler.args.cflags}</value>
<!-- If glibc and linux kernel are both not sufficient...then define the CFLAGS --> <!-- If glibc and linux kernel are both not sufficient...then define the CFLAGS -->
<regex>^((?!CFLAGS=).)*$</regex> <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> <failIfNoMatch>false</failIfNoMatch>
</configuration> </configuration>
</execution> </execution>

View 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;
}
}

View 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

View File

@ -23,8 +23,8 @@
#include <sys/eventfd.h> #include <sys/eventfd.h>
#include <sys/sendfile.h> #include <sys/sendfile.h>
#include <sys/un.h> #include <sys/un.h>
#include <linux/tcp.h> // TCP_NOTSENT_LOWAT is a linux specific define
#include <netinet/in.h> #include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <unistd.h> #include <unistd.h>
@ -40,28 +40,8 @@
#include "netty_unix_socket.h" #include "netty_unix_socket.h"
#include "netty_unix_errors.h" #include "netty_unix_errors.h"
#include "netty_unix_util.h" #include "netty_unix_util.h"
#include "netty_unix_limits.h"
// Define SO_REUSEPORT if not found to fix build issues. #include "netty_epoll_linuxsocket.h"
// 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
// TCP_FASTOPEN is defined in linux 3.7. We define this here so older kernels can compile. // TCP_FASTOPEN is defined in linux 3.7. We define this here so older kernels can compile.
#ifndef TCP_FASTOPEN #ifndef TCP_FASTOPEN
@ -104,49 +84,9 @@ jfieldID packetPortFieldId = NULL;
jfieldID packetMemoryAddressFieldId = NULL; jfieldID packetMemoryAddressFieldId = NULL;
jfieldID packetCountFieldId = 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 // 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) { static int getSysctlValue(const char * property, int* returnValue) {
int rc = -1; int rc = -1;
FILE *fd=fopen(property, "r"); 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; timeout = MAX_EPOLL_TIMEOUT_MSEC;
} }
clock_gettime(epollWaitClock, &ts); clock_gettime(waitClockId, &ts);
timeBeforeWait = ts.tv_sec * 1000 + ts.tv_nsec / 1000000; timeBeforeWait = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
for (;;) { for (;;) {
@ -248,7 +188,7 @@ static jint netty_epoll_native_epollWait0(JNIEnv* env, jclass clazz, jint efd, j
} }
if ((err = errno) == EINTR) { if ((err = errno) == EINTR) {
if (timeout > 0) { if (timeout > 0) {
clock_gettime(epollWaitClock, &ts); clock_gettime(waitClockId, &ts);
timeNow = ts.tv_sec * 1000 + ts.tv_nsec / 1000000; timeNow = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
timeDiff = timeNow - timeBeforeWait; timeDiff = timeNow - timeBeforeWait;
timeout -= timeDiff; 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) { static jint netty_epoll_native_sendmmsg0(JNIEnv* env, jclass clazz, jint fd, jobjectArray packets, jint offset, jint len) {
struct mmsghdr msg[len]; struct mmsghdr msg[len];
struct sockaddr_storage addr[len]; struct sockaddr_storage addr[len];
socklen_t addrSize;
int i; int i;
memset(msg, 0, sizeof(msg)); 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 scopeId = (*env)->GetIntField(env, packet, packetScopeIdFieldId);
jint port = (*env)->GetIntField(env, packet, packetPortFieldId); 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; return -1;
} }
msg[i].msg_hdr.msg_name = &addr[i]; 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_iov = (struct iovec*) (intptr_t) (*env)->GetLongField(env, packet, packetMemoryAddressFieldId);
msg[i].msg_hdr.msg_iovlen = (*env)->GetIntField(env, packet, packetCountFieldId);; 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; 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) { 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); 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; 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) { static jstring netty_epoll_native_kernelVersion(JNIEnv* env, jclass clazz) {
struct utsname name; struct utsname name;
@ -627,14 +314,6 @@ static jstring netty_epoll_native_kernelVersion(JNIEnv* env, jclass clazz) {
return NULL; 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) { static jboolean netty_epoll_native_isSupportingSendmmsg(JNIEnv* env, jclass clazz) {
if (sendmmsg) { if (sendmmsg) {
return JNI_TRUE; return JNI_TRUE;
@ -699,10 +378,6 @@ static jint netty_epoll_native_splice0(JNIEnv* env, jclass clazz, jint fd, jlong
return (jint) res; 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) { static jint netty_epoll_native_tcpMd5SigMaxKeyLen(JNIEnv* env, jclass clazz) {
struct tcp_md5sig md5sig; struct tcp_md5sig md5sig;
@ -713,43 +388,6 @@ static jint netty_epoll_native_tcpMd5SigMaxKeyLen(JNIEnv* env, jclass clazz) {
return TCP_MD5SIG_MAXKEYLEN; 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 Registered Methods End
// JNI Method Registration Table Begin // JNI Method Registration Table Begin
@ -759,10 +397,7 @@ static const JNINativeMethod statically_referenced_fixed_method_table[] = {
{ "epollout", "()I", (void *) netty_epoll_native_epollout }, { "epollout", "()I", (void *) netty_epoll_native_epollout },
{ "epollrdhup", "()I", (void *) netty_epoll_native_epollrdhup }, { "epollrdhup", "()I", (void *) netty_epoll_native_epollrdhup },
{ "epollerr", "()I", (void *) netty_epoll_native_epollerr }, { "epollerr", "()I", (void *) netty_epoll_native_epollerr },
{ "ssizeMax", "()J", (void *) netty_epoll_native_ssizeMax },
{ "tcpMd5SigMaxKeyLen", "()I", (void *) netty_epoll_native_tcpMd5SigMaxKeyLen }, { "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 }, { "isSupportingSendmmsg", "()Z", (void *) netty_epoll_native_isSupportingSendmmsg },
{ "isSupportingTcpFastopen", "()Z", (void *) netty_epoll_native_isSupportingTcpFastopen }, { "isSupportingTcpFastopen", "()Z", (void *) netty_epoll_native_isSupportingTcpFastopen },
{ "kernelVersion", "()Ljava/lang/String;", (void *) netty_epoll_native_kernelVersion } { "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 }, { "epollCtlMod0", "(III)I", (void *) netty_epoll_native_epollCtlMod0 },
{ "epollCtlDel0", "(II)I", (void *) netty_epoll_native_epollCtlDel0 }, { "epollCtlDel0", "(II)I", (void *) netty_epoll_native_epollCtlDel0 },
// "sendmmsg0" has a dynamic signature // "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 // "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 }, { "sizeofEpollEvent", "()I", (void *) netty_epoll_native_sizeofEpollEvent },
{ "offsetofEpollData", "()I", (void *) netty_epoll_native_offsetofEpollData }, { "offsetofEpollData", "()I", (void *) netty_epoll_native_offsetofEpollData },
{ "splice0", "(IJIJJ)I", (void *) netty_epoll_native_splice0 }, { "splice0", "(IJIJJ)I", (void *) netty_epoll_native_splice0 }
{ "setTcpMd5Sig0", "(I[BI[B)V", (void *) netty_epoll_native_setTcpMd5Sig0 }
}; };
static const jint fixed_method_table_size = sizeof(fixed_method_table) / sizeof(fixed_method_table[0]); 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->signature = netty_unix_util_prepend("(I[L", dynamicTypeName);
dynamicMethod->fnPtr = (void *) netty_epoll_native_sendmmsg0; dynamicMethod->fnPtr = (void *) netty_epoll_native_sendmmsg0;
free(dynamicTypeName); free(dynamicTypeName);
++dynamicMethod; ++dynamicMethod;
dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/DefaultFileRegion;JJJ)J"); dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/DefaultFileRegion;JJJ)J");
dynamicMethod->name = "sendfile0"; dynamicMethod->name = "sendfile0";
@ -864,6 +475,9 @@ static jint netty_epoll_native_JNI_OnLoad(JNIEnv* env, const char* packagePrefix
freeDynamicMethodsTable(dynamicMethods); freeDynamicMethodsTable(dynamicMethods);
dynamicMethods = NULL; dynamicMethods = NULL;
// Load all c modules that we depend upon // 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) { if (netty_unix_errors_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
return 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) { if (netty_unix_socket_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
return JNI_ERR; return JNI_ERR;
} }
if (netty_epoll_linuxsocket_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
return JNI_ERR;
}
// Initialize this module // Initialize this module
char* nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/DefaultFileRegion"); char* nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/DefaultFileRegion");
jclass fileRegionCls = (*env)->FindClass(env, nettyClassName); 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; 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; 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) { static void netty_epoll_native_JNI_OnUnLoad(JNIEnv* env) {
netty_unix_limits_JNI_OnUnLoad(env);
netty_unix_errors_JNI_OnUnLoad(env); netty_unix_errors_JNI_OnUnLoad(env);
netty_unix_filedescriptor_JNI_OnUnLoad(env); netty_unix_filedescriptor_JNI_OnUnLoad(env);
netty_unix_socket_JNI_OnUnLoad(env); netty_unix_socket_JNI_OnUnLoad(env);
} netty_epoll_linuxsocket_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;
} }
jint JNI_OnLoad(JavaVM* vm, void* reserved) { jint JNI_OnLoad(JavaVM* vm, void* reserved) {
@ -1013,11 +594,11 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
jint status = 0; jint status = 0;
// We need to use an address of a function that is uniquely part of this library, so choose a static // 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. // 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"); fprintf(stderr, "FATAL: transport-native-epoll JNI call to dladdr failed!\n");
return JNI_ERR; 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) { if (status == JNI_ERR) {
fprintf(stderr, "FATAL: transport-native-epoll JNI encountered unexpected dlinfo.dli_fname: %s\n", dlinfo.dli_fname); fprintf(stderr, "FATAL: transport-native-epoll JNI encountered unexpected dlinfo.dli_fname: %s\n", dlinfo.dli_fname);
return JNI_ERR; return JNI_ERR;

View File

@ -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);
}

View File

@ -28,6 +28,7 @@ import io.netty.channel.EventLoop;
import io.netty.channel.RecvByteBufAllocator; import io.netty.channel.RecvByteBufAllocator;
import io.netty.channel.socket.ChannelInputShutdownEvent; import io.netty.channel.socket.ChannelInputShutdownEvent;
import io.netty.channel.socket.ChannelInputShutdownReadComplete; import io.netty.channel.socket.ChannelInputShutdownReadComplete;
import io.netty.channel.unix.FileDescriptor;
import io.netty.channel.unix.Socket; import io.netty.channel.unix.Socket;
import io.netty.channel.unix.UnixChannel; import io.netty.channel.unix.UnixChannel;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
@ -43,20 +44,20 @@ import static io.netty.util.internal.ObjectUtil.checkNotNull;
abstract class AbstractEpollChannel extends AbstractChannel implements UnixChannel { abstract class AbstractEpollChannel extends AbstractChannel implements UnixChannel {
private static final ChannelMetadata METADATA = new ChannelMetadata(false); private static final ChannelMetadata METADATA = new ChannelMetadata(false);
private final int readFlag; private final int readFlag;
private final Socket fileDescriptor; final LinuxSocket socket;
protected int flags = Native.EPOLLET; protected int flags = Native.EPOLLET;
boolean inputClosedSeenErrorOnRead; boolean inputClosedSeenErrorOnRead;
boolean epollInReadyRunnablePending; boolean epollInReadyRunnablePending;
protected volatile boolean active; protected volatile boolean active;
AbstractEpollChannel(Socket fd, int flag) { AbstractEpollChannel(LinuxSocket fd, int flag) {
this(null, fd, flag, false); 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); super(parent);
fileDescriptor = checkNotNull(fd, "fd"); socket = checkNotNull(fd, "fd");
readFlag = flag; readFlag = flag;
flags |= flag; flags |= flag;
this.active = active; this.active = active;
@ -89,8 +90,8 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
} }
@Override @Override
public final Socket fd() { public final FileDescriptor fd() {
return fileDescriptor; return socket;
} }
@Override @Override
@ -115,7 +116,7 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
try { try {
doDeregister(); doDeregister();
} finally { } finally {
fileDescriptor.close(); socket.close();
} }
} }
@ -131,7 +132,7 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
@Override @Override
public boolean isOpen() { public boolean isOpen() {
return fileDescriptor.isOpen(); return socket.isOpen();
} }
@Override @Override
@ -158,7 +159,7 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
} }
final boolean shouldBreakEpollInReady(ChannelConfig config) { final boolean shouldBreakEpollInReady(ChannelConfig config) {
return fileDescriptor.isInputShutdown() && (inputClosedSeenErrorOnRead || !isAllowHalfClosure(config)); return socket.isInputShutdown() && (inputClosedSeenErrorOnRead || !isAllowHalfClosure(config));
} }
final boolean isAllowHalfClosure(ChannelConfig config) { final boolean isAllowHalfClosure(ChannelConfig config) {
@ -265,10 +266,10 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
int localReadAmount; int localReadAmount;
unsafe().recvBufAllocHandle().attemptedBytesRead(byteBuf.writableBytes()); unsafe().recvBufAllocHandle().attemptedBytesRead(byteBuf.writableBytes());
if (byteBuf.hasMemoryAddress()) { if (byteBuf.hasMemoryAddress()) {
localReadAmount = fileDescriptor.readAddress(byteBuf.memoryAddress(), writerIndex, byteBuf.capacity()); localReadAmount = socket.readAddress(byteBuf.memoryAddress(), writerIndex, byteBuf.capacity());
} else { } else {
ByteBuffer buf = byteBuf.internalNioBuffer(writerIndex, byteBuf.writableBytes()); 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) { if (localReadAmount > 0) {
byteBuf.writerIndex(writerIndex + localReadAmount); byteBuf.writerIndex(writerIndex + localReadAmount);
@ -283,8 +284,8 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
long memoryAddress = buf.memoryAddress(); long memoryAddress = buf.memoryAddress();
int readerIndex = buf.readerIndex(); int readerIndex = buf.readerIndex();
int writerIndex = buf.writerIndex(); int writerIndex = buf.writerIndex();
for (int i = writeSpinCount - 1; i >= 0; i--) { for (int i = writeSpinCount; i > 0; --i) {
int localFlushedAmount = fileDescriptor.writeAddress(memoryAddress, readerIndex, writerIndex); int localFlushedAmount = socket.writeAddress(memoryAddress, readerIndex, writerIndex);
if (localFlushedAmount > 0) { if (localFlushedAmount > 0) {
writtenBytes += localFlushedAmount; writtenBytes += localFlushedAmount;
if (writtenBytes == readableBytes) { if (writtenBytes == readableBytes) {
@ -302,10 +303,10 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
} else { } else {
nioBuf = buf.nioBuffer(); nioBuf = buf.nioBuffer();
} }
for (int i = writeSpinCount - 1; i >= 0; i--) { for (int i = writeSpinCount; i > 0; --i) {
int pos = nioBuf.position(); int pos = nioBuf.position();
int limit = nioBuf.limit(); int limit = nioBuf.limit();
int localFlushedAmount = fileDescriptor.write(nioBuf, pos, limit); int localFlushedAmount = socket.write(nioBuf, pos, limit);
if (localFlushedAmount > 0) { if (localFlushedAmount > 0) {
nioBuf.position(pos + localFlushedAmount); nioBuf.position(pos + localFlushedAmount);
writtenBytes += localFlushedAmount; writtenBytes += localFlushedAmount;
@ -410,10 +411,10 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
* Shutdown the input side of the channel. * Shutdown the input side of the channel.
*/ */
void shutdownInput(boolean rdHup) { void shutdownInput(boolean rdHup) {
if (!fd().isInputShutdown()) { if (!socket.isInputShutdown()) {
if (isAllowHalfClosure(config())) { if (isAllowHalfClosure(config())) {
try { try {
fd().shutdown(true, false); socket.shutdown(true, false);
} catch (IOException ignored) { } catch (IOException ignored) {
// We attempted to shutdown and failed, which means the input has already effectively been // We attempted to shutdown and failed, which means the input has already effectively been
// shutdown. // shutdown.
@ -422,9 +423,8 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
} catch (NotYetConnectedException ignore) { } catch (NotYetConnectedException ignore) {
// We attempted to shutdown and failed, which means the input has already effectively been // We attempted to shutdown and failed, which means the input has already effectively been
// shutdown. // shutdown.
fireEventAndClose(ChannelInputShutdownEvent.INSTANCE);
return;
} }
clearEpollIn();
pipeline().fireUserEventTriggered(ChannelInputShutdownEvent.INSTANCE); pipeline().fireUserEventTriggered(ChannelInputShutdownEvent.INSTANCE);
} else { } else {
close(voidPromise()); close(voidPromise());
@ -471,7 +471,7 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
* Called once a EPOLLOUT event is ready to be processed * Called once a EPOLLOUT event is ready to be processed
*/ */
void epollOutReady() { void epollOutReady() {
if (fd().isOutputShutdown()) { if (socket.isOutputShutdown()) {
return; return;
} }
// directly call super.flush0() to force a flush now // directly call super.flush0() to force a flush now

View File

@ -23,8 +23,6 @@ import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromise;
import io.netty.channel.EventLoop; import io.netty.channel.EventLoop;
import io.netty.channel.ServerChannel; import io.netty.channel.ServerChannel;
import io.netty.channel.unix.FileDescriptor;
import io.netty.channel.unix.Socket;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.SocketAddress; import java.net.SocketAddress;
@ -32,31 +30,15 @@ import java.net.SocketAddress;
public abstract class AbstractEpollServerChannel extends AbstractEpollChannel implements ServerChannel { public abstract class AbstractEpollServerChannel extends AbstractEpollChannel implements ServerChannel {
private static final ChannelMetadata METADATA = new ChannelMetadata(false, 16); private static final ChannelMetadata METADATA = new ChannelMetadata(false, 16);
/**
* @deprecated Use {@link #AbstractEpollServerChannel(Socket, boolean)}.
*/
@Deprecated
protected AbstractEpollServerChannel(int fd) { protected AbstractEpollServerChannel(int fd) {
this(new Socket(fd), false); this(new LinuxSocket(fd), false);
} }
/** AbstractEpollServerChannel(LinuxSocket fd) {
* @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) {
this(fd, isSoErrorZero(fd)); this(fd, isSoErrorZero(fd));
} }
protected AbstractEpollServerChannel(Socket fd, boolean active) { AbstractEpollServerChannel(LinuxSocket fd, boolean active) {
super(null, fd, Native.EPOLLIN, active); super(null, fd, Native.EPOLLIN, active);
} }
@ -117,6 +99,7 @@ public abstract class AbstractEpollServerChannel extends AbstractEpollChannel im
final ChannelPipeline pipeline = pipeline(); final ChannelPipeline pipeline = pipeline();
allocHandle.reset(config); allocHandle.reset(config);
allocHandle.attemptedBytesRead(1);
epollInBefore(); epollInBefore();
Throwable exception = null; 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 // 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 // EpollRecvByteAllocatorHandle knows if it should try to read again or not when autoRead is
// enabled. // enabled.
allocHandle.lastBytesRead(fd().accept(acceptedAddress)); allocHandle.lastBytesRead(socket.accept(acceptedAddress));
if (allocHandle.lastBytesRead() == -1) { if (allocHandle.lastBytesRead() == -1) {
// this means everything was handled for now // this means everything was handled for now
break; break;
} }
allocHandle.incMessagesRead(1); allocHandle.incMessagesRead(1);
int len = acceptedAddress[0];
readPending = false; readPending = false;
pipeline.fireChannelRead(newChildChannel(allocHandle.lastBytesRead(), acceptedAddress, 1, len)); pipeline.fireChannelRead(newChildChannel(allocHandle.lastBytesRead(), acceptedAddress, 1,
acceptedAddress[0]));
} while (allocHandle.continueReading()); } while (allocHandle.continueReading());
} catch (Throwable t) { } catch (Throwable t) {
exception = t; exception = t;

View File

@ -17,9 +17,7 @@ package io.netty.channel.epoll;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.CompositeByteBuf; import io.netty.buffer.CompositeByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelConfig; import io.netty.channel.ChannelConfig;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
@ -35,7 +33,8 @@ import io.netty.channel.FileRegion;
import io.netty.channel.RecvByteBufAllocator; import io.netty.channel.RecvByteBufAllocator;
import io.netty.channel.socket.DuplexChannel; import io.netty.channel.socket.DuplexChannel;
import io.netty.channel.unix.FileDescriptor; 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.PlatformDependent;
import io.netty.util.internal.StringUtil; import io.netty.util.internal.StringUtil;
import io.netty.util.internal.ThrowableUtil; import io.netty.util.internal.ThrowableUtil;
@ -54,6 +53,7 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static io.netty.channel.unix.FileDescriptor.pipe; 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; import static io.netty.util.internal.ObjectUtil.checkNotNull;
public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel implements DuplexChannel { public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel implements DuplexChannel {
@ -89,45 +89,25 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel im
private WritableByteChannel byteChannel; private WritableByteChannel byteChannel;
/**
* @deprecated Use {@link #AbstractEpollStreamChannel(Channel, Socket)}.
*/
@Deprecated
protected AbstractEpollStreamChannel(Channel parent, int fd) { 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) { protected AbstractEpollStreamChannel(int fd) {
this(new Socket(fd)); this(new LinuxSocket(fd));
} }
/** AbstractEpollStreamChannel(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) {
this(fd, isSoErrorZero(fd)); this(fd, isSoErrorZero(fd));
} }
protected AbstractEpollStreamChannel(Channel parent, Socket fd) { AbstractEpollStreamChannel(Channel parent, LinuxSocket fd) {
super(parent, fd, Native.EPOLLIN, true); super(parent, fd, Native.EPOLLIN, true);
// Add EPOLLRDHUP so we are notified once the remote peer close the connection. // Add EPOLLRDHUP so we are notified once the remote peer close the connection.
flags |= Native.EPOLLRDHUP; flags |= Native.EPOLLRDHUP;
} }
protected AbstractEpollStreamChannel(Socket fd, boolean active) { protected AbstractEpollStreamChannel(LinuxSocket fd, boolean active) {
super(null, fd, Native.EPOLLIN, active); super(null, fd, Native.EPOLLIN, active);
// Add EPOLLRDHUP so we are notified once the remote peer close the connection. // Add EPOLLRDHUP so we are notified once the remote peer close the connection.
flags |= Native.EPOLLRDHUP; flags |= Native.EPOLLRDHUP;
@ -301,8 +281,8 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel im
boolean done = false; boolean done = false;
int offset = 0; int offset = 0;
int end = offset + cnt; int end = offset + cnt;
for (int i = writeSpinCount - 1; i >= 0; i--) { for (int i = writeSpinCount; i > 0; --i) {
long localWrittenBytes = fd().writevAddresses(array.memoryAddress(offset), cnt); long localWrittenBytes = socket.writevAddresses(array.memoryAddress(offset), cnt);
if (localWrittenBytes == 0) { if (localWrittenBytes == 0) {
break; break;
} }
@ -340,8 +320,8 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel im
boolean done = false; boolean done = false;
int offset = 0; int offset = 0;
int end = offset + nioBufferCnt; int end = offset + nioBufferCnt;
for (int i = writeSpinCount - 1; i >= 0; i--) { for (int i = writeSpinCount; i > 0; --i) {
long localWrittenBytes = fd().writev(nioBuffers, offset, nioBufferCnt); long localWrittenBytes = socket.writev(nioBuffers, offset, nioBufferCnt);
if (localWrittenBytes == 0) { if (localWrittenBytes == 0) {
break; break;
} }
@ -390,10 +370,10 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel im
boolean done = false; boolean done = false;
long flushedAmount = 0; 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 offset = region.transferred();
final long localFlushedAmount = final long localFlushedAmount =
Native.sendfile(fd().intValue(), region, baseOffset, offset, regionCount - offset); Native.sendfile(socket.intValue(), region, baseOffset, offset, regionCount - offset);
if (localFlushedAmount == 0) { if (localFlushedAmount == 0) {
break; break;
} }
@ -426,9 +406,9 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel im
long flushedAmount = 0; long flushedAmount = 0;
if (byteChannel == null) { 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()); final long localFlushedAmount = region.transferTo(byteChannel, region.transferred());
if (localFlushedAmount == 0) { if (localFlushedAmount == 0) {
break; 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 // Special handling of CompositeByteBuf to reduce memory copies if some of the Components
// in the CompositeByteBuf are backed by a memoryAddress. // in the CompositeByteBuf are backed by a memoryAddress.
CompositeByteBuf comp = (CompositeByteBuf) buf; 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. // more then 1024 buffers for gathering writes so just do a memory copy.
buf = newDirectBuffer(buf); buf = newDirectBuffer(buf);
assert buf.hasMemoryAddress(); assert buf.hasMemoryAddress();
@ -589,7 +569,7 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel im
private void shutdownOutput0(final ChannelPromise promise) { private void shutdownOutput0(final ChannelPromise promise) {
try { try {
fd().shutdown(false, true); socket.shutdown(false, true);
promise.setSuccess(); promise.setSuccess();
} catch (Throwable cause) { } catch (Throwable cause) {
promise.setFailure(cause); promise.setFailure(cause);
@ -598,7 +578,7 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel im
private void shutdownInput0(final ChannelPromise promise) { private void shutdownInput0(final ChannelPromise promise) {
try { try {
fd().shutdown(true, false); socket.shutdown(true, false);
promise.setSuccess(); promise.setSuccess();
} catch (Throwable cause) { } catch (Throwable cause) {
promise.setFailure(cause); promise.setFailure(cause);
@ -607,7 +587,7 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel im
private void shutdown0(final ChannelPromise promise) { private void shutdown0(final ChannelPromise promise) {
try { try {
fd().shutdown(true, true); socket.shutdown(true, true);
promise.setSuccess(); promise.setSuccess();
} catch (Throwable cause) { } catch (Throwable cause) {
promise.setFailure(cause); promise.setFailure(cause);
@ -616,17 +596,17 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel im
@Override @Override
public boolean isOutputShutdown() { public boolean isOutputShutdown() {
return fd().isOutputShutdown(); return socket.isOutputShutdown();
} }
@Override @Override
public boolean isInputShutdown() { public boolean isInputShutdown() {
return fd().isInputShutdown(); return socket.isInputShutdown();
} }
@Override @Override
public boolean isShutdown() { public boolean isShutdown() {
return fd().isShutdown(); return socket.isShutdown();
} }
@Override @Override
@ -764,12 +744,12 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel im
*/ */
protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception { protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
if (localAddress != null) { if (localAddress != null) {
fd().bind(localAddress); socket.bind(localAddress);
} }
boolean success = false; boolean success = false;
try { try {
boolean connected = fd().connect(remoteAddress); boolean connected = socket.connect(remoteAddress);
if (!connected) { if (!connected) {
setFlag(Native.EPOLLOUT); setFlag(Native.EPOLLOUT);
} }
@ -952,7 +932,7 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel im
* Finish the connect * Finish the connect
*/ */
boolean doFinishConnect() throws Exception { boolean doFinishConnect() throws Exception {
if (fd().finishConnect()) { if (socket.finishConnect()) {
clearFlag(Native.EPOLLOUT); clearFlag(Native.EPOLLOUT);
return true; return true;
} else { } else {
@ -1085,7 +1065,7 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel im
int splicedIn = 0; int splicedIn = 0;
for (;;) { for (;;) {
// Splicing until there is nothing left to splice. // 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) { if (localSplicedIn == 0) {
break; break;
} }
@ -1185,7 +1165,7 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel im
public boolean spliceOut() throws Exception { public boolean spliceOut() throws Exception {
assert ch.eventLoop().inEventLoop(); assert ch.eventLoop().inEventLoop();
try { 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; len -= splicedOut;
if (len == 0) { if (len == 0) {
if (autoRead) { if (autoRead) {
@ -1257,55 +1237,14 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel im
} }
} }
private final class SocketWritableByteChannel implements WritableByteChannel { private final class EpollSocketWritableByteChannel extends SocketWritableByteChannel {
EpollSocketWritableByteChannel() {
@Override super(socket);
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;
} }
@Override @Override
public boolean isOpen() { protected ByteBufAllocator alloc() {
return fd().isOpen(); return AbstractEpollStreamChannel.this.alloc();
}
@Override
public void close() throws IOException {
fd().close();
} }
} }
} }

View File

@ -22,7 +22,6 @@ import io.netty.channel.DefaultChannelConfig;
import io.netty.channel.MessageSizeEstimator; import io.netty.channel.MessageSizeEstimator;
import io.netty.channel.RecvByteBufAllocator; import io.netty.channel.RecvByteBufAllocator;
import io.netty.channel.WriteBufferWaterMark; import io.netty.channel.WriteBufferWaterMark;
import java.io.IOException; import java.io.IOException;
import java.util.Map; import java.util.Map;

View File

@ -16,15 +16,13 @@
package io.netty.channel.epoll; package io.netty.channel.epoll;
import io.netty.channel.ChannelOption; import io.netty.channel.ChannelOption;
import io.netty.channel.unix.DomainSocketReadMode; import io.netty.channel.unix.UnixChannelOption;
import java.net.InetAddress; import java.net.InetAddress;
import java.util.Map; 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> 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<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_KEEPIDLE = valueOf(EpollChannelOption.class, "TCP_KEEPIDLE");
public static final ChannelOption<Integer> TCP_KEEPINTVL = valueOf(EpollChannelOption.class, "TCP_KEEPINTVL"); 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"); ChannelOption.valueOf(EpollChannelOption.class, "TCP_DEFER_ACCEPT");
public static final ChannelOption<Boolean> TCP_QUICKACK = valueOf(EpollChannelOption.class, "TCP_QUICKACK"); 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 = public static final ChannelOption<EpollMode> EPOLL_MODE =
ChannelOption.valueOf(EpollChannelOption.class, "EPOLL_MODE"); ChannelOption.valueOf(EpollChannelOption.class, "EPOLL_MODE");
@ -46,6 +42,5 @@ public final class EpollChannelOption<T> extends ChannelOption<T> {
@SuppressWarnings({ "unused", "deprecation" }) @SuppressWarnings({ "unused", "deprecation" })
private EpollChannelOption() { private EpollChannelOption() {
super(null);
} }
} }

View File

@ -29,8 +29,7 @@ import io.netty.channel.socket.DatagramChannel;
import io.netty.channel.socket.DatagramChannelConfig; import io.netty.channel.socket.DatagramChannelConfig;
import io.netty.channel.socket.DatagramPacket; import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.unix.DatagramSocketAddress; import io.netty.channel.unix.DatagramSocketAddress;
import io.netty.channel.unix.FileDescriptor; import io.netty.channel.unix.IovArray;
import io.netty.channel.unix.Socket;
import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.StringUtil; import io.netty.util.internal.StringUtil;
@ -45,7 +44,8 @@ import java.nio.channels.NotYetConnectedException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; 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 * {@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); config = new EpollDatagramChannelConfig(this);
} }
/** public EpollDatagramChannel(int fd) {
* @deprecated Use {@link #EpollDatagramChannel(Socket)}. this(new LinuxSocket(fd));
*/
@Deprecated
public EpollDatagramChannel(FileDescriptor fd) {
this(new Socket(fd.intValue()));
} }
public EpollDatagramChannel(Socket fd) { EpollDatagramChannel(LinuxSocket fd) {
super(null, fd, Native.EPOLLIN, true); super(null, fd, Native.EPOLLIN, true);
// As we create an EpollDatagramChannel from a FileDescriptor we should try to obtain the remote and local // 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. // 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 @Override
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public boolean isActive() { public boolean isActive() {
return fd().isOpen() && (config.getActiveOnOpen() && isRegistered() || active); return socket.isOpen() && (config.getActiveOnOpen() && isRegistered() || active);
} }
@Override @Override
@ -280,8 +276,8 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
protected void doBind(SocketAddress localAddress) throws Exception { protected void doBind(SocketAddress localAddress) throws Exception {
InetSocketAddress addr = (InetSocketAddress) localAddress; InetSocketAddress addr = (InetSocketAddress) localAddress;
checkResolvable(addr); checkResolvable(addr);
fd().bind(addr); socket.bind(addr);
local = fd().localAddress(); local = socket.localAddress();
active = true; active = true;
} }
@ -307,7 +303,7 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
NativeDatagramPacketArray.NativeDatagramPacket[] packets = array.packets(); NativeDatagramPacketArray.NativeDatagramPacket[] packets = array.packets();
while (cnt > 0) { while (cnt > 0) {
int send = Native.sendmmsg(fd().intValue(), packets, offset, cnt); int send = Native.sendmmsg(socket.intValue(), packets, offset, cnt);
if (send == 0) { if (send == 0) {
// Did not write all messages. // Did not write all messages.
setFlag(Native.EPOLLOUT); setFlag(Native.EPOLLOUT);
@ -323,7 +319,7 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
} }
} }
boolean done = false; boolean done = false;
for (int i = config().getWriteSpinCount() - 1; i >= 0; i--) { for (int i = config().getWriteSpinCount(); i > 0; --i) {
if (doWriteMessage(msg)) { if (doWriteMessage(msg)) {
done = true; done = true;
break; break;
@ -375,7 +371,7 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
final int writtenBytes; final int writtenBytes;
if (data.hasMemoryAddress()) { if (data.hasMemoryAddress()) {
long memoryAddress = data.memoryAddress(); long memoryAddress = data.memoryAddress();
writtenBytes = fd().sendToAddress(memoryAddress, data.readerIndex(), data.writerIndex(), writtenBytes = socket.sendToAddress(memoryAddress, data.readerIndex(), data.writerIndex(),
remoteAddress.getAddress(), remoteAddress.getPort()); remoteAddress.getAddress(), remoteAddress.getPort());
} else if (data instanceof CompositeByteBuf) { } else if (data instanceof CompositeByteBuf) {
IovArray array = ((EpollEventLoop) eventLoop()).cleanArray(); IovArray array = ((EpollEventLoop) eventLoop()).cleanArray();
@ -383,11 +379,11 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
int cnt = array.count(); int cnt = array.count();
assert cnt != 0; assert cnt != 0;
writtenBytes = fd().sendToAddresses(array.memoryAddress(0), writtenBytes = socket.sendToAddresses(array.memoryAddress(0),
cnt, remoteAddress.getAddress(), remoteAddress.getPort()); cnt, remoteAddress.getAddress(), remoteAddress.getPort());
} else { } else {
ByteBuffer nioData = data.internalNioBuffer(data.readerIndex(), data.readableBytes()); 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()); 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 // Special handling of CompositeByteBuf to reduce memory copies if some of the Components
// in the CompositeByteBuf are backed by a memoryAddress. // in the CompositeByteBuf are backed by a memoryAddress.
CompositeByteBuf comp = (CompositeByteBuf) content; CompositeByteBuf comp = (CompositeByteBuf) content;
if (comp.isDirect() && comp.nioBufferCount() <= Native.IOV_MAX) { if (comp.isDirect() && comp.nioBufferCount() <= IOV_MAX) {
return msg; 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 // Special handling of CompositeByteBuf to reduce memory copies if some of the Components
// in the CompositeByteBuf are backed by a memoryAddress. // in the CompositeByteBuf are backed by a memoryAddress.
CompositeByteBuf comp = (CompositeByteBuf) buf; 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. // more then 1024 buffers for gathering writes so just do a memory copy.
buf = newDirectBuffer(buf); buf = newDirectBuffer(buf);
assert buf.hasMemoryAddress(); 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 // Special handling of CompositeByteBuf to reduce memory copies if some of the Components
// in the CompositeByteBuf are backed by a memoryAddress. // in the CompositeByteBuf are backed by a memoryAddress.
CompositeByteBuf comp = (CompositeByteBuf) content; CompositeByteBuf comp = (CompositeByteBuf) content;
if (comp.isDirect() && comp.nioBufferCount() <= Native.IOV_MAX) { if (comp.isDirect() && comp.nioBufferCount() <= IOV_MAX) {
return e; return e;
} }
} }
@ -494,7 +490,7 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
checkResolvable(remoteAddress); checkResolvable(remoteAddress);
EpollDatagramChannel.this.remote = remoteAddress; EpollDatagramChannel.this.remote = remoteAddress;
EpollDatagramChannel.this.local = fd().localAddress(); EpollDatagramChannel.this.local = socket.localAddress();
success = true; success = true;
// First notify the promise before notifying the handler. // First notify the promise before notifying the handler.
@ -543,11 +539,11 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
final DatagramSocketAddress remoteAddress; final DatagramSocketAddress remoteAddress;
if (data.hasMemoryAddress()) { if (data.hasMemoryAddress()) {
// has a memory address so use optimized call // has a memory address so use optimized call
remoteAddress = fd().recvFromAddress(data.memoryAddress(), data.writerIndex(), remoteAddress = socket.recvFromAddress(data.memoryAddress(), data.writerIndex(),
data.capacity()); data.capacity());
} else { } else {
ByteBuffer nioData = data.internalNioBuffer(data.writerIndex(), data.writableBytes()); 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) { if (remoteAddress == null) {

View File

@ -23,7 +23,6 @@ import io.netty.channel.MessageSizeEstimator;
import io.netty.channel.RecvByteBufAllocator; import io.netty.channel.RecvByteBufAllocator;
import io.netty.channel.WriteBufferWaterMark; import io.netty.channel.WriteBufferWaterMark;
import io.netty.channel.socket.DatagramChannelConfig; import io.netty.channel.socket.DatagramChannelConfig;
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.NetworkInterface; import java.net.NetworkInterface;
@ -208,7 +207,7 @@ public final class EpollDatagramChannelConfig extends EpollChannelConfig impleme
@Override @Override
public int getSendBufferSize() { public int getSendBufferSize() {
try { try {
return datagramChannel.fd().getSendBufferSize(); return datagramChannel.socket.getSendBufferSize();
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
} }
@ -217,7 +216,7 @@ public final class EpollDatagramChannelConfig extends EpollChannelConfig impleme
@Override @Override
public EpollDatagramChannelConfig setSendBufferSize(int sendBufferSize) { public EpollDatagramChannelConfig setSendBufferSize(int sendBufferSize) {
try { try {
datagramChannel.fd().setSendBufferSize(sendBufferSize); datagramChannel.socket.setSendBufferSize(sendBufferSize);
return this; return this;
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
@ -227,7 +226,7 @@ public final class EpollDatagramChannelConfig extends EpollChannelConfig impleme
@Override @Override
public int getReceiveBufferSize() { public int getReceiveBufferSize() {
try { try {
return datagramChannel.fd().getReceiveBufferSize(); return datagramChannel.socket.getReceiveBufferSize();
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
} }
@ -236,7 +235,7 @@ public final class EpollDatagramChannelConfig extends EpollChannelConfig impleme
@Override @Override
public EpollDatagramChannelConfig setReceiveBufferSize(int receiveBufferSize) { public EpollDatagramChannelConfig setReceiveBufferSize(int receiveBufferSize) {
try { try {
datagramChannel.fd().setReceiveBufferSize(receiveBufferSize); datagramChannel.socket.setReceiveBufferSize(receiveBufferSize);
return this; return this;
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
@ -246,7 +245,7 @@ public final class EpollDatagramChannelConfig extends EpollChannelConfig impleme
@Override @Override
public int getTrafficClass() { public int getTrafficClass() {
try { try {
return Native.getTrafficClass(datagramChannel.fd().intValue()); return datagramChannel.socket.getTrafficClass();
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
} }
@ -255,7 +254,7 @@ public final class EpollDatagramChannelConfig extends EpollChannelConfig impleme
@Override @Override
public EpollDatagramChannelConfig setTrafficClass(int trafficClass) { public EpollDatagramChannelConfig setTrafficClass(int trafficClass) {
try { try {
Native.setTrafficClass(datagramChannel.fd().intValue(), trafficClass); datagramChannel.socket.setTrafficClass(trafficClass);
return this; return this;
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
@ -265,7 +264,7 @@ public final class EpollDatagramChannelConfig extends EpollChannelConfig impleme
@Override @Override
public boolean isReuseAddress() { public boolean isReuseAddress() {
try { try {
return Native.isReuseAddress(datagramChannel.fd().intValue()) == 1; return datagramChannel.socket.isReuseAddress();
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
} }
@ -274,7 +273,7 @@ public final class EpollDatagramChannelConfig extends EpollChannelConfig impleme
@Override @Override
public EpollDatagramChannelConfig setReuseAddress(boolean reuseAddress) { public EpollDatagramChannelConfig setReuseAddress(boolean reuseAddress) {
try { try {
Native.setReuseAddress(datagramChannel.fd().intValue(), reuseAddress ? 1 : 0); datagramChannel.socket.setReuseAddress(reuseAddress);
return this; return this;
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
@ -284,7 +283,7 @@ public final class EpollDatagramChannelConfig extends EpollChannelConfig impleme
@Override @Override
public boolean isBroadcast() { public boolean isBroadcast() {
try { try {
return Native.isBroadcast(datagramChannel.fd().intValue()) == 1; return datagramChannel.socket.isBroadcast();
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
} }
@ -293,7 +292,7 @@ public final class EpollDatagramChannelConfig extends EpollChannelConfig impleme
@Override @Override
public EpollDatagramChannelConfig setBroadcast(boolean broadcast) { public EpollDatagramChannelConfig setBroadcast(boolean broadcast) {
try { try {
Native.setBroadcast(datagramChannel.fd().intValue(), broadcast ? 1 : 0); datagramChannel.socket.setBroadcast(broadcast);
return this; return this;
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
@ -351,7 +350,7 @@ public final class EpollDatagramChannelConfig extends EpollChannelConfig impleme
*/ */
public boolean isReusePort() { public boolean isReusePort() {
try { try {
return Native.isReusePort(datagramChannel.fd().intValue()) == 1; return datagramChannel.socket.isReusePort();
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
} }
@ -366,7 +365,7 @@ public final class EpollDatagramChannelConfig extends EpollChannelConfig impleme
*/ */
public EpollDatagramChannelConfig setReusePort(boolean reusePort) { public EpollDatagramChannelConfig setReusePort(boolean reusePort) {
try { try {
Native.setReusePort(datagramChannel.fd().intValue(), reusePort ? 1 : 0); datagramChannel.socket.setReusePort(reusePort);
return this; return this;
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);

View File

@ -23,12 +23,12 @@ import io.netty.channel.unix.DomainSocketAddress;
import io.netty.channel.unix.DomainSocketChannel; import io.netty.channel.unix.DomainSocketChannel;
import io.netty.channel.unix.FileDescriptor; import io.netty.channel.unix.FileDescriptor;
import io.netty.channel.unix.PeerCredentials; 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 java.net.SocketAddress;
import static io.netty.channel.unix.Socket.newSocketDomain; import static io.netty.channel.epoll.LinuxSocket.newSocketDomain;
import java.io.IOException;
public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel implements DomainSocketChannel { public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel implements DomainSocketChannel {
private final EpollDomainSocketChannelConfig config = new EpollDomainSocketChannelConfig(this); private final EpollDomainSocketChannelConfig config = new EpollDomainSocketChannelConfig(this);
@ -40,33 +40,20 @@ public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel i
super(newSocketDomain(), false); super(newSocketDomain(), false);
} }
/** EpollDomainSocketChannel(Channel parent, FileDescriptor fd) {
* @deprecated Use {@link #EpollDomainSocketChannel(Channel, Socket)}. super(parent, new LinuxSocket(fd.intValue()));
*/
@Deprecated
public EpollDomainSocketChannel(Channel parent, FileDescriptor fd) {
super(parent, new Socket(fd.intValue()));
} }
/** public EpollDomainSocketChannel(int fd) {
* @deprecated Use {@link #EpollDomainSocketChannel(Socket, boolean)}.
* <p>
* Creates a new {@link EpollDomainSocketChannel} from an existing {@link FileDescriptor}
*/
@Deprecated
public EpollDomainSocketChannel(FileDescriptor fd) {
super(fd); super(fd);
} }
public EpollDomainSocketChannel(Channel parent, Socket fd) { public EpollDomainSocketChannel(Channel parent, LinuxSocket fd) {
super(parent, fd); super(parent, fd);
} }
/** public EpollDomainSocketChannel(int fd, boolean active) {
* Creates a new {@link EpollDomainSocketChannel} from an existing {@link FileDescriptor} super(new LinuxSocket(fd), active);
*/
public EpollDomainSocketChannel(Socket fd, boolean active) {
super(fd, active);
} }
@Override @Override
@ -86,7 +73,7 @@ public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel i
@Override @Override
protected void doBind(SocketAddress localAddress) throws Exception { protected void doBind(SocketAddress localAddress) throws Exception {
fd().bind(localAddress); socket.bind(localAddress);
local = (DomainSocketAddress) localAddress; local = (DomainSocketAddress) localAddress;
} }
@ -118,7 +105,7 @@ public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel i
@Override @Override
protected boolean doWriteSingle(ChannelOutboundBuffer in, int writeSpinCount) throws Exception { protected boolean doWriteSingle(ChannelOutboundBuffer in, int writeSpinCount) throws Exception {
Object msg = in.current(); 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. // File descriptor was written, so remove it.
in.remove(); in.remove();
return true; return true;
@ -138,8 +125,9 @@ public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel i
* Returns the unix credentials (uid, gid, pid) of the peer * 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> * <a href=http://man7.org/linux/man-pages/man7/socket.7.html>SO_PEERCRED</a>
*/ */
@UnstableApi
public PeerCredentials peerCredentials() throws IOException { public PeerCredentials peerCredentials() throws IOException {
return fd().getPeerCredentials(); return socket.getPeerCredentials();
} }
private final class EpollDomainUnsafe extends EpollStreamUnsafe { private final class EpollDomainUnsafe extends EpollStreamUnsafe {
@ -158,7 +146,7 @@ public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel i
} }
private void epollInReadFd() { private void epollInReadFd() {
if (fd().isInputShutdown()) { if (socket.isInputShutdown()) {
clearEpollIn0(); clearEpollIn0();
return; 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 // 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 // EpollRecvByteAllocatorHandle knows if it should try to read again or not when autoRead is
// enabled. // enabled.
allocHandle.lastBytesRead(Native.recvFd(fd().intValue())); allocHandle.lastBytesRead(socket.recvFd());
switch(allocHandle.lastBytesRead()) { switch(allocHandle.lastBytesRead()) {
case 0: case 0:
break readLoop; break readLoop;

View File

@ -21,6 +21,7 @@ import io.netty.channel.SelectStrategy;
import io.netty.channel.SingleThreadEventLoop; import io.netty.channel.SingleThreadEventLoop;
import io.netty.channel.epoll.AbstractEpollChannel.AbstractEpollUnsafe; import io.netty.channel.epoll.AbstractEpollChannel.AbstractEpollUnsafe;
import io.netty.channel.unix.FileDescriptor; import io.netty.channel.unix.FileDescriptor;
import io.netty.channel.unix.IovArray;
import io.netty.util.IntSupplier; import io.netty.util.IntSupplier;
import io.netty.util.collection.IntObjectHashMap; import io.netty.util.collection.IntObjectHashMap;
import io.netty.util.collection.IntObjectMap; import io.netty.util.collection.IntObjectMap;
@ -46,6 +47,12 @@ final class EpollEventLoop extends SingleThreadEventLoop {
private static final AtomicIntegerFieldUpdater<EpollEventLoop> WAKEN_UP_UPDATER = private static final AtomicIntegerFieldUpdater<EpollEventLoop> WAKEN_UP_UPDATER =
AtomicIntegerFieldUpdater.newUpdater(EpollEventLoop.class, "wakenUp"); 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 epollFd;
private final FileDescriptor eventFd; private final FileDescriptor eventFd;
private final IntObjectMap<AbstractEpollChannel> channels = new IntObjectHashMap<AbstractEpollChannel>(4096); private final IntObjectMap<AbstractEpollChannel> channels = new IntObjectHashMap<AbstractEpollChannel>(4096);
@ -132,7 +139,7 @@ final class EpollEventLoop extends SingleThreadEventLoop {
*/ */
void add(AbstractEpollChannel ch) throws IOException { void add(AbstractEpollChannel ch) throws IOException {
assert inEventLoop(); assert inEventLoop();
int fd = ch.fd().intValue(); int fd = ch.socket.intValue();
Native.epollCtlAdd(epollFd.intValue(), fd, ch.flags); Native.epollCtlAdd(epollFd.intValue(), fd, ch.flags);
channels.put(fd, ch); channels.put(fd, ch);
} }
@ -142,7 +149,7 @@ final class EpollEventLoop extends SingleThreadEventLoop {
*/ */
void modify(AbstractEpollChannel ch) throws IOException { void modify(AbstractEpollChannel ch) throws IOException {
assert inEventLoop(); 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(); assert inEventLoop();
if (ch.isOpen()) { if (ch.isOpen()) {
int fd = ch.fd().intValue(); int fd = ch.socket.intValue();
if (channels.remove(fd) != null) { if (channels.remove(fd) != null) {
// Remove the epoll. This is only needed if it's still open as otherwise it will be automatically // 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. // removed once the file-descriptor is closed.

View File

@ -33,6 +33,10 @@ import java.util.concurrent.ThreadFactory;
* it only works on linux. * it only works on linux.
*/ */
public final class EpollEventLoopGroup extends MultithreadEventLoopGroup { 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}. * Create a new instance using the default number of threads and the default {@link ThreadFactory}.

View File

@ -24,14 +24,14 @@ import io.netty.util.internal.ObjectUtil;
class EpollRecvByteAllocatorHandle implements RecvByteBufAllocator.ExtendedHandle { class EpollRecvByteAllocatorHandle implements RecvByteBufAllocator.ExtendedHandle {
private final RecvByteBufAllocator.ExtendedHandle delegate; private final RecvByteBufAllocator.ExtendedHandle delegate;
private boolean isEdgeTriggered;
private boolean receivedRdHup;
private final UncheckedBooleanSupplier defaultMaybeMoreDataSupplier = new UncheckedBooleanSupplier() { private final UncheckedBooleanSupplier defaultMaybeMoreDataSupplier = new UncheckedBooleanSupplier() {
@Override @Override
public boolean get() { public boolean get() {
return maybeMoreDataToRead(); return maybeMoreDataToRead();
} }
}; };
private boolean isEdgeTriggered;
private boolean receivedRdHup;
EpollRecvByteAllocatorHandle(RecvByteBufAllocator.ExtendedHandle handle) { EpollRecvByteAllocatorHandle(RecvByteBufAllocator.ExtendedHandle handle) {
this.delegate = ObjectUtil.checkNotNull(handle, "handle"); this.delegate = ObjectUtil.checkNotNull(handle, "handle");

View File

@ -21,6 +21,7 @@ import io.netty.channel.ChannelOption;
import io.netty.channel.MessageSizeEstimator; import io.netty.channel.MessageSizeEstimator;
import io.netty.channel.RecvByteBufAllocator; import io.netty.channel.RecvByteBufAllocator;
import io.netty.channel.WriteBufferWaterMark; import io.netty.channel.WriteBufferWaterMark;
import io.netty.channel.socket.ServerSocketChannelConfig;
import io.netty.util.NetUtil; import io.netty.util.NetUtil;
import java.io.IOException; 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_RCVBUF;
import static io.netty.channel.ChannelOption.SO_REUSEADDR; import static io.netty.channel.ChannelOption.SO_REUSEADDR;
public class EpollServerChannelConfig extends EpollChannelConfig { public class EpollServerChannelConfig extends EpollChannelConfig implements ServerSocketChannelConfig {
protected final AbstractEpollChannel channel; protected final AbstractEpollChannel channel;
private volatile int backlog = NetUtil.SOMAXCONN; private volatile int backlog = NetUtil.SOMAXCONN;
private volatile int pendingFastOpenRequestsThreshold; private volatile int pendingFastOpenRequestsThreshold;
@ -84,7 +85,7 @@ public class EpollServerChannelConfig extends EpollChannelConfig {
public boolean isReuseAddress() { public boolean isReuseAddress() {
try { try {
return Native.isReuseAddress(channel.fd().intValue()) == 1; return channel.socket.isReuseAddress();
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
} }
@ -92,7 +93,7 @@ public class EpollServerChannelConfig extends EpollChannelConfig {
public EpollServerChannelConfig setReuseAddress(boolean reuseAddress) { public EpollServerChannelConfig setReuseAddress(boolean reuseAddress) {
try { try {
Native.setReuseAddress(channel.fd().intValue(), reuseAddress ? 1 : 0); channel.socket.setReuseAddress(reuseAddress);
return this; return this;
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
@ -101,7 +102,7 @@ public class EpollServerChannelConfig extends EpollChannelConfig {
public int getReceiveBufferSize() { public int getReceiveBufferSize() {
try { try {
return channel.fd().getReceiveBufferSize(); return channel.socket.getReceiveBufferSize();
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
} }
@ -109,7 +110,7 @@ public class EpollServerChannelConfig extends EpollChannelConfig {
public EpollServerChannelConfig setReceiveBufferSize(int receiveBufferSize) { public EpollServerChannelConfig setReceiveBufferSize(int receiveBufferSize) {
try { try {
channel.fd().setReceiveBufferSize(receiveBufferSize); channel.socket.setReceiveBufferSize(receiveBufferSize);
return this; return this;
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
@ -154,6 +155,11 @@ public class EpollServerChannelConfig extends EpollChannelConfig {
return this; return this;
} }
@Override
public EpollServerChannelConfig setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
return this;
}
@Override @Override
public EpollServerChannelConfig setConnectTimeoutMillis(int connectTimeoutMillis) { public EpollServerChannelConfig setConnectTimeoutMillis(int connectTimeoutMillis) {
super.setConnectTimeoutMillis(connectTimeoutMillis); super.setConnectTimeoutMillis(connectTimeoutMillis);

View File

@ -17,7 +17,6 @@ package io.netty.channel.epoll;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.unix.DomainSocketAddress; import io.netty.channel.unix.DomainSocketAddress;
import io.netty.channel.unix.FileDescriptor;
import io.netty.channel.unix.ServerDomainSocketChannel; import io.netty.channel.unix.ServerDomainSocketChannel;
import io.netty.channel.unix.Socket; import io.netty.channel.unix.Socket;
import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLogger;
@ -26,8 +25,7 @@ import io.netty.util.internal.logging.InternalLoggerFactory;
import java.io.File; import java.io.File;
import java.net.SocketAddress; 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 public final class EpollServerDomainSocketChannel extends AbstractEpollServerChannel
implements ServerDomainSocketChannel { implements ServerDomainSocketChannel {
@ -41,22 +39,15 @@ public final class EpollServerDomainSocketChannel extends AbstractEpollServerCha
super(newSocketDomain(), false); super(newSocketDomain(), false);
} }
/** public EpollServerDomainSocketChannel(int fd) {
* @deprecated Use {@link #EpollServerDomainSocketChannel(Socket, boolean)}.
* Creates a new {@link EpollServerDomainSocketChannel} from an existing {@link FileDescriptor}.
*/
public EpollServerDomainSocketChannel(FileDescriptor fd) {
super(fd); super(fd);
} }
/** EpollServerDomainSocketChannel(LinuxSocket fd) {
* @deprecated Use {@link #EpollServerDomainSocketChannel(Socket, boolean)}.
*/
public EpollServerDomainSocketChannel(Socket fd) {
super(fd); super(fd);
} }
public EpollServerDomainSocketChannel(Socket fd, boolean active) { EpollServerDomainSocketChannel(LinuxSocket fd, boolean active) {
super(fd, active); super(fd, active);
} }
@ -72,8 +63,8 @@ public final class EpollServerDomainSocketChannel extends AbstractEpollServerCha
@Override @Override
protected void doBind(SocketAddress localAddress) throws Exception { protected void doBind(SocketAddress localAddress) throws Exception {
fd().bind(localAddress); socket.bind(localAddress);
fd().listen(config.getBacklog()); socket.listen(config.getBacklog());
local = (DomainSocketAddress) localAddress; local = (DomainSocketAddress) localAddress;
active = true; active = true;
} }

View File

@ -18,8 +18,6 @@ package io.netty.channel.epoll;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.EventLoop; import io.netty.channel.EventLoop;
import io.netty.channel.socket.ServerSocketChannel; import io.netty.channel.socket.ServerSocketChannel;
import io.netty.channel.unix.FileDescriptor;
import io.netty.channel.unix.Socket;
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
@ -29,8 +27,8 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Map; 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.NativeInetAddress.address;
import static io.netty.channel.unix.Socket.newSocketStream;
/** /**
* {@link ServerSocketChannel} implementation that uses linux EPOLL Edge-Triggered Mode for * {@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); config = new EpollServerSocketChannelConfig(this);
} }
/** public EpollServerSocketChannel(int fd) {
* @deprecated Use {@link #EpollServerSocketChannel(Socket, boolean)}.
* Creates a new {@link EpollServerSocketChannel} from an existing {@link FileDescriptor}.
*/
@Deprecated
public EpollServerSocketChannel(FileDescriptor fd) {
// Must call this constructor to ensure this object's local address is configured correctly. // 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. // The local address can only be obtained from a Socket object.
this(new Socket(fd.intValue())); this(new LinuxSocket(fd));
} }
/** EpollServerSocketChannel(LinuxSocket fd) {
* @deprecated Use {@link #EpollServerSocketChannel(Socket, boolean)}.
* Creates a new {@link EpollServerSocketChannel} from an existing {@link Socket}.
*/
@Deprecated
public EpollServerSocketChannel(Socket fd) {
super(fd); super(fd);
// As we create an EpollServerSocketChannel from a FileDescriptor we should try to obtain the remote and local // 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. // 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); config = new EpollServerSocketChannelConfig(this);
} }
public EpollServerSocketChannel(Socket fd, boolean active) { EpollServerSocketChannel(LinuxSocket fd, boolean active) {
super(fd, active); super(fd, active);
// As we create an EpollServerSocketChannel from a FileDescriptor we should try to obtain the remote and local // 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. // 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 { protected void doBind(SocketAddress localAddress) throws Exception {
InetSocketAddress addr = (InetSocketAddress) localAddress; InetSocketAddress addr = (InetSocketAddress) localAddress;
checkResolvable(addr); checkResolvable(addr);
fd().bind(addr); socket.bind(addr);
local = fd().localAddress(); local = socket.localAddress();
if (Native.IS_SUPPORTING_TCP_FASTOPEN && config.getTcpFastopen() > 0) { 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; active = true;
} }
@ -119,7 +107,7 @@ public final class EpollServerSocketChannel extends AbstractEpollServerChannel i
@Override @Override
protected Channel newChildChannel(int fd, byte[] address, int offset, int len) throws Exception { 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() { Collection<InetAddress> tcpMd5SigAddresses() {

View File

@ -186,7 +186,7 @@ public final class EpollServerSocketChannelConfig extends EpollServerChannelConf
*/ */
public boolean isReusePort() { public boolean isReusePort() {
try { try {
return Native.isReusePort(channel.fd().intValue()) == 1; return channel.socket.isReusePort();
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
} }
@ -201,7 +201,7 @@ public final class EpollServerSocketChannelConfig extends EpollServerChannelConf
*/ */
public EpollServerSocketChannelConfig setReusePort(boolean reusePort) { public EpollServerSocketChannelConfig setReusePort(boolean reusePort) {
try { try {
Native.setReusePort(channel.fd().intValue(), reusePort ? 1 : 0); channel.socket.setReusePort(reusePort);
return this; return this;
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
@ -214,7 +214,7 @@ public final class EpollServerSocketChannelConfig extends EpollServerChannelConf
*/ */
public boolean isFreeBind() { public boolean isFreeBind() {
try { try {
return Native.isIpFreeBind(channel.fd().intValue()) != 0; return channel.socket.isIpFreeBind();
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
} }
@ -226,7 +226,7 @@ public final class EpollServerSocketChannelConfig extends EpollServerChannelConf
*/ */
public EpollServerSocketChannelConfig setFreeBind(boolean freeBind) { public EpollServerSocketChannelConfig setFreeBind(boolean freeBind) {
try { try {
Native.setIpFreeBind(channel.fd().intValue(), freeBind ? 1 : 0); channel.socket.setIpFreeBind(freeBind);
return this; return this;
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
@ -238,7 +238,7 @@ public final class EpollServerSocketChannelConfig extends EpollServerChannelConf
*/ */
public EpollServerSocketChannelConfig setTcpDeferAccept(int deferAccept) { public EpollServerSocketChannelConfig setTcpDeferAccept(int deferAccept) {
try { try {
channel.fd().setTcpDeferAccept(deferAccept); channel.socket.setTcpDeferAccept(deferAccept);
return this; return this;
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
@ -250,7 +250,7 @@ public final class EpollServerSocketChannelConfig extends EpollServerChannelConf
*/ */
public int getTcpDeferAccept() { public int getTcpDeferAccept() {
try { try {
return channel.fd().getTcpDeferAccept(); return channel.socket.getTcpDeferAccept();
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
} }

View File

@ -19,8 +19,6 @@ import io.netty.channel.Channel;
import io.netty.channel.ChannelException; import io.netty.channel.ChannelException;
import io.netty.channel.socket.ServerSocketChannel; import io.netty.channel.socket.ServerSocketChannel;
import io.netty.channel.socket.SocketChannel; 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.concurrent.GlobalEventExecutor;
import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.PlatformDependent;
@ -35,7 +33,7 @@ import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Executor; 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 * {@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(); private volatile Collection<InetAddress> tcpMd5SigAddresses = Collections.emptyList();
EpollSocketChannel(Channel parent, Socket fd, InetSocketAddress remote) { EpollSocketChannel(Channel parent, LinuxSocket fd, InetSocketAddress remote) {
super(parent, fd); super(parent, fd);
config = new EpollSocketChannelConfig(this); config = new EpollSocketChannelConfig(this);
// Directly cache the remote and local addresses // Directly cache the remote and local addresses
@ -69,23 +67,16 @@ public final class EpollSocketChannel extends AbstractEpollStreamChannel impleme
config = new EpollSocketChannelConfig(this); config = new EpollSocketChannelConfig(this);
} }
/** public EpollSocketChannel(int fd) {
* @deprecated Use {@link #EpollSocketChannel(Socket, boolean)}.
*/
@Deprecated
public EpollSocketChannel(FileDescriptor fd) {
super(fd); super(fd);
// As we create an EpollSocketChannel from a FileDescriptor we should try to obtain the remote and local // 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. // address from it. This is needed as the FileDescriptor may be bound/connected already.
remote = fd().remoteAddress(); remote = socket.remoteAddress();
local = fd().localAddress(); local = socket.localAddress();
config = new EpollSocketChannelConfig(this); config = new EpollSocketChannelConfig(this);
} }
/** EpollSocketChannel(LinuxSocket fd, boolean active) {
* Creates a new {@link EpollSocketChannel} from an existing {@link FileDescriptor}.
*/
public EpollSocketChannel(Socket fd, boolean active) {
super(fd, active); super(fd, active);
// As we create an EpollSocketChannel from a FileDescriptor we should try to obtain the remote and local // 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. // 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) { public EpollTcpInfo tcpInfo(EpollTcpInfo info) {
try { try {
Native.tcpInfo(fd().intValue(), info); socket.getTcpInfo(info);
return info; return info;
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
@ -137,8 +128,8 @@ public final class EpollSocketChannel extends AbstractEpollStreamChannel impleme
@Override @Override
protected void doBind(SocketAddress local) throws Exception { protected void doBind(SocketAddress local) throws Exception {
InetSocketAddress localAddress = (InetSocketAddress) local; InetSocketAddress localAddress = (InetSocketAddress) local;
fd().bind(localAddress); socket.bind(localAddress);
this.local = fd().localAddress(); this.local = socket.localAddress();
} }
@Override @Override
@ -192,7 +183,7 @@ public final class EpollSocketChannel extends AbstractEpollStreamChannel impleme
boolean connected = super.doConnect(remoteAddress, localAddress); boolean connected = super.doConnect(remoteAddress, localAddress);
if (connected) { if (connected) {
remote = computeRemoteAddr(remoteAddr, fd().remoteAddress()); remote = computeRemoteAddr(remoteAddr, socket.remoteAddress());
} else { } else {
// Store for later usage in doFinishConnect() // Store for later usage in doFinishConnect()
requestedRemote = remoteAddr; 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. // 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 // See https://github.com/netty/netty/issues/3463
local = fd().localAddress(); local = socket.localAddress();
return connected; return connected;
} }
@ -229,7 +220,7 @@ public final class EpollSocketChannel extends AbstractEpollStreamChannel impleme
@Override @Override
boolean doFinishConnect() throws Exception { boolean doFinishConnect() throws Exception {
if (super.doFinishConnect()) { if (super.doFinishConnect()) {
remote = computeRemoteAddr(requestedRemote, fd().remoteAddress()); remote = computeRemoteAddr(requestedRemote, socket.remoteAddress());
requestedRemote = null; requestedRemote = null;
return true; return true;
} }

View File

@ -28,10 +28,16 @@ import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.util.Map; 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 { public final class EpollSocketChannelConfig extends EpollChannelConfig implements SocketChannelConfig {
private static final long MAX_UINT32_T = 0xFFFFFFFFL;
private final EpollSocketChannel channel; private final EpollSocketChannel channel;
private volatile boolean allowHalfClosure; private volatile boolean allowHalfClosure;
@ -156,7 +162,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
@Override @Override
public int getReceiveBufferSize() { public int getReceiveBufferSize() {
try { try {
return channel.fd().getReceiveBufferSize(); return channel.socket.getReceiveBufferSize();
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
} }
@ -165,7 +171,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
@Override @Override
public int getSendBufferSize() { public int getSendBufferSize() {
try { try {
return channel.fd().getSendBufferSize(); return channel.socket.getSendBufferSize();
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
} }
@ -174,7 +180,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
@Override @Override
public int getSoLinger() { public int getSoLinger() {
try { try {
return channel.fd().getSoLinger(); return channel.socket.getSoLinger();
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
} }
@ -183,7 +189,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
@Override @Override
public int getTrafficClass() { public int getTrafficClass() {
try { try {
return Native.getTrafficClass(channel.fd().intValue()); return channel.socket.getTrafficClass();
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
} }
@ -192,7 +198,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
@Override @Override
public boolean isKeepAlive() { public boolean isKeepAlive() {
try { try {
return channel.fd().isKeepAlive(); return channel.socket.isKeepAlive();
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
} }
@ -201,7 +207,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
@Override @Override
public boolean isReuseAddress() { public boolean isReuseAddress() {
try { try {
return Native.isReuseAddress(channel.fd().intValue()) == 1; return channel.socket.isReuseAddress();
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
} }
@ -210,7 +216,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
@Override @Override
public boolean isTcpNoDelay() { public boolean isTcpNoDelay() {
try { try {
return channel.fd().isTcpNoDelay(); return channel.socket.isTcpNoDelay();
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
} }
@ -221,7 +227,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
*/ */
public boolean isTcpCork() { public boolean isTcpCork() {
try { try {
return channel.fd().isTcpCork(); return channel.socket.isTcpCork();
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
} }
@ -233,7 +239,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
*/ */
public long getTcpNotSentLowAt() { public long getTcpNotSentLowAt() {
try { try {
return Native.getTcpNotSentLowAt(channel.fd().intValue()) & MAX_UINT32_T; return channel.socket.getTcpNotSentLowAt();
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
} }
@ -244,7 +250,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
*/ */
public int getTcpKeepIdle() { public int getTcpKeepIdle() {
try { try {
return Native.getTcpKeepIdle(channel.fd().intValue()); return channel.socket.getTcpKeepIdle();
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
} }
@ -255,7 +261,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
*/ */
public int getTcpKeepIntvl() { public int getTcpKeepIntvl() {
try { try {
return Native.getTcpKeepIntvl(channel.fd().intValue()); return channel.socket.getTcpKeepIntvl();
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
} }
@ -266,7 +272,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
*/ */
public int getTcpKeepCnt() { public int getTcpKeepCnt() {
try { try {
return Native.getTcpKeepCnt(channel.fd().intValue()); return channel.socket.getTcpKeepCnt();
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
} }
@ -277,7 +283,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
*/ */
public int getTcpUserTimeout() { public int getTcpUserTimeout() {
try { try {
return Native.getTcpUserTimeout(channel.fd().intValue()); return channel.socket.getTcpUserTimeout();
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
} }
@ -286,7 +292,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
@Override @Override
public EpollSocketChannelConfig setKeepAlive(boolean keepAlive) { public EpollSocketChannelConfig setKeepAlive(boolean keepAlive) {
try { try {
channel.fd().setKeepAlive(keepAlive); channel.socket.setKeepAlive(keepAlive);
return this; return this;
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
@ -302,7 +308,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
@Override @Override
public EpollSocketChannelConfig setReceiveBufferSize(int receiveBufferSize) { public EpollSocketChannelConfig setReceiveBufferSize(int receiveBufferSize) {
try { try {
channel.fd().setReceiveBufferSize(receiveBufferSize); channel.socket.setReceiveBufferSize(receiveBufferSize);
return this; return this;
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
@ -312,7 +318,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
@Override @Override
public EpollSocketChannelConfig setReuseAddress(boolean reuseAddress) { public EpollSocketChannelConfig setReuseAddress(boolean reuseAddress) {
try { try {
Native.setReuseAddress(channel.fd().intValue(), reuseAddress ? 1 : 0); channel.socket.setReuseAddress(reuseAddress);
return this; return this;
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
@ -322,7 +328,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
@Override @Override
public EpollSocketChannelConfig setSendBufferSize(int sendBufferSize) { public EpollSocketChannelConfig setSendBufferSize(int sendBufferSize) {
try { try {
channel.fd().setSendBufferSize(sendBufferSize); channel.socket.setSendBufferSize(sendBufferSize);
return this; return this;
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
@ -332,7 +338,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
@Override @Override
public EpollSocketChannelConfig setSoLinger(int soLinger) { public EpollSocketChannelConfig setSoLinger(int soLinger) {
try { try {
channel.fd().setSoLinger(soLinger); channel.socket.setSoLinger(soLinger);
return this; return this;
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
@ -342,7 +348,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
@Override @Override
public EpollSocketChannelConfig setTcpNoDelay(boolean tcpNoDelay) { public EpollSocketChannelConfig setTcpNoDelay(boolean tcpNoDelay) {
try { try {
channel.fd().setTcpNoDelay(tcpNoDelay); channel.socket.setTcpNoDelay(tcpNoDelay);
return this; return this;
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
@ -354,7 +360,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
*/ */
public EpollSocketChannelConfig setTcpCork(boolean tcpCork) { public EpollSocketChannelConfig setTcpCork(boolean tcpCork) {
try { try {
channel.fd().setTcpCork(tcpCork); channel.socket.setTcpCork(tcpCork);
return this; return this;
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
@ -366,11 +372,8 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
* @param tcpNotSentLowAt is a uint32_t * @param tcpNotSentLowAt is a uint32_t
*/ */
public EpollSocketChannelConfig setTcpNotSentLowAt(long tcpNotSentLowAt) { public EpollSocketChannelConfig setTcpNotSentLowAt(long tcpNotSentLowAt) {
if (tcpNotSentLowAt < 0 || tcpNotSentLowAt > MAX_UINT32_T) {
throw new IllegalArgumentException("tcpNotSentLowAt must be a uint32_t");
}
try { try {
Native.setTcpNotSentLowAt(channel.fd().intValue(), (int) tcpNotSentLowAt); channel.socket.setTcpNotSentLowAt(tcpNotSentLowAt);
return this; return this;
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
@ -380,7 +383,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
@Override @Override
public EpollSocketChannelConfig setTrafficClass(int trafficClass) { public EpollSocketChannelConfig setTrafficClass(int trafficClass) {
try { try {
Native.setTrafficClass(channel.fd().intValue(), trafficClass); channel.socket.setTrafficClass(trafficClass);
return this; return this;
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
@ -392,7 +395,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
*/ */
public EpollSocketChannelConfig setTcpKeepIdle(int seconds) { public EpollSocketChannelConfig setTcpKeepIdle(int seconds) {
try { try {
Native.setTcpKeepIdle(channel.fd().intValue(), seconds); channel.socket.setTcpKeepIdle(seconds);
return this; return this;
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
@ -404,7 +407,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
*/ */
public EpollSocketChannelConfig setTcpKeepIntvl(int seconds) { public EpollSocketChannelConfig setTcpKeepIntvl(int seconds) {
try { try {
Native.setTcpKeepIntvl(channel.fd().intValue(), seconds); channel.socket.setTcpKeepIntvl(seconds);
return this; return this;
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
@ -416,7 +419,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
*/ */
public EpollSocketChannelConfig setTcpKeepCntl(int probes) { public EpollSocketChannelConfig setTcpKeepCntl(int probes) {
try { try {
Native.setTcpKeepCnt(channel.fd().intValue(), probes); channel.socket.setTcpKeepCnt(probes);
return this; return this;
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
@ -428,7 +431,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
*/ */
public EpollSocketChannelConfig setTcpUserTimeout(int milliseconds) { public EpollSocketChannelConfig setTcpUserTimeout(int milliseconds) {
try { try {
Native.setTcpUserTimeout(channel.fd().intValue(), milliseconds); channel.socket.setTcpUserTimeout(milliseconds);
return this; return this;
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
@ -455,7 +458,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
*/ */
public EpollSocketChannelConfig setTcpQuickAck(boolean quickAck) { public EpollSocketChannelConfig setTcpQuickAck(boolean quickAck) {
try { try {
channel.fd().setTcpQuickAck(quickAck); channel.socket.setTcpQuickAck(quickAck);
return this; return this;
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
@ -468,7 +471,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
*/ */
public boolean isTcpQuickAck() { public boolean isTcpQuickAck() {
try { try {
return channel.fd().isTcpQuickAck(); return channel.socket.isTcpQuickAck();
} catch (IOException e) { } catch (IOException e) {
throw new ChannelException(e); throw new ChannelException(e);
} }

View File

@ -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;
}

View File

@ -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.epollin;
import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollout; import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollout;
import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollrdhup; 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.isSupportingSendmmsg;
import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.isSupportingTcpFastopen; import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.isSupportingTcpFastopen;
import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.kernelVersion; 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.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_EAGAIN_NEGATIVE;
import static io.netty.channel.unix.Errors.ERRNO_EPIPE_NEGATIVE; import static io.netty.channel.unix.Errors.ERRNO_EPIPE_NEGATIVE;
import static io.netty.channel.unix.Errors.ERRNO_EWOULDBLOCK_NEGATIVE; import static io.netty.channel.unix.Errors.ERRNO_EWOULDBLOCK_NEGATIVE;
@ -72,11 +69,8 @@ public final class Native {
public static final int EPOLLET = epollet(); public static final int EPOLLET = epollet();
public static final int EPOLLERR = epollerr(); 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_SENDMMSG = isSupportingSendmmsg();
public static final boolean IS_SUPPORTING_TCP_FASTOPEN = isSupportingTcpFastopen(); 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 int TCP_MD5SIG_MAXKEYLEN = tcpMd5SigMaxKeyLen();
public static final String KERNEL_VERSION = kernelVersion(); public static final String KERNEL_VERSION = kernelVersion();
@ -185,82 +179,10 @@ public final class Native {
private static native int sendmmsg0( private static native int sendmmsg0(
int fd, NativeDatagramPacketArray.NativeDatagramPacket[] msgs, int offset, int len); 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 // epoll_event related
public static native int sizeofEpollEvent(); public static native int sizeofEpollEvent();
public static native int offsetofEpollData(); public static native int offsetofEpollData();
private Native() {
// utility
}
private static void loadNativeLibrary() { private static void loadNativeLibrary() {
String name = SystemPropertyUtil.get("os.name").toLowerCase(Locale.UK).trim(); String name = SystemPropertyUtil.get("os.name").toLowerCase(Locale.UK).trim();
if (!name.startsWith("linux")) { if (!name.startsWith("linux")) {
@ -269,4 +191,8 @@ public final class Native {
NativeLibraryLoader.load(SystemPropertyUtil.get("io.netty.packagePrefix", "").replace('.', '-') + NativeLibraryLoader.load(SystemPropertyUtil.get("io.netty.packagePrefix", "").replace('.', '-') +
"netty-transport-native-epoll", PlatformDependent.getClassLoader(Native.class)); "netty-transport-native-epoll", PlatformDependent.getClassLoader(Native.class));
} }
private Native() {
// utility
}
} }

View File

@ -15,16 +15,18 @@
*/ */
package io.netty.channel.epoll; package io.netty.channel.epoll;
import static io.netty.channel.unix.NativeInetAddress.ipv4MappedIpv6Address;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelOutboundBuffer; import io.netty.channel.ChannelOutboundBuffer;
import io.netty.channel.socket.DatagramPacket; import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.unix.IovArray;
import io.netty.util.concurrent.FastThreadLocal; import io.netty.util.concurrent.FastThreadLocal;
import java.net.Inet6Address; import java.net.Inet6Address;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; 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+ * 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. // 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 int count;
private NativeDatagramPacketArray() { private NativeDatagramPacketArray() {

View File

@ -55,7 +55,7 @@ final class TcpMd5Util {
// Remove mappings not present in the new set. // Remove mappings not present in the new set.
for (InetAddress addr : current) { for (InetAddress addr : current) {
if (!newKeys.containsKey(addr)) { 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. // Set new mappings and store addresses which we set.
final Collection<InetAddress> addresses = new ArrayList<InetAddress>(newKeys.size()); final Collection<InetAddress> addresses = new ArrayList<InetAddress>(newKeys.size());
for (Entry<InetAddress, byte[]> e : newKeys.entrySet()) { 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()); addresses.add(e.getKey());
} }

View File

@ -23,6 +23,6 @@ public class EpollAbstractDomainSocketEchoTest extends EpollDomainSocketEchoTest
@Override @Override
protected SocketAddress newSocketAddress() { protected SocketAddress newSocketAddress() {
// these don't actually show up in the file system so creating a temp file isn't reliable // 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());
} }
} }

View File

@ -26,7 +26,9 @@ import java.net.InetSocketAddress;
import java.nio.channels.ClosedChannelException; import java.nio.channels.ClosedChannelException;
import java.util.Random; import java.util.Random;
import org.junit.After;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
@ -37,9 +39,18 @@ public class EpollSocketChannelConfigTest {
private static Random rand; private static Random rand;
@BeforeClass @BeforeClass
public static void before() { public static void beforeClass() {
rand = new Random(); rand = new Random();
group = new EpollEventLoopGroup(1); group = new EpollEventLoopGroup(1);
}
@AfterClass
public static void afterClass() {
group.shutdownGracefully();
}
@Before
public void setup() {
Bootstrap bootstrap = new Bootstrap(); Bootstrap bootstrap = new Bootstrap();
ch = (EpollSocketChannel) bootstrap.group(group) ch = (EpollSocketChannel) bootstrap.group(group)
.channel(EpollSocketChannel.class) .channel(EpollSocketChannel.class)
@ -47,13 +58,9 @@ public class EpollSocketChannelConfigTest {
.bind(new InetSocketAddress(0)).syncUninterruptibly().channel(); .bind(new InetSocketAddress(0)).syncUninterruptibly().channel();
} }
@AfterClass @After
public static void after() { public void teardown() {
try { ch.close().syncUninterruptibly();
ch.close().syncUninterruptibly();
} finally {
group.shutdownGracefully();
}
} }
private long randLong(long min, long max) { private long randLong(long min, long max) {

View File

@ -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();
}
}

View File

@ -25,6 +25,7 @@ import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.channel.unix.DomainSocketAddress; 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;
import io.netty.testsuite.transport.TestsuitePermutation.BootstrapFactory; import io.netty.testsuite.transport.TestsuitePermutation.BootstrapFactory;
import io.netty.testsuite.transport.socket.SocketTestPermutation; import io.netty.testsuite.transport.socket.SocketTestPermutation;
@ -214,12 +215,6 @@ class EpollSocketTestPermutation extends SocketTestPermutation {
} }
public static DomainSocketAddress newSocketAddress() { public static DomainSocketAddress newSocketAddress() {
try { return UnixTestUtils.newSocketAddress();
File file = File.createTempFile("netty", "dsocket");
file.delete();
return new DomainSocketAddress(file);
} catch (IOException e) {
throw new IllegalStateException(e);
}
} }
} }

View 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>

View 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;
}
}

View 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_ */

View 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) {
}

View 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_ */

View 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);
}

View File

@ -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());
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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();
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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() { }
}

View File

@ -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();
}
}

View File

@ -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() {
}
}

View File

@ -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);
}
}
}
}

View File

@ -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");
}
}

View File

@ -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);
}
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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.
}
}
}

View File

@ -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]);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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));
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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();
}

View File

@ -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
}
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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());
}
}

View File

@ -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
}
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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());
}
}

View File

@ -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);
}
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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