Permalink
Browse files

Introduce a native transport for linux using epoll ET

This transport use JNI (C) to directly make use of epoll in Edge-Triggered mode for maximal performance on Linux. Beside this it also support using TCP_CORK and produce less GC then the NIO transport using JDK NIO.
It only builds on linux and skip the build if linux is not used. The transport produce a jar which contains all needed .so files for 32bit and 64 bit. The user only need to include the jar as dependency as usually
to make use of it and use the correct classes.

This includes also some cleanup of @trustin
  • Loading branch information...
1 parent c73e1e3 commit e0299e12228ef5d6791e0e8e722a5788ff7f617e @normanmaurer normanmaurer committed Feb 15, 2014
Showing with 3,643 additions and 1 deletion.
  1. +149 −0 common/src/main/java/io/netty/util/internal/NativeLibraryLoader.java
  2. +143 −0 common/src/main/java/io/netty/util/internal/PlatformDependent.java
  3. +16 −1 pom.xml
  4. +3 −0 transport-native-epoll/README.md
  5. +113 −0 transport-native-epoll/pom.xml
  6. +859 −0 transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.c
  7. +60 −0 transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.h
  8. +163 −0 transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollChannel.java
  9. +27 −0 transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollChannelOption.java
  10. +356 −0 transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java
  11. +75 −0 transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoopGroup.java
  12. +133 −0 transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerSocketChannel.java
  13. +176 −0 transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerSocketChannelConfig.java
  14. +590 −0 transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollSocketChannel.java
  15. +274 −0 transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollSocketChannelConfig.java
  16. +136 −0 transport-native-epoll/src/main/java/io/netty/channel/epoll/Native.java
  17. +21 −0 transport-native-epoll/src/main/java/io/netty/channel/epoll/package-info.java
  18. +47 −0 transport-native-epoll/src/main/native-package/m4/custom.m4
  19. +31 −0 transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketEchoTest.java
  20. +31 −0 transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketFileRegionTest.java
  21. +31 −0 transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketFixedLengthEchoTest.java
  22. +31 −0 transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketGatheringWriteTest.java
  23. +31 −0 transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketObjectEchoTest.java
  24. +39 −0 transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketSslEchoTest.java
  25. +31 −0 transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketStartTlsTest.java
  26. +31 −0 transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketStringEchoTest.java
  27. +46 −0 transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollTestUtils.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2014 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.util.internal;
