Skip to content

Commit

Permalink
Introduce a native transport for linux using epoll ET
Browse files Browse the repository at this point in the history
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
Norman Maurer committed Feb 15, 2014
1 parent c73e1e3 commit e0299e1
Show file tree
Hide file tree
Showing 27 changed files with 3,643 additions and 1 deletion.
149 changes: 149 additions & 0 deletions common/src/main/java/io/netty/util/internal/NativeLibraryLoader.java
Original file line number Diff line number Diff line change
@@ -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
}
}
143 changes: 143 additions & 0 deletions common/src/main/java/io/netty/util/internal/PlatformDependent.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -74,6 +75,10 @@ public final class PlatformDependent {

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);
Expand Down Expand Up @@ -153,6 +158,20 @@ public static boolean hasJavassist() {
return HAS_JAVASSIST;
}

/**
* 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.
*/
Expand Down Expand Up @@ -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
}
Expand Down
17 changes: 16 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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 -->
Expand Down Expand Up @@ -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>
Expand Down
3 changes: 3 additions & 0 deletions transport-native-epoll/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Native transport for Linux

See [our wiki page](http://netty.io/wiki/native-transports.html).
Loading

0 comments on commit e0299e1

Please sign in to comment.