io.netty.channel.epoll.Native Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* 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.ChannelException;
import io.netty.channel.DefaultFileRegion;
import io.netty.channel.unix.DomainSocketAddress;
import io.netty.util.internal.EmptyArrays;
import io.netty.util.internal.NativeLibraryLoader;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.SystemPropertyUtil;
import java.io.IOException;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.util.Locale;
/**
* Native helper methods
*
* Internal usage only!
*/
final class Native {
static {
String name = SystemPropertyUtil.get("os.name").toLowerCase(Locale.UK).trim();
if (!name.startsWith("linux")) {
throw new IllegalStateException("Only supported on Linux");
}
NativeLibraryLoader.load("netty-transport-native-epoll", PlatformDependent.getClassLoader(Native.class));
}
// EventLoop operations and constants
public static final int EPOLLIN = epollin();
public static final int EPOLLOUT = epollout();
public static final int EPOLLRDHUP = epollrdhup();
public static final int EPOLLET = epollet();
public static final int IOV_MAX = iovMax();
public static final int UIO_MAX_IOV = uioMaxIov();
public static final boolean IS_SUPPORTING_SENDMMSG = isSupportingSendmmsg();
private static final byte[] IPV4_MAPPED_IPV6_PREFIX = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0xff, (byte) 0xff };
// As all our JNI methods return -errno on error we need to compare with the negative errno codes.
private static final int ERRNO_EBADF_NEGATIVE = -errnoEBADF();
private static final int ERRNO_EPIPE_NEGATIVE = -errnoEPIPE();
private static final int ERRNO_ECONNRESET_NEGATIVE = -errnoECONNRESET();
private static final int ERRNO_EAGAIN_NEGATIVE = -errnoEAGAIN();
private static final int ERRNO_EWOULDBLOCK_NEGATIVE = -errnoEWOULDBLOCK();
private static final int ERRNO_EINPROGRESS_NEGATIVE = -errnoEINPROGRESS();
/**
* Holds the mappings for errno codes to String messages.
* This eliminates the need to call back into JNI to get the right String message on an exception
* and thus is faster.
*
* The array length of 1024 should be more then enough because errno.h only holds < 200 codes.
*/
private static final String[] ERRORS = new String[1024]; //
// Pre-instantiated exceptions which does not need any stacktrace and
// can be thrown multiple times for performance reasons.
private static final ClosedChannelException CLOSED_CHANNEL_EXCEPTION;
private static final IOException CONNECTION_RESET_EXCEPTION_WRITE;
private static final IOException CONNECTION_RESET_EXCEPTION_WRITEV;
private static final IOException CONNECTION_RESET_EXCEPTION_READ;
private static final IOException CONNECTION_RESET_EXCEPTION_SENDFILE;
private static final IOException CONNECTION_RESET_EXCEPTION_SENDTO;
private static final IOException CONNECTION_RESET_EXCEPTION_SENDMSG;
private static final IOException CONNECTION_RESET_EXCEPTION_SENDMMSG;
static {
for (int i = 0; i < ERRORS.length; i++) {
// This is ok as strerror returns 'Unknown error i' when the message is not known.
ERRORS[i] = strError(i);
}
CONNECTION_RESET_EXCEPTION_READ = newConnectionResetException("syscall:read(...)",
ERRNO_ECONNRESET_NEGATIVE);
CONNECTION_RESET_EXCEPTION_WRITE = newConnectionResetException("syscall:write(...)",
ERRNO_EPIPE_NEGATIVE);
CONNECTION_RESET_EXCEPTION_WRITEV = newConnectionResetException("syscall:writev(...)",
ERRNO_EPIPE_NEGATIVE);
CONNECTION_RESET_EXCEPTION_SENDFILE = newConnectionResetException("syscall:sendfile(...)",
ERRNO_EPIPE_NEGATIVE);
CONNECTION_RESET_EXCEPTION_SENDTO = newConnectionResetException("syscall:sendto(...)",
ERRNO_EPIPE_NEGATIVE);
CONNECTION_RESET_EXCEPTION_SENDMSG = newConnectionResetException("syscall:sendmsg(...)",
ERRNO_EPIPE_NEGATIVE);
CONNECTION_RESET_EXCEPTION_SENDMMSG = newConnectionResetException("syscall:sendmmsg(...)",
ERRNO_EPIPE_NEGATIVE);
CLOSED_CHANNEL_EXCEPTION = new ClosedChannelException();
CLOSED_CHANNEL_EXCEPTION.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
}
private static IOException newConnectionResetException(String method, int errnoNegative) {
IOException exception = newIOException(method, errnoNegative);
exception.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
return exception;
}
private static IOException newIOException(String method, int err) {
return new IOException(method + "() failed: " + ERRORS[-err]);
}
private static int ioResult(String method, int err, IOException resetCause) throws IOException {
// network stack saturated... try again later
if (err == ERRNO_EAGAIN_NEGATIVE || err == ERRNO_EWOULDBLOCK_NEGATIVE) {
return 0;
}
if (err == ERRNO_EPIPE_NEGATIVE || err == ERRNO_ECONNRESET_NEGATIVE) {
throw resetCause;
}
if (err == ERRNO_EBADF_NEGATIVE) {
throw CLOSED_CHANNEL_EXCEPTION;
}
// TODO: We could even go futher and use a pre-instanced IOException for the other error codes, but for
// all other errors it may be better to just include a stacktrace.
throw newIOException(method, err);
}
public static native int eventFd();
public static native void eventFdWrite(int fd, long value);
public static native void eventFdRead(int fd);
public static native int epollCreate();
public static int epollWait(int efd, EpollEventArray events, int timeout) throws IOException {
int ready = epollWait0(efd, events.memoryAddress(), events.length(), timeout);
if (ready < 0) {
throw newIOException("epoll_wait", ready);
}
return ready;
}
private static native int epollWait0(int efd, long address, int len, int timeout);
public static native void epollCtlAdd(int efd, final int fd, final int flags);
public static native void epollCtlMod(int efd, final int fd, final int flags);
public static native void epollCtlDel(int efd, final int fd);
private static native int errnoEBADF();
private static native int errnoEPIPE();
private static native int errnoECONNRESET();
private static native int errnoEAGAIN();
private static native int errnoEWOULDBLOCK();
private static native int errnoEINPROGRESS();
private static native String strError(int err);
// File-descriptor operations
public static void close(int fd) throws IOException {
int res = close0(fd);
if (res < 0) {
throw newIOException("close", res);
}
}
private static native int close0(int fd);
public static int write(int fd, ByteBuffer buf, int pos, int limit) throws IOException {
int res = write0(fd, buf, pos, limit);
if (res >= 0) {
return res;
}
return ioResult("write", res, CONNECTION_RESET_EXCEPTION_WRITE);
}
private static native int write0(int fd, ByteBuffer buf, int pos, int limit);
public static int writeAddress(int fd, long address, int pos, int limit) throws IOException {
int res = writeAddress0(fd, address, pos, limit);
if (res >= 0) {
return res;
}
return ioResult("writeAddress", res, CONNECTION_RESET_EXCEPTION_WRITE);
}
private static native int writeAddress0(int fd, long address, int pos, int limit);
public static long writev(int fd, ByteBuffer[] buffers, int offset, int length) throws IOException {
long res = writev0(fd, buffers, offset, length);
if (res >= 0) {
return res;
}
return ioResult("writev", (int) res, CONNECTION_RESET_EXCEPTION_WRITEV);
}
private static native long writev0(int fd, ByteBuffer[] buffers, int offset, int length);
public static long writevAddresses(int fd, long memoryAddress, int length)
throws IOException {
long res = writevAddresses0(fd, memoryAddress, length);
if (res >= 0) {
return res;
}
return ioResult("writevAddresses", (int) res, CONNECTION_RESET_EXCEPTION_WRITEV);
}
private static native long writevAddresses0(int fd, long memoryAddress, int length);
public static int read(int fd, ByteBuffer buf, int pos, int limit) throws IOException {
int res = read0(fd, buf, pos, limit);
if (res > 0) {
return res;
}
if (res == 0) {
return -1;
}
return ioResult("read", res, CONNECTION_RESET_EXCEPTION_READ);
}
private static native int read0(int fd, ByteBuffer buf, int pos, int limit);
public static int readAddress(int fd, long address, int pos, int limit) throws IOException {
int res = readAddress0(fd, address, pos, limit);
if (res > 0) {
return res;
}
if (res == 0) {
return -1;
}
return ioResult("readAddress", res, CONNECTION_RESET_EXCEPTION_READ);
}
private static native int readAddress0(int fd, long address, int pos, int limit);
public static long sendfile(
int dest, 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 = sendfile0(dest, src, baseOffset, offset, length);
if (res >= 0) {
return res;
}
return ioResult("sendfile", (int) res, CONNECTION_RESET_EXCEPTION_SENDFILE);
}
private static native long sendfile0(
int dest, DefaultFileRegion src, long baseOffset, long offset, long length) throws IOException;
public static int sendTo(
int fd, ByteBuffer buf, int pos, int limit, InetAddress addr, int port) throws IOException {
// just duplicate the toNativeInetAddress code here to minimize object creation as this method is expected
// to be called frequently
byte[] address;
int scopeId;
if (addr instanceof Inet6Address) {
address = addr.getAddress();
scopeId = ((Inet6Address) addr).getScopeId();
} else {
// convert to ipv4 mapped ipv6 address;
scopeId = 0;
address = ipv4MappedIpv6Address(addr.getAddress());
}
int res = sendTo0(fd, buf, pos, limit, address, scopeId, port);
if (res >= 0) {
return res;
}
return ioResult("sendTo", res, CONNECTION_RESET_EXCEPTION_SENDTO);
}
private static native int sendTo0(
int fd, ByteBuffer buf, int pos, int limit, byte[] address, int scopeId, int port);
public static int sendToAddress(
int fd, long memoryAddress, int pos, int limit, InetAddress addr, int port) throws IOException {
// just duplicate the toNativeInetAddress code here to minimize object creation as this method is expected
// to be called frequently
byte[] address;
int scopeId;
if (addr instanceof Inet6Address) {
address = addr.getAddress();
scopeId = ((Inet6Address) addr).getScopeId();
} else {
// convert to ipv4 mapped ipv6 address;
scopeId = 0;
address = ipv4MappedIpv6Address(addr.getAddress());
}
int res = sendToAddress0(fd, memoryAddress, pos, limit, address, scopeId, port);
if (res >= 0) {
return res;
}
return ioResult("sendToAddress", res, CONNECTION_RESET_EXCEPTION_SENDTO);
}
private static native int sendToAddress0(
int fd, long memoryAddress, int pos, int limit, byte[] address, int scopeId, int port);
public static int sendToAddresses(
int fd, long memoryAddress, int length, InetAddress addr, int port) throws IOException {
// just duplicate the toNativeInetAddress code here to minimize object creation as this method is expected
// to be called frequently
byte[] address;
int scopeId;
if (addr instanceof Inet6Address) {
address = addr.getAddress();
scopeId = ((Inet6Address) addr).getScopeId();
} else {
// convert to ipv4 mapped ipv6 address;
scopeId = 0;
address = ipv4MappedIpv6Address(addr.getAddress());
}
int res = sendToAddresses(fd, memoryAddress, length, address, scopeId, port);
if (res >= 0) {
return res;
}
return ioResult("sendToAddresses", res, CONNECTION_RESET_EXCEPTION_SENDMSG);
}
private static native int sendToAddresses(
int fd, long memoryAddress, int length, byte[] address, int scopeId, int port);
public static native EpollDatagramChannel.DatagramSocketAddress recvFrom(
int fd, ByteBuffer buf, int pos, int limit) throws IOException;
public static native EpollDatagramChannel.DatagramSocketAddress recvFromAddress(
int fd, long memoryAddress, int pos, int limit) throws IOException;
public static int sendmmsg(
int fd, NativeDatagramPacketArray.NativeDatagramPacket[] msgs, int offset, int len) throws IOException {
int res = sendmmsg0(fd, msgs, offset, len);
if (res >= 0) {
return res;
}
return ioResult("sendmmsg", res, CONNECTION_RESET_EXCEPTION_SENDMMSG);
}
private static native int sendmmsg0(
int fd, NativeDatagramPacketArray.NativeDatagramPacket[] msgs, int offset, int len);
private static native boolean isSupportingSendmmsg();
// socket operations
public static int socketStreamFd() {
int res = socketStream();
if (res < 0) {
throw new ChannelException(newIOException("socketStreamFd", res));
}
return res;
}
public static int socketDgramFd() {
int res = socketDgram();
if (res < 0) {
throw new ChannelException(newIOException("socketDgramFd", res));
}
return res;
}
public static int socketDomainFd() {
int res = socketDomain();
if (res < 0) {
throw new ChannelException(newIOException("socketDomain", res));
}
return res;
}
private static native int socketStream();
private static native int socketDgram();
private static native int socketDomain();
public static void bind(int fd, SocketAddress socketAddress) throws IOException {
if (socketAddress instanceof InetSocketAddress) {
InetSocketAddress addr = (InetSocketAddress) socketAddress;
NativeInetAddress address = toNativeInetAddress(addr.getAddress());
int res = bind(fd, address.address, address.scopeId, addr.getPort());
if (res < 0) {
throw newIOException("bind", res);
}
} else if (socketAddress instanceof DomainSocketAddress) {
DomainSocketAddress addr = (DomainSocketAddress) socketAddress;
int res = bindDomainSocket(fd, addr.path());
if (res < 0) {
throw newIOException("bind", res);
}
} else {
throw new Error("Unexpected SocketAddress implementation " + socketAddress);
}
}
private static native int bind(int fd, byte[] address, int scopeId, int port);
private static native int bindDomainSocket(int fd, String path);
public static void listen(int fd, int backlog) throws IOException {
int res = listen0(fd, backlog);
if (res < 0) {
throw newIOException("listen", res);
}
}
private static native int listen0(int fd, int backlog);
public static boolean connect(int fd, SocketAddress socketAddress) throws IOException {
int res;
if (socketAddress instanceof InetSocketAddress) {
InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress;
NativeInetAddress address = toNativeInetAddress(inetSocketAddress.getAddress());
res = connect(fd, address.address, address.scopeId, inetSocketAddress.getPort());
} else if (socketAddress instanceof DomainSocketAddress) {
DomainSocketAddress unixDomainSocketAddress = (DomainSocketAddress) socketAddress;
res = connectDomainSocket(fd, unixDomainSocketAddress.path());
} else {
throw new Error("Unexpected SocketAddress implementation " + socketAddress);
}
if (res < 0) {
if (res == ERRNO_EINPROGRESS_NEGATIVE) {
// connect not complete yet need to wait for EPOLLOUT event
return false;
}
throw newIOException("connect", res);
}
return true;
}
private static native int connect(int fd, byte[] address, int scopeId, int port);
private static native int connectDomainSocket(int fd, String path);
public static boolean finishConnect(int fd) throws IOException {
int res = finishConnect0(fd);
if (res < 0) {
if (res == ERRNO_EINPROGRESS_NEGATIVE) {
// connect still in progress
return false;
}
throw newIOException("finishConnect", res);
}
return true;
}
private static native int finishConnect0(int fd);
public static InetSocketAddress remoteAddress(int fd) {
byte[] addr = remoteAddress0(fd);
// addr may be null if getpeername failed.
// See https://github.com/netty/netty/issues/3328
if (addr == null) {
return null;
}
return address(addr, 0, addr.length);
}
public static InetSocketAddress localAddress(int fd) {
byte[] addr = localAddress0(fd);
// addr may be null if getpeername failed.
// See https://github.com/netty/netty/issues/3328
if (addr == null) {
return null;
}
return address(addr, 0, addr.length);
}
static InetSocketAddress address(byte[] addr, int offset, int len) {
// The last 4 bytes are always the port
final int port = decodeInt(addr, offset + len - 4);
final InetAddress address;
try {
switch (len) {
// 8 bytes:
// - 4 == ipaddress
// - 4 == port
case 8:
byte[] ipv4 = new byte[4];
System.arraycopy(addr, offset, ipv4, 0, 4);
address = InetAddress.getByAddress(ipv4);
break;
// 24 bytes:
// - 16 == ipaddress
// - 4 == scopeId
// - 4 == port
case 24:
byte[] ipv6 = new byte[16];
System.arraycopy(addr, offset, ipv6, 0, 16);
int scopeId = decodeInt(addr, offset + len - 8);
address = Inet6Address.getByAddress(null, ipv6, scopeId);
break;
default:
throw new Error();
}
return new InetSocketAddress(address, port);
} catch (UnknownHostException e) {
throw new Error("Should never happen", e);
}
}
static int decodeInt(byte[] addr, int index) {
return (addr[index] & 0xff) << 24 |
(addr[index + 1] & 0xff) << 16 |
(addr[index + 2] & 0xff) << 8 |
addr[index + 3] & 0xff;
}
private static native byte[] remoteAddress0(int fd);
private static native byte[] localAddress0(int fd);
public static int accept(int fd, byte[] addr) throws IOException {
int res = accept0(fd, addr);
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("accept", res);
}
private static native int accept0(int fd, byte[] addr);
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);
public static void shutdown(int fd, boolean read, boolean write) throws IOException {
int res = shutdown0(fd, read, write);
if (res < 0) {
throw newIOException("shutdown", res);
}
}
private static native int shutdown0(int fd, boolean read, boolean write);
// Socket option operations
public static native int getReceiveBufferSize(int fd);
public static native int getSendBufferSize(int fd);
public static native int isKeepAlive(int fd);
public static native int isReuseAddress(int fd);
public static native int isReusePort(int fd);
public static native int isTcpNoDelay(int fd);
public static native int isTcpCork(int fd);
public static native int getSoLinger(int fd);
public static native int getTrafficClass(int fd);
public static native int isBroadcast(int fd);
public static native int getTcpKeepIdle(int fd);
public static native int getTcpKeepIntvl(int fd);
public static native int getTcpKeepCnt(int fd);
public static native int getSoError(int fd);
public static native void setKeepAlive(int fd, int keepAlive);
public static native void setReceiveBufferSize(int fd, int receiveBufferSize);
public static native void setReuseAddress(int fd, int reuseAddress);
public static native void setReusePort(int fd, int reuseAddress);
public static native void setSendBufferSize(int fd, int sendBufferSize);
public static native void setTcpNoDelay(int fd, int tcpNoDelay);
public static native void setTcpCork(int fd, int tcpCork);
public static native void setSoLinger(int fd, int soLinger);
public static native void setTrafficClass(int fd, int tcpNoDelay);
public static native void setBroadcast(int fd, int broadcast);
public static native void setTcpKeepIdle(int fd, int seconds);
public static native void setTcpKeepIntvl(int fd, int seconds);
public static native void setTcpKeepCnt(int fd, int probes);
public static void tcpInfo(int fd, EpollTcpInfo info) {
tcpInfo0(fd, info.info);
}
private static native void tcpInfo0(int fd, int[] array);
private static NativeInetAddress toNativeInetAddress(InetAddress addr) {
byte[] bytes = addr.getAddress();
if (addr instanceof Inet6Address) {
return new NativeInetAddress(bytes, ((Inet6Address) addr).getScopeId());
} else {
// convert to ipv4 mapped ipv6 address;
return new NativeInetAddress(ipv4MappedIpv6Address(bytes));
}
}
static byte[] ipv4MappedIpv6Address(byte[] ipv4) {
byte[] address = new byte[16];
System.arraycopy(IPV4_MAPPED_IPV6_PREFIX, 0, address, 0, IPV4_MAPPED_IPV6_PREFIX.length);
System.arraycopy(ipv4, 0, address, 12, ipv4.length);
return address;
}
private static class NativeInetAddress {
final byte[] address;
final int scopeId;
NativeInetAddress(byte[] address, int scopeId) {
this.address = address;
this.scopeId = scopeId;
}
NativeInetAddress(byte[] address) {
this(address, 0);
}
}
public static native String kernelVersion();
private static native int iovMax();
private static native int uioMaxIov();
// epoll_event related
public static native int sizeofEpollEvent();
public static native int offsetofEpollData();
private static native int epollin();
private static native int epollout();
private static native int epollrdhup();
private static native int epollet();
private Native() {
// utility
}
}