+
+import io.netty.util.internal.logging.InternalLogger;
+import io.netty.util.internal.logging.InternalLoggerFactory;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.util.Locale;
+import java.util.regex.Pattern;
+
+/**
+ * Helper class to load JNI resources.
+ *
+ */
+public final class NativeLibraryLoader {
+
+ private static final InternalLogger logger = InternalLoggerFactory.getInstance(NativeLibraryLoader.class);
+
+ private static final Pattern REPLACE = Pattern.compile("\\W+");
+ private static final File WORKDIR;
+
+ static {
+ String workdir = SystemPropertyUtil.get("io.netty.native.workdir");
+ if (workdir != null) {
+ File f = new File(workdir);
+ if (!f.exists()) {
+ // ok to ignore as createTempFile will take care
+ //noinspection ResultOfMethodCallIgnored
+ f.mkdirs();
+ }
+
+ try {
+ f = f.getAbsoluteFile();
+ } catch (Exception ignored) {
+ // Good to have an absolute path, but it's OK.
+ }
+
+ WORKDIR = f;
+ logger.debug("-Dio.netty.netty.workdir: {}", WORKDIR);
+ } else {
+ WORKDIR = PlatformDependent.tmpdir();
+ logger.debug("-Dio.netty.netty.workdir: {} (io.netty.tmpdir)", WORKDIR);
+ }
+ }
+
+ /**
+ * Load the given library with the specified {@link java.lang.ClassLoader}
+ */
+ public static void load(String name, ClassLoader loader) {
+ String libname = System.mapLibraryName(name);
+ String path = "META-INF/native/" + osIdentifier() + PlatformDependent.bitMode() + '/' + libname;
+
+ URL url = loader.getResource(path);
+ if (url == null) {
+ // Fall back to normal loading of JNI stuff
+ System.loadLibrary(name);
+ } else {
+ int index = libname.lastIndexOf('.');
+ String prefix = libname.substring(0, index);
+ String suffix = libname.substring(index, libname.length());
+ InputStream in = null;
+ OutputStream out = null;
+ File tmpFile = null;
+ boolean loaded = false;
+ try {
+ tmpFile = File.createTempFile(prefix, suffix, WORKDIR);
+ in = url.openStream();
+ out = new FileOutputStream(tmpFile);
+
+ byte[] buffer = new byte[8192];
+ int length;
+ while ((length = in.read(buffer)) > 0) {
+ out.write(buffer, 0, length);
+ }
+ out.flush();
+ out.close();
+ out = null;
+
+ System.load(tmpFile.getPath());
+ loaded = true;
+ } catch (Exception e) {
+ throw (UnsatisfiedLinkError) new UnsatisfiedLinkError(
+ "could not load a native library: " + name).initCause(e);
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException ignore) {
+ // ignore
+ }
+ }
+ if (out != null) {
+ try {
+ out.close();
+ } catch (IOException ignore) {
+ // ignore
+ }
+ }
+ if (tmpFile != null) {
+ if (loaded) {
+ tmpFile.deleteOnExit();
+ } else {
+ if (!tmpFile.delete()) {
+ tmpFile.deleteOnExit();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private static String osIdentifier() {
+ String name = SystemPropertyUtil.get("os.name", "unknown").toLowerCase(Locale.US).trim();
+ if (name.startsWith("win")) {
+ return "windows";
+ }
+ if (name.startsWith("mac os x")) {
+ return "osx";
+ }
+ if (name.startsWith("linux")) {
+ return "linux";
+ }
+
+ return REPLACE.matcher(name).replaceAll("_");
+ }
+
+ private NativeLibraryLoader() {
+ // Utility
+ }
+}
@@ -21,6 +21,7 @@
import io.netty.util.internal.logging.InternalLoggerFactory;
import java.io.BufferedReader;
+import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
@@ -74,6 +75,10 @@
private static final boolean HAS_JAVASSIST = hasJavassist0();
+ private static final File TMPDIR = tmpdir0();
+
+ private static final int BIT_MODE = bitMode0();
+
static {
if (logger.isDebugEnabled()) {
logger.debug("-Dio.netty.noPreferDirect: {}", !DIRECT_BUFFER_PREFERRED);
@@ -154,6 +159,20 @@ public static boolean hasJavassist() {
}
/**
+ * Returns the temporary directory.
+ */
+ public static File tmpdir() {
+ return TMPDIR;
+ }
+
+ /**
+ * Returns the bit mode of the current VM (usually 32 or 64.)
+ */
+ public static int bitMode() {
+ return BIT_MODE;
+ }
+
+ /**
* Raises an exception bypassing compiler checks for checked exceptions.
*/
public static void throwException(Throwable t) {
@@ -638,6 +657,130 @@ private static boolean hasJavassist0() {
}
}
+ private static File tmpdir0() {
+ File f;
+ try {
+ f = toDirectory(SystemPropertyUtil.get("io.netty.tmpdir"));
+ if (f != null) {
+ logger.debug("-Dio.netty.tmpdir: {}", f);
+ return f;
+ }
+
+ f = toDirectory(SystemPropertyUtil.get("java.io.tmpdir"));
+ if (f != null) {
+ logger.debug("-Dio.netty.tmpdir: {} (java.io.tmpdir)", f);
+ return f;
+ }
+
+ // This shouldn't happen, but just in case ..
+ if (isWindows()) {
+ f = toDirectory(System.getenv("TEMP"));
+ if (f != null) {
+ logger.debug("-Dio.netty.tmpdir: {} (%TEMP%)", f);
+ return f;
+ }
+
+ String userprofile = System.getenv("USERPROFILE");
+ if (userprofile != null) {
+ f = toDirectory(userprofile + "\\AppData\\Local\\Temp");
+ if (f != null) {
+ logger.debug("-Dio.netty.tmpdir: {} (%USERPROFILE%\\AppData\\Local\\Temp)", f);
+ return f;
+ }
+
+ f = toDirectory(userprofile + "\\Local Settings\\Temp");
+ if (f != null) {
+ logger.debug("-Dio.netty.tmpdir: {} (%USERPROFILE%\\Local Settings\\Temp)", f);
+ return f;
+ }
+ }
+ } else {
+ f = toDirectory(System.getenv("TMPDIR"));
+ if (f != null) {
+ logger.debug("-Dio.netty.tmpdir: {} ($TMPDIR)", f);
+ return f;
+ }
+ }
+ } catch (Exception ignored) {
+ // Environment variable inaccessible
+ }
+
+ // Last resort.
+ if (isWindows()) {
+ f = new File("C:\\Windows\\Temp");
+ } else {
+ f = new File("/tmp");
+ }
+
+ logger.warn("Failed to get the temporary directory; falling back to: {}", f);
+ return f;
+ }
+
+ @SuppressWarnings("ResultOfMethodCallIgnored")
+ private static File toDirectory(String path) {
+ if (path == null) {
+ return null;
+ }
+
+ File f = new File(path);
+ if (!f.exists()) {
+ f.mkdirs();
+ }
+
+ if (!f.isDirectory()) {
+ return null;
+ }
+
+ try {
+ return f.getAbsoluteFile();
+ } catch (Exception ignored) {
+ return f;
+ }
+ }
+
+ private static int bitMode0() {
+ // Check user-specified bit mode first.
+ int bitMode = SystemPropertyUtil.getInt("io.netty.bitMode", 0);
+ if (bitMode > 0) {
+ logger.debug("-Dio.netty.bitMode: {}", bitMode);
+ return bitMode;
+ }
+
+ // And then the vendor specific ones which is probably most reliable.
+ bitMode = SystemPropertyUtil.getInt("sun.arch.data.model", 0);
+ if (bitMode > 0) {
+ logger.debug("-Dio.netty.bitMode: {} (sun.arch.data.model)", bitMode);
+ return bitMode;
+ }
+ bitMode = SystemPropertyUtil.getInt("com.ibm.vm.bitmode", 0);
+ if (bitMode > 0) {
+ logger.debug("-Dio.netty.bitMode: {} (com.ibm.vm.bitmode)", bitMode);
+ return bitMode;
+ }
+
+ // os.arch also gives us a good hint.
+ String arch = SystemPropertyUtil.get("os.arch", "").toLowerCase(Locale.US).trim();
+ if ("amd64".equals(arch) || "x86_64".equals(arch)) {
+ bitMode = 64;
+ } else if ("i386".equals(arch) || "i486".equals(arch) || "i586".equals(arch) || "i686".equals(arch)) {
+ bitMode = 32;
+ }
+
+ if (bitMode > 0) {
+ logger.debug("-Dio.netty.bitMode: {} (os.arch: {})", bitMode, arch);
+ }
+
+ // Last resort: guess from VM name and then fall back to most common 64-bit mode.
+ String vm = SystemPropertyUtil.get("java.vm.name", "").toLowerCase(Locale.US);
+ Pattern BIT_PATTERN = Pattern.compile("([1-9][0-9]+)-?bit");
+ Matcher m = BIT_PATTERN.matcher(vm);
+ if (m.find()) {
+ return Integer.parseInt(m.group(1));
+ } else {
+ return 64;
+ }
+ }
+
private PlatformDependent() {
// only static method supported
}
View
17 pom.xml
@@ -120,6 +120,17 @@
<maven.javadoc.failOnError>false</maven.javadoc.failOnError>
</properties>
</profile>
+ <profile>
+ <id>linux-native</id>
+ <activation>
+ <os>
+ <family>linux</family>
+ </os>
+ </activation>
+ <modules>
+ <module>transport-native-epoll</module>
+ </modules>
+ </profile>
</profiles>
<properties>
@@ -499,6 +510,10 @@
<goal>manifest</goal>
</goals>
<configuration>
+ <supportedProjectTypes>
+ <supportedProjectType>jar</supportedProjectType>
+ <supportedProjectType>bundle</supportedProjectType>
+ </supportedProjectTypes>
<instructions>
<Export-Package>${project.groupId}.*</Export-Package>
<!-- enforce JVM vendor package as optional -->
@@ -565,7 +580,7 @@
<version>2.4.2</version>
<configuration>
<useReleaseProfile>false</useReleaseProfile>
- <arguments>-P release,sonatype-oss-release,full,no-osgi</arguments>
+ <arguments>-P release,sonatype-oss-release,full,no-osgi,linux-native</arguments>
<autoVersionSubmodules>true</autoVersionSubmodules>
<allowTimestampedSnapshots>false</allowTimestampedSnapshots>
<tagNameFormat>netty-@{project.version}</tagNameFormat>
@@ -0,0 +1,3 @@
+# Native transport for Linux
+
+See [our wiki page](http://netty.io/wiki/native-transports.html).
Oops, something went wrong.

0 comments on commit e0299e1

Please sign in to comment